UNIX: разработка сетевых приложений
Хотя четверть из представленных в таблице сочетаний недопустима, в обозримом будущем большинство реализаций IPv6 будут использоваться на узлах с двойным стеком протоколов и поддерживать не только IPv6. Если мы удалим из таблицы вторую строку и вторую колонку, все записи «Нет» исчезнут и единственной проблемой останется запись, помеченная звездочкой.
12.4. Макроопределения проверки адреса IPv6
Существует небольшой класс приложений IPv6, которые должны знать, с каким собеседником они взаимодействуют (IPv4 или IPv6). Эти приложения должны знать, является ли адрес собеседника адресом IPv4, преобразованным к виду IPv6. Определены двенадцать макросов, проверяющих некоторые свойства адреса Ipv6.
#include <netinet/in.h>int IN6_IS_ADDR_UNSPECIFIED(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_LOOPBACK(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_MULTICAST(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_LINKLOCAL(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_SITELOCAL(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_V4MAPPED(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_V4COMPAT(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_MC_NODELOCAL(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_MC_LINKLOCAL(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_MC_SITELOCAL(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_MC_ORGLOCAL(const struct in6_addr *<i>aptr</i>);int IN6_IS_ADDR_MC_GLOBAL(const struct in6_addr *<i>aptr</i>);<i>Все возвращают: ненулевое значение, если адрес IPv6 имеет указанный тип, 0 в противном случае</i>Первые семь макросов проверяют базовый тип адреса IPv6. Мы покажем различные типы адресов в разделе А.5. Последние пять макросов проверяют область действия адреса многоадресной передачи IPv6 (см. раздел 19.2).
Клиент IPv6 может вызвать макрос
для проверки адреса IPv6, возвращенного распознавателем. Сервер IPv6 может вызвать этот макрос для проверки адреса IPv6, возвращенного функцией accept илиIN6_IS_ADDR_V4MAPPED.recvfromКак пример приложения, которому нужен этот макрос, можно привести FTP и его команду
. Если мы запустим FTP-клиент, зарегистрируемся на FTP-сервере и выполним команду FTPPORT, FTP-клиент пошлет командуdirFTP-серверу через управляющее соединение. Она сообщит серверу IP-адрес и порт клиента, с которым затем сервер создаст соединение. (В главе 27 [111] содержатся подробные сведения о протоколе приложения FTP.) Но FTP-клиент IPv6 должен знать, с каким сервером имеет дело — IPv4 или IPv6, поскольку сервер IPv4 требует команду в форматеPORT(где первые четыре числа, каждое от 0 до 255, формируют 4-байтовый адрес IPv4, а два последних — 2-байтовый номер порта), а серверу IPv6 необходима командаPORT a1, a2, a3, a4, p1, p2(RFC 2428 [3]), содержащая семейство адреса, адрес в текстовом формате и порт в текстовом формате. В упражнении 12.1 приводятся примеры использования обеих команд.EPRT12.5. Переносимость исходного кода
Большинство существующих сетевых приложений написаны для IPv4. Структуры
размещаются в памяти и заполняются, а функцияsockaddr_inзадаетsocketв качестве первого аргумента. При переходе от листинга 1.1 к листингу 1.2 мы видели, что эти приложения IPv4 можно преобразовать в приложения IPv6 без особых усилий. Многие показанные нами изменения можно выполнить автоматически, используя некоторые сценарии редактирования. Программы, более зависящие от IPv4, использующие такие свойства, как многоадресная передача, параметры IP или символьные (неструктурированные) сокеты, потребуют больших усилий при преобразовании.AF_INETЕсли мы преобразуем приложение для работы с IPv6 и распространим его исходный код, нам придется думать о том, поддерживает ли принимающая система протокол IPv6. Типичный способ решения этой проблемы — применять в коде
, используя по возможности IPv6 (поскольку мы видели в этой главе, что клиент IPv6 может взаимодействовать с серверами IPv4 и наоборот). Проблема такого подхода в том, что код очень быстро засоряется директивами#ifdef, и его становится сложнее отслеживать и обслуживать.#ifdefНаилучшим подходом будет рассмотрение перехода на IPv6 как возможности сделать программу не зависящей от протокола. Первым шагом здесь будет удаление вызовов функций
иgethostbynameи использование функцийgethostbyaddrиgetaddrinfo, описанных в предыдущей главе. Это позволит нам обращаться со структурами адресов сокетов как с непрозрачными объектами, ссылаться на которые можно с помощью указателя и размера, что как раз и выполняют основные функции сокетов:getnameinfo,bind,connectи т.д. Наши функцииrecvfromиз раздела 3.8 помогут работать с ними независимо от IPv4 и IPv6. Очевидно, эти функции содержатsock_XXXдля работы с IPv4 и IPv6, но если мы скроем эту зависимость от протокола в нескольких библиотечных функциях, наш код станет проще. В разделе 21.7 мы разработаем ряд функций#ifdef, которые помогут сделать приложения многоадресной передачи не зависящими от версии протокола IP.mcast_XXXДругой момент, который нужно учесть, — что произойдет, если мы откомпилируем наш исходный код в системе, поддерживающей и IPv4, и IPv6, затем распространим либо исполняемый код, либо объектные файлы (но не исходный код) и кто-то запустит наше приложение в системе, не поддерживающей IPv6. Есть вероятность, что сервер локальных имен поддерживает записи типа AAAA и возвращает как записи типа AAAA, так и записи типа А некоему собеседнику, с которым пытается соединиться наше приложение. Если наше приложение, работающее с IPv6, вызовет функцию
для создания сокета IPv6, она не будет работать, если узел не поддерживает IPv6. Мы решаем этот вопрос с помощью функций, описанных в следующей главе, игнорируя ошибку функцииsocketи пытаясь использовать следующий адрес в списке, возвращаемом сервером имен. Если предположить, что у собеседника имеется запись типа А и что сервер имен возвращает запись типа А в дополнение к любой записи типа AAAA, то сокет IPv4 успешно создастся. Этот тип функциональности имеется в библиотечной функции, но не в исходном коде каждого приложения.socket