Пользователь
0,0
рейтинг
24 октября 2009 в 04:10

Альтернативный способ записи IP-адресов (версия 2)

Прочитав в сентябре заметку Альтернативный способ записи IP-адресов, подумал интересно, какие же умные бывают программы, и забыл.

Недавно пришлось устанавливать mpi для локальной разработки. Попробовал и MS MPI и MPICH, оба завершались с неочевидной ошибкой сети 10051, как гласит MSDN, это означает, что сеть не доступна. Долго пытался ее побороть, думал уже и на ОС и на сами программные пакеты, пересоздавал сетевые интерфейсы. Но вышло все немного по другому…

Потом случайно вспомнил о заметке про IP адреса и меня осенило, имя компьютеру я дал необычное: 0x0A0D. Опасения подтвердились, это имя сразу преобразовывалось к IP адресу и программы mpi не могли подсоеденится к диспетчеру.

OC Windows


И так, неужели все программы такие умные? Для начала посмотрим, как системная утилита ping ОС MS Windows разрешает входящий параметр в IP адрес. Делается это с помощью POSIX-совместимой функции getaddrinfo библиотеки WinSock. Происходит это примерно так:
  struct addrinfo hints;
  ZeroMemory( &hints, sizeof(hints) );
  hints.ai_flags = AI_NUMERICHOST;

  dwRetval = getaddrinfo(argv[1], NULL, &hints, &result);
  if ( dwRetval != 0 ) {
    hints.ai_flags = AI_CANONNAME;
    dwRetval = getaddrinfo(argv[1], NULL, &hints, &result);
    if ( dwRetval != 0 ) {
      printf("getaddrinfo failed with error: %d\n", dwRetval);
      WSACleanup();
      return 1;
    }
  }


* This source code was highlighted with Source Code Highlighter.

Вот ее описание:
int WSAAPI getaddrinfo(
 __in  const char *nodename,
 __in  const char *servname,
 __in  const struct addrinfo *hints,
 __out struct addrinfo **res
);

* This source code was highlighted with Source Code Highlighter.

Самым интересным параметром является hints, который служит подсказкой для интерпретации входной строки nodename. Поле ai_flags может принимать несколько значений от которых зависит логика преобразования входящей строки в адрес. В утилите ping, как и во многих других программах, сначала осуществляется попытка прямого преобразования строки в IP адрес, а только потом это повторяется через подсистему DNS. Это вполне логично и рационально.

И так, что происходит, когда мы принуждаем осуществить прямое преобразование? В недрах кода winsock проходит множество преобразований и проверок, но в конце концов поток исполнения доходит до функции RtlIpv4StringToAddressW библиотеки Ntdll.dll. Кстати, эта функция абсолютно независима от сетевой подсистемы и может быть использована без явной инициализации WinSock. Она как раз и осуществляет интерпретацию всевозможных вариантов записи IP адреса. В MSDN говорится о всех форматах, но почему именно они?

ОС Linux


С этой операционной сиситемой все значительно проще, так как открыты исходники ее и ее многих утилит. Найдя первый исходник утилиты ping для Linux, можно увидить, как в ней происходит преобразование строки в IP адрес:
  bzero((char *)&whereto, sizeof(struct sockaddr) );
  to->sin_family = AF_INET;
  to->sin_addr.s_addr = inet_addr(av[0]);
  if(to->sin_addr.s_addr != (unsigned)-1) {
    strcpy(hnamebuf, av[0]);
    hostname = hnamebuf;
  } else {
    hp = gethostbyname(av[0]);
    if (hp) {
      to->sin_family = hp->h_addrtype;
      bcopy(hp->h_addr, (caddr_t)&to->sin_addr, hp->h_length);
      hostname = hp->h_name;
    } else {
      printf("%s: unknown host %sn", argv[0], av[0]);
      exit(1);
    }
  }


* This source code was highlighted with Source Code Highlighter.

Как видно, код отличается, но определив назначения функций, можно понять, что алгоритмы идентичные. Почитав man inet_addr, становится ясным, что эта функция аналогична RtlIpv4StringToAddressW ОС Windows. Также в ее описании упоминается загадочный стандарт POSIX.1-2001.

Стандарт POSIX.1-2001


И так стандарт гласит, функция должна принимать строку в одном из следующих форматов:
  • a.b.c.d — Когда указаны 4 части, каждая часть должна быть проинтепретирована как байт данных и присвоен слева направо четырем байтам IP адреса
  • a.b.c — Когда указаны 3 части, третья часть интерпретируется как 16-битная величина и присваивается правым двум байтам IP адреса. Таким образом можно кратко записывать адреса в сетях класса B
  • a.b — Когда указаны 2 части, вторая часть интерпретируется как 24-битное значение и используется для краткой записи адресов класса A
  • a — Когда указана одна часть, она должна быть непосредственно преобразована в IP адрес без перестановок

Все части точечной нотации IP адреса должны быть числами в десятичной, восьмеричной или шестнадцетиричной системах счисления согласно стандарту ISO C.

Выводы


  1. Нужно выбирать сетевое имя компьютера, которое нельзя напрямую преобразовать в адрес, иначе компьютер может быть недоступен по имени
  2. Преобразование IP4 адресов никак не связано с записью адресов IPv6 или форматом URL, но адреса IP4 являются подмножеством адресов IPv6
  3. Это работает только в POSIX совместимых системах и только в программах, которые сначала пытаются напрямую преобразовать адрес (на некоторых Linux адрес типа a не воспринимается)
  4. Для локальных соединения лучше использовать имя localhost, а не имя компьютера :)


Ссылки


Альтернативный способ записи IP-адресов
The Ping Page
inet_addr IEEE Std 1003.1
Владимир @bolsh
карма
16,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое

Комментарии (4)

  • 0
    Или выбрать такое имя, чтобы совпадало с адресом.
    • 0
      ага, тоже вариант :)
  • +1
    Хех %) Вы, на самом деле пошли правильной дорогой, но прошли порядка половины пути. Дело в том, что только в некоторых утилитах есть такой финт ушами, как в ping. Центральная функция тут скорее gethostbyname() и она сама по себе вполне может делать такие преобразования, а самое главное — на нее уже влияют общесистемные настройки ресолвера — т.е. для glibc это /etc/nsswitch.conf — т.к. сам по себе ресолвер и представляет из себя набор модулей, который предоставляют разные реализации интерфейса gethostbyname.

    Вот пример того, как она работает на относительно современной Linux-системе (Debian EGLIBC 2.13-4):

    ============ 10.2.3.4
    Address: 0a 02 03 04 
    
    ============ 42
    Address: 00 00 00 2a 
    
    ============ localhost
    Address: 7f 00 00 01 
    
    ============ 0x17
    unknown host 0x17
    
    ============ 0077
    Address: 00 00 00 3f 
    
    • +1
      Спасибо за дополнение!

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.