使用AF_UNSPEC时,getaddrinfo()仅返回ipv6

问题描述 投票:1回答:1

当我想连接到服务器(在本地运行)并且不知道应用程序是否使用ipv4,ipv6或两者时,我应该两次调用getaddrinfo(),一次使用AF_INET,一次使用AF_INET6,并尝试所有返回的地址?

一些上下文:getaddrinfo()为ipv4 / ipv6-agnostic主机名解析提供了一种方法。在线我发现指导说明连接到服务器的常用算法的指南是使用带有AF_UNSPEC提示的getaddrinfo(),并尝试列表中返回的地址。

但是,在我的设置中,当我使用getaddrinfo()时,AF_UNSPEC只返回一个ipv6条目,主机是"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文件只包含localhost的ipv6条目,因此只返回AF_UNSPEC。对于AF_INET,该条目不被视为合格,localhost正确解析为127.0.0.1

c networking ipv6 ipv4 getaddrinfo
1个回答
2
投票

这确实是一个有趣的问题:

这是一个案例,由glibc的nss-files模块特别处理;如果发出了对具有地址族AF_INET的localhost的请求并且解析了/ etc / hosts的v6 localhost条目,则会将其隐式转换为地址为127.0.0.1的v4 localhost条目。

请参阅nss/nss_files/files-hosts.c(第70行),执行此转换:

else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
  {
    in_addr_t localhost = htonl (INADDR_LOOPBACK);
    memcpy (entdata->host_addr, &localhost, sizeof (localhost));
  }

在请求AF_UNSPEC时,不会使用此分支,因此只有在/etc/hosts中显式输入时才会解析v4 localhost地址。

© www.soinside.com 2019 - 2024. All rights reserved.