Альтернативный способ записи 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
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 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
          Спасибо за дополнение!

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