Search code examples
csocketswinapiipv6getaddrinfo

getaddrinfo() with IPv6 not making sense


I don't understand why getaddrinfo is not returning a valid IPv6 address.

On my system the code below is printing 22:B8:00:00:00:00:00:00:00:00:00:00:00:00, but I expected a 01 somewhere, since localhost should resolve to ::1.

At the same time, sa_data is only 14 bytes, whereas IPv6 addresses are 16 bytes, so it seems that the last couple of bytes are always chopped off, and the function can't return an IPv6 address?

Can someone explain what's going on? How am I supposed to use this function with 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;
}

Solution

  • sockaddr struct definitions for reference:

    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;
    };
    

    When ai_family == AF_INET6 ai_addr actually points to a struct sockaddr_in6. The first few bytes you are printing are sin6_port and sin6_flowinfo. The IPv6 address comes after.

    Edit to add:

    You can use ai_addr directly with functions like bind() and getnameinfo(). You typically won't need to dig down into the struct definition details. For example, I would use getnameinfo() with NI_NUMERICHOST to get a printable address.