Кросс пост с Хабрахабра для тестирования возможностей Pelican.
Прочитав в сентябре заметку Альтернативный способ записи IP-адресов, подумал интересно, какие же умные бывают программы, и забыл.
Недавно пришлось устанавливать mpi для локальной разработки. Попробовал и MS MPI и MPICH, оба завершались с неочевидной ошибкой сети 10051, как гласит MSDN, это означает, что сеть не доступна. Долго пытался ее побороть, думал уже и на ОС и на сами программные пакеты, пересоздавал сетевые интерфейсы. Но вышло все немного по другому…
Потом случайно вспомнил о заметке про IP адреса и меня осенило, имя компьютеру я дал необычное: 0x0A0D. Опасения подтвердились, это имя сразу преобразовывалось к IP адресу и программы mpi не могли подсоеденится к диспетчеру.
И так, неужели все программы такие умные? Для начала посмотрим, как системная утилита 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 говорится о всех форматах, но почему именно они?
С этой операционной сиситемой все значительно проще, так как открыты исходники ее и ее многих утилит. Найдя первый исходник утилиты 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.
И так стандарт гласит, функция должна принимать строку в одном из следующих форматов: - a.b.c.d — Когда указаны 4 части, каждая часть должна быть проинтепретирована как байт данных и присвоен слева направо четырем байтам IP адреса - a.b.c — Когда указаны 3 части, третья часть интерпретируется как 16-битная величина и присваивается правым двум байтам IP адреса. Таким образом можно кратко записывать адреса в сетях класса B - a.b — Когда указаны 2 части, вторая часть интерпретируется как 24-битное значение и используется для краткой записи адресов класса A - a — Когда указана одна часть, она должна быть непосредственно преобразована в IP адрес без перестановок
PHP web developer, python developer