7

getaddrinfo 调用有许多有趣的标志。我想知道 AI_V4MAPPED 标志的目的是什么。在任何系统上,我似乎都无法让 getaddrinfo 生成 ::ffff:nnnn 表单地址,正如我在设置此标志时所期望的那样。我期待错了吗?我看到错误了吗?

特别是,如果我询问 AF_INET6 系列地址并指定 AI_V4MAPPED,我希望看到仅具有 DNS A(IPv4 地址)记录的主机的 ::ffff:nnnn 地址。而且我通常还期望,如果我指定 AI_ALL,我将同时获得主机的 DNS AAAA(IPv6 地址)记录和 ::ffff:nnnn 格式的 DNS A 记录。

再说一次,我在这里期待所有错误的事情吗?

我已经在 Fedora 11 - glibc 2.10.1 和 OS X 10.4 上对此进行了测试。

4

2 回答 2

7

在 Debian Lenny (glibc 2.7) 上,我得到了你所期望的结果——除了一个例外——如果我指定AI_V4MAPPED不带AI_ALL,并且我查找的主机名有指向 A 记录的 CNAME,我不会得到那些返回。AI_ALL如果还指定了,或者主机名直接与 A 记录相关联,则它工作正常。

我不知道为什么 - 也许这是一个 glibc 错误?

这是我的测试程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    struct addrinfo hints = { 0 };
    struct addrinfo *res, *res_c;
    int err;
    char name[INET6_ADDRSTRLEN];

    if (argc < 2)
    {
        return 1;
    }

    hints.ai_family = AF_INET6;
    hints.ai_flags = AI_V4MAPPED | AI_ALL;

    err = getaddrinfo(argv[1], NULL, &hints, &res);

    if (err)
    {
        printf("getaddrinfo: %s\n", gai_strerror(err));
        return 1;
    }

    for (res_c = res; res_c; res_c = res_c->ai_next)
    {
        const void *addr;
        int port;
        struct protoent *proto;

        switch (res_c->ai_family)
        {
            case AF_INET6:
                addr = &((struct sockaddr_in6 *)(res_c->ai_addr))->sin6_addr;
                port = ((struct sockaddr_in6 *)(res_c->ai_addr))->sin6_port;
                printf("AF_INET6\t");
                break;
            case AF_INET:
                addr = &((struct sockaddr_in *)(res_c->ai_addr))->sin_addr;
                port = ((struct sockaddr_in *)(res_c->ai_addr))->sin_port;
                printf("AF_INET\t");
                break;
            default:
                addr = NULL;
                printf("(%d)\t", res_c->ai_family);
        }

        proto = getprotobynumber(res_c->ai_protocol);
        if (proto)
        {
            printf("%s\t", proto->p_name);
        }
        else
        {
            printf("(%d)\t", res_c->ai_protocol);
        }

        switch (res_c->ai_socktype)
        {
            case SOCK_STREAM:
                printf("SOCK_STREAM\t");
                break;

            case SOCK_DGRAM:
                printf("SOCK_DGRAM\t");
                break;

            default:
                printf("(?socktype?)\t");
                break;
        }

        if (addr && inet_ntop(res_c->ai_family, addr, name, sizeof name))
            printf("addr = %s", name);

        if (addr)
            printf(",%d", port);

        printf("\n");
    }

    return 0;
}
于 2009-09-12T13:56:04.810 回答
4

以我的经验,AI_V4MAPPED不适用于 Mac OS X 10.6。如果您提供hints.ai_family = AF_INET6hints.ai_flags = AI_V4MAPPED它将始终返回EAI_NONAME,并gai_strerror()打印“提供的节点名或服务名,或未知”。

它在 OS X 10.7 上正常工作。

即使您使用 Fedora,也可以将其发布在这里以防万一。

于 2011-09-26T22:33:21.523 回答