Страница 129 из 399
11.8. Функция freeaddrinfo
Вся память, занимаемая структурами addrinfo, структурами ai_addr и строкой ai_cano
#include <netdb.h>
void freeaddrinfo(struct addrinfo *<i>ai</i>);
Переменная ai должна указывать на первую из структур addrinfo, возвращаемых функцией getaddrinfo. Освобождается вся область памяти, занятая структурами из связного списка, вместе с динамически выделенной областью памяти, содержащей данные, на которые указывают эти структуры (например, структуры адресов сокетов и канонические имена узлов).
Предположим, что мы вызываем функцию getaddrinfo, проходим последовательно по всему связному списку структур addrinfo и находим нужную структуру. Если далее мы попытаемся сохранить нужную нам информацию простым копированием структуры addrinfo, а затем вызовем функцию freeaddrinfo, мы получим скрытую ошибку. Причина в том, что структура addrinfo сама указывает на динамически выделенный участок памяти (для структуры адреса сокета и, возможно, для канонического имени). Но эта область памяти, на которую указывает сохраненная нами структура, при вызове функции freeaddrinfo освобождается и может использоваться для хранения какой-либо иной информации.
Создание копии только самой структуры addrinfo, а не структур, на которые она, в свою очередь, указывает, называется поверхностным копированием (shallow сору). Копирование структуры addrinfo и всех структур, на которые она указывает, называется детальным копированием (deep сору).
11.9. Функция getaddrinfo: IPv6
Стандарт POSIX определяет как getaddrinfo, так и возвращаемые этой функцией данные для протоколов IPv4 и IPv6. Отметим следующие моменты, прежде чем свести возвращаемые значения воедино в табл. 11.3.
■ Входные данные функции getaddrinfo могут относиться к двум различным типам, которые выбираются в зависимости от того, какой тип структуры адреса сокета вызывающий процесс хочет получить обратно и какой тип записей нужно искать в DNS или иной базе данных.
■ Семейством адресов, указанным вызывающим процессом в структуре hints, задается тип структуры адреса сокета, который вызывающий процесс предполагает получить. Если вызывающий процесс задает AF_INET, функция не должна возвращать структуры sockaddr_in6, а для AF_INET6 функция не должна возвращать структур sockaddr_in.
■ POSIX утверждает, что при задании семейства AF_UNSPEC должны возвращаться адреса, которые могут использоваться с любым семейством протоколов, допускающим применение имени узла и имени службы. Это подразумевает, что если у узла имеются как записи типа AAAA, так и записи типа А, то записи типа AAAA возвращаются как структуры sockaddr_in6, а записи типа A — как структуры sockaddr_in. Нет смысла возвращать еще и записи типа А как адреса IPv4, преобразованные к виду IPv6, в структурах sockaddr_in6, потому что при этом не возвращается никакой дополнительной информации — эти адреса уже возвращены в структурах sockaddr_in.
■ Это утверждение POSIX также подразумевает, что если флаг AI_PASSIVE задан без имени узла, то должен быть возвращен универсальный адрес IPv6 (IN6ADDR_ANY_INIT или 0::0) в структуре sockaddr_in6 вместе с универсальным адресом IPv4 (INADDR_ANY или 0.0.0.0) в структуре sockaddr_in. Также нет смысла возвращать сначала универсальный адрес IPv4, поскольку мы увидим в разделе 12.2, что на узле с двойным стеком сокет сервера IPv6 может обрабатывать и клиенты IPv4, и клиенты IPv6.
■ Семейство адресов, указанное в поле ai_family структуры hint вместе с флагами AI_V4MAPPED и AI_ALL поля ai_flags, задают тип записей, поиск которых ведется в DNS (тип А или тип AAAA), и тип возвращаемых адресов (IPv4, IPv6 или IPv4, преобразованные к виду IPv6). Мы обобщили это в табл. 11.3.
■ Имя узла может также быть либо шестнадцатеричной строкой IPv6, либо строкой в точечно-десятичной записи IPv4. Допустимость этой строки зависит от семейства адресов, заданного вызывающим процессом. Шестнадцатеричная строка IPv6 неприемлема, если задано семейство AF_INET, а строка в точечно-десятичной записи IPv4 неприемлема, если задано семейство AF_INET6. Но если задано семейство AF_UNSPEC, то допустимы оба варианта, и при этом возвращается соответствующий тип структуры адреса сокета.
Можно возразить, что если в качестве семейства протоколов задано AF_INET6, строка в точечно-десятичной записи должна возвращаться как адрес IPv4, преобразованный к виду IPv6 в структуре sockaddr_in6. Но другим способом получения этого результата является установка префикса строки с десятичной точкой 0::ffff:.
В табл. 11.3 показано, как будут обрабатываться адреса IPv4 и IPv6 функцией getaddrinfo. Колонка «Результат» отражает то, что мы хотим возвратить вызывающему процессу, если входные переменные таковы, как показано в первых трех колонках. Колонка «Действия» — то, каким образом мы получаем этот результат.
Таблица 11.3. Функция getaddrinfo: ее действия и результаты