【问题标题】:getaddrinfo() with IPv6 not making sense使用 IPv6 的 getaddrinfo() 没有意义
【发布时间】:2018-02-04 14:46:57
【问题描述】:

我不明白为什么 getaddrinfo 没有返回有效的 IPv6 地址。

在我的系统上,下面的代码正在打印22:B8:00:00:00:00:00:00:00:00:00:00:00:00,但我希望在某个地方出现01,因为localhost 应该解析为::1

同时sa_data只有14个字节,而IPv6地址是16个字节,所以看起来最后几个字节总是被砍掉,函数不能返回 IPv6 地址?

有人可以解释发生了什么吗?我应该如何在 IPv6 中使用此功能?

#include <stdio.h>
#include <WinSock2.h>
#include <WS2TCPIP.h>
#pragma comment(lib, "WS2_32")

int main(int argc, char *argv[])
{
    WSADATA wsadata;
    WSAStartup(0x0002, &wsadata);
    addrinfo addr_hints = { 0, PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, *addrs_out;
    getaddrinfo("localhost", "8888", &addr_hints, &addrs_out);
    fprintf(stderr,
        "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 0]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 1]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 2]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 3]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 4]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 5]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 6]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 7]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 8]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 9]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[10]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[11]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[12]),
        static_cast<unsigned char>(addrs_out->ai_addr->sa_data[13]));
    freeaddrinfo(addrs_out);
    return 0;
}

【问题讨论】:

  • 首先一定要检查getaddrinfo()的返回值。如果失败,您的地址将是垃圾。
  • @Jeff:我在我的实际代码中;这不是这里的问题。

标签: c sockets winapi ipv6 getaddrinfo


【解决方案1】:

sockaddr 不被严格解释为指向 sockaddr 结构。结构的解释不同 不同地址族的上下文。

所以我们需要首先检查 sockaddr 中的 sa_familyai_familyaddrinfo (在此基础中它必须相等)并基于此需要“重新解释”来自 sockaddr 的指针(这就像 @987654329 @指针)指向真实结构。说为此使用联合:

addrinfo *addrs_out, *addr;

if (getaddrinfo("localhost", "8888", 0, &addrs_out) == NOERROR)
{
    addr = addrs_out;

    CHAR buf[256], *sz, srv[128];
    ULONG n;
    PUCHAR Byte;

    do 
    {
        union {
            sockaddr* ai_addr;
            SOCKADDR_IN* pa;
            SOCKADDR_IN6* pa6;
        };

        ai_addr = addr->ai_addr;

        if (addr->ai_family != ai_addr->sa_family)
        {
            __debugbreak();
        }

        switch (addr->ai_family)
        {
        case AF_INET6:
            Byte = pa6->sin6_addr.u.Byte, n = RTL_NUMBER_OF(pa6->sin6_addr.u.Byte), sz = buf;
            do 
            {
                sz += sprintf(sz, "%02X:", *Byte++);
            } while (--n);

            sz[-1] = 0;
            DbgPrint("AF_INET6: %s\n", buf);
            break;

        case AF_INET:
            if (0 <= RtlIpv4AddressToStringExA(&pa->sin_addr.S_un.S_addr, pa->sin_port, buf, &(n = RTL_NUMBER_OF(buf))))
            {
                DbgPrint("AF_INET: %s\n", buf);
            }
            break;
        }

        // alt print
        if (getnameinfo(ai_addr, (socklen_t)addr->ai_addrlen, buf, RTL_NUMBER_OF(buf), srv, RTL_NUMBER_OF(srv), NI_NUMERICHOST ) == NOERROR)
        {
            DbgPrint("%s:%s\n", buf, srv);
        }

    } while (addr = addr->ai_next);

    freeaddrinfo(addrs_out);
}

【讨论】:

    【解决方案2】:

    sockaddr 结构定义供参考:

    struct sockaddr {
        ushort  sa_family;
        char    sa_data[14];
    };
    
    
    struct sockaddr_in6 {
        short   sin6_family;
        u_short sin6_port;
        u_long  sin6_flowinfo;
        struct  in6_addr sin6_addr;
        u_long  sin6_scope_id;
    };
    

    ai_family == AF_INET6 ai_addr 实际上指向struct sockaddr_in6。您要打印的前几个字节是sin6_portsin6_flowinfo。 IPv6 地址在后面。

    编辑添加:

    您可以直接将ai_addrbind()getnameinfo() 等函数一起使用。您通常不需要深入研究结构定义的详细信息。例如,我会使用getnameinfo()NI_NUMERICHOST 来获取可打印的地址。

    【讨论】:

    • 天啊,我以为sockaddr 足够容纳sockaddr_in6,但似乎这就是SOCKADDR_STORAGE 的用途!终于有道理了,谢谢!
    • 附带问题,但您是否知道是否有一个地方列出了每个标准地址族的长度?
    • @Mehrdad 我不知道。其中有 40 多个,它们自己的 sockaddr 定义分散在各种头文件中。
    猜你喜欢
    • 2012-02-03
    • 2011-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多