getaddrinfo
В программировании на языке C функции доменные getaddrinfo() и getnameinfo() преобразуют имена , имена хостов и IP-адреса в удобочитаемые текстовые представления и структурированные двоичные форматы для сетевого API операционной системы . Обе функции содержатся в POSIX . стандартном интерфейсе прикладного программирования (API) [1]
getaddrinfo и getnameinfo являются обратными функциями друг друга. Они не зависят от сетевых протоколов и поддерживают как IPv4 , так и IPv6 . Это рекомендуемый интерфейс для разрешения имен при создании независимых от протокола приложений и для перехода устаревшего кода IPv4 в Интернет IPv6.
Внутри функции могут использовать различные методы разрешения, не ограничиваясь системой доменных имен (DNS). Переключатель службы имен обычно используется в Unix-подобных системах и влияет на большую часть реализации этой пары, как и в случае с их предшественниками эпохи BSD-сокетов . [2]
структура адреса
[ редактировать ]Структура данных C , используемая для представления адресов и имен хостов в сетевом API, следующая:
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr* ai_addr;
char* ai_canonname; /* canonical name */
struct addrinfo* ai_next; /* this struct can form a linked list */
};
В некоторых старых системах типом ai_addrlen является size_t вместо socklen_t . Большинство функций сокетов, таких как Accept() и getpeername() , требуют, чтобы параметр имел тип socklen_t * , и программисты часто передают адрес элементу структуры ai_addrlen addrinfo . Если типы несовместимы, например, в 64-битной системе Solaris 9 , где size_t равен 8 байтам, а socklen_t равен 4 байтам, могут возникнуть ошибки во время выполнения.
Структура содержит структуры ai_family и sockaddr со своим собственным полем sa_family . В некоторых реализациях им присваиваются одни и те же значения, когда структура создается с помощью функции getaddrinfo .
getaddrinfo()
[ редактировать ]getaddrinfo() преобразует удобочитаемые текстовые строки, представляющие имена хостов или IP-адреса, в динамически выделяемый связанный список структур struct addrinfo. Прототип функции для этой функции указан следующим образом:
int getaddrinfo(const char* hostname,
const char* service,
const struct addrinfo* hints,
struct addrinfo** res);
- имя хоста
- может быть либо именем домена, например «example.com», строкой адреса, например «127.0.0.1», либо NULL, и в этом случае адрес 0.0.0.0 или 127.0.0.1 назначается в зависимости от флагов подсказок.
- услуга
- может быть номером порта, переданным в виде строки, например «80», или именем службы, например «echo». В последнем случае типичная реализация использует getservbyname() для запроса файла /etc/services для сопоставления службы с номером порта.
- намеки
- может иметь значение NULL или структуру addrinfo с типом запрошенной службы.
- рез
- — это указатель, указывающий на новую структуру addrinfo с информацией, запрошенной после успешного завершения функции. [3] Функция возвращает 0 в случае успеха и ненулевое значение ошибки в случае неудачи. [1]
Хотя реализации различаются на разных платформах, функция сначала пытается получить номер порта, обычно путем перехода на сервис . Если строковое значение является числом, оно преобразуется в целое число и вызывает htons() . Если это имя службы, например www , служба ищется с помощью getservbyname() , используя протокол, полученный изhints->ai_socktype, в качестве второго параметра этой функции. Затем, если имя хоста задано (не NULL), его разрешает вызов gethostbyname() адрес 0.0.0.0 , в противном случае используется , если дляhints->ai_flags установлено значение AI_PASSIVE , и 127.0.0.1 в противном случае. В одном из этих условий он выделил новую структуру addrinfo, заполненную соответствующим sockaddr_in , а также добавил к ней порт, полученный в начале. Наконец, параметр **res разыменовывается, чтобы он указывал на вновь выделенную структуру addrinfo . [4] В некоторых реализациях, таких как версия Unix для Mac OS, подсказки->ai_protocol переопределяют значение подсказок->ai_socktype, в то время как в других оно противоположно, поэтому оба должны быть определены с эквивалентными значениями, чтобы код мог работать с несколькими платформы.
freeaddrinfo()
[ редактировать ]Эта функция освобождает память, выделенную функцией getaddrinfo() . Поскольку результатом последнего является связанный список структур addrinfo, начинающийся с адреса ai , функция freeaddrinfo() проходит по списку и освобождает каждую из них по очереди.
void freeaddrinfo(struct addrinfo *ai);
получитьимяинформация()
[ редактировать ]Функция getnameinfo() преобразует внутреннее двоичное представление IP-адреса в форме указателя на структуру sockaddr в текстовые строки, состоящие из имени хоста или, если адрес не может быть преобразован в имя, в текстовое представление IP-адреса, как а также имя или номер порта службы. Прототип функции указан следующим образом:
int getnameinfo(const struct sockaddr* sa, socklen_t salen,
char* host, size_t hostlen,
char* serv, size_t servlen,
int flags);
Пример
[ редактировать ]В следующем примере метод getaddrinfo() используется для разрешения доменного имени www.example.com в его списке адресов, а затем вызывается метод getnameinfo() для каждого результата, чтобы вернуть каноническое имя адреса. Как правило, это создает исходное имя хоста , если конкретный адрес не имеет несколько имен, и в этом случае каноническое возвращается имя. В этом примере имя домена печатается три раза, по одному для каждого из трех полученных результатов.
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#ifndef NI_MAXHOST
#define NI_MAXHOST 1025
#endif
int main(void)
{
struct addrinfo* result;
struct addrinfo* res;
int error;
/* resolve the domain name into a list of addresses */
error = getaddrinfo("www.example.com", NULL, NULL, &result);
if (error != 0) {
if (error == EAI_SYSTEM) {
perror("getaddrinfo");
} else {
fprintf(stderr, "error in getaddrinfo: %s\n", gai_strerror(error));
}
exit(EXIT_FAILURE);
}
/* loop over all returned results and do inverse lookup */
for (res = result; res != NULL; res = res->ai_next) {
char hostname[NI_MAXHOST];
error = getnameinfo(res->ai_addr, res->ai_addrlen, hostname, NI_MAXHOST, NULL, 0, 0);
if (error != 0) {
fprintf(stderr, "error in getnameinfo: %s\n", gai_strerror(error));
continue;
}
if (*hostname != '\0')
printf("hostname: %s\n", hostname);
}
freeaddrinfo(result);
return 0;
}
Безопасность
[ редактировать ]16 февраля 2016 г. было объявлено об ошибке безопасности в реализации glibc функции getaddrinfo(), использующей технику переполнения буфера , которая может позволить злоумышленнику выполнить произвольный код. [5]
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Перейти обратно: а б «freeaddrinfo, getaddrinfo — получить информацию об адресе» . Базовые спецификации открытой группы, выпуск 7, издание 2018 г. (POSIX.1-2017) . Открытая группа . Проверено 5 марта 2022 г.
- ^ «nss — раздел 5 страниц руководства: Форматы файлов» . docs.oracle.com .
- ^ Стивенс Р., Феннер, Рудофф [2003] Сетевое программирование UNIX®, том 1, третье издание: API сетевых сокетов. Издатель: Addison-Wesley Professional. Паб. Дата: 14 ноября 2003 г. с. 256
- ^ Хаджиму УМЕМОТО [2000] getaddrinfo.c Доступ по адресу: https://opensource.apple.com/source/passwordserver_sasl/passwordserver_sasl-14/cyrus_sasl/lib/getaddrinfo.c
- ^ «CVE-2015-7547: переполнение буфера стека Glibc getaddrinfo» .
Внешние ссылки
[ редактировать ]- Спецификации freeaddrinfo и getaddrinfo в POSIX.1-2017 . Базовые спецификации открытой группы, выпуск 7, издание 2018 г.
- RFC 3493, Расширения базового интерфейса сокетов для IPv6