Когда я хочу подключиться к серверу (работающему локально) и не знаю, использует ли приложение ipv4, ipv6 или оба, следует ли мне вызывать getaddrinfo()
дважды, один раз с AF_INET
и один раз с AF_INET6
,и попробовать все возвращенные адреса?
Некоторый контекст: getaddrinfo()
предоставляет способ разрешения имен хостов, не зависящих от ipv4 / ipv6.В сети я нашел рекомендации, согласно которым общий алгоритм подключения к серверу должен использовать getaddrinfo()
с подсказкой AF_UNSPEC и пробовать адреса, возвращенные в списке.
Однако в моей настройке getaddrinfo()
только возвращает запись ipv6, когда я использую AF_UNSPEC
, хостом является "localhost"
.С другой стороны, если я запрашиваю IPv4 явно, getaddrinfo()
возвращает адрес IPv4.
В этом примере getaddrinfo()
вызывается один раз с AF_UNSPEC
и один раз с AF_INET
, и перебирает возвращенный результатперечислять и распечатывать семейства адресов записей:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
const char* family_to_string(int family) {
switch (family) {
case AF_INET:
return "AF_INET";
case AF_INET6:
return "AF_INET6";
case AF_UNSPEC:
return "AF_UNSPEC";
default:
return "UNKNOWN";
}
}
int main(void) {
struct addrinfo hints;
struct addrinfo *res, *it;
static const char* host = "localhost";
static const char* port = "42420";
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_UNSPEC;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
int ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_INET;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
return 0;
}
Меня немного удивило, когда я получил этот вывод:
getaddrinfo(.. AF_UNSPEC ..):
entry for AF_INET6
getaddrinfo(.. AF_INET ..):
entry for AF_INET
После того, как немного покопался в поведении getaddrinfo()
, похоже, что он сначала проверяет записи в /etc/hosts
, и если он находит совпадающие записи, он только возвращает их и не пытается разрешить имя хоста по-другому.
Поскольку мой файл /etc/hosts
толькосодержит запись ipv6 для localhost
, только возвращаемую для AF_UNSPEC
.Для AF_INET
запись не считается приемлемой, а localhost
правильно разрешено до 127.0.0.1
.