Сб, 24 ноя 2012 by Bolshevik in General | Comments

Кросс пост с Хабрахабра для тестирования возможностей Pelican.

Прочитав в сентябре заметку Альтернативный способ записи 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;
   }
}

Вот ее описание:

int WSAAPI getaddrinfo(
    __in  const char *nodename,
    __in  const char *servname,
    __in  const struct addrinfo *hints,
    __out struct addrinfo **res
);

Самым интересным параметром является 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);
  }
}

Как видно, код отличается, но определив назначения функций, можно понять, что алгоритмы идентичные. Почитав 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 адрес без перестановок

Выводы

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

Ссылки

Comments

comments powered by Disqus

About

PHP web developer, python developer