Search code examples
windowswinapiwinsockwinsock2

How to get IPv4 address from SOCKADDR structure?


I can't figure out how to get the actual IP v4 address (e. g. 192.168.1.1 or its corresponding packed form as a 32-bit integer) from the Address field of IP_ADAPTER_UNICAST_ADDRESS_LH structure. I went down the rabbit hole of the linked lists and corresponding MSDN links, but SOCKADDR is where it cuts off. SOCKADDR is the generic data holder, it has to be cast to the IPv4 variant in order to obtain a structured view of the data.

Trouble is, I can't find which structure is the IPv4 mapping for SOCKADDR. Tried in_addr, getting garbage values.

auto pAddresses = static_cast<IP_ADAPTER_ADDRESSES*>(malloc(defaultBufferSize));
    ULONG outBufLen = defaultBufferSize;
    constexpr ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
    auto result = GetAdaptersAddresses(AF_INET /* Only IP v4 since no cameras use v6 yet */, flags, nullptr, pAddresses, &outBufLen);

    uint32_t ipV4Address = 0;

    // Iterating over the returned adapter info structures
    for (auto currentInterfaceInfo = pAddresses; currentInterfaceInfo != nullptr; currentInterfaceInfo = currentInterfaceInfo->Next)
    {
        if (currentInterfaceInfo->IfType != IF_TYPE_IEEE80211 || currentInterfaceInfo->Ipv4Enabled != 1 || currentInterfaceInfo->ReceiveOnly != 0)
            continue;

        if (currentInterfaceInfo->FirstUnicastAddress)
        {
            for (auto addressInfo = currentInterfaceInfo->FirstUnicastAddress; addressInfo != nullptr; addressInfo = currentInterfaceInfo->FirstUnicastAddress->Next)
            {
                if (!addressInfo->Address.lpSockaddr || addressInfo->Address.lpSockaddr->sa_family != AF_INET /* IP v4 */)
                    continue;

                auto ipv4AddrInfo = reinterpret_cast<in_addr*>(addressInfo->Address.lpSockaddr);
                                                     ^^^^????
            }
        }
    }

Solution

  • Given a SOCKADDR*, look at its sa_family field to determine the address type.

    • for an AF_INET address, typecast the SOCKADDR* to SOCKADDR_IN*, and then you can access its sin_addr field.

    • for an AF_INET6 address, typecast the SOCKADDR* to SOCKADDR_IN6* and then you can access its sin6_addr field.

    std::vector<BYTE> buffer(defaultBufferSize);
    auto pAddresses = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(buffer.data());
    ULONG outBufLen = defaultBufferSize;
    
    constexpr ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
    auto result = GetAdaptersAddresses(AF_INET /* Only IP v4 since no cameras use v6 yet */, flags, nullptr, pAddresses, &outBufLen);
    
    uint32_t ipV4Address = 0;
    
    // Iterating over the returned adapter info structures
    for (auto currentInterfaceInfo = pAddresses; currentInterfaceInfo != nullptr; currentInterfaceInfo = currentInterfaceInfo->Next)
    {
        if (currentInterfaceInfo->IfType != IF_TYPE_IEEE80211 || currentInterfaceInfo->Ipv4Enabled != 1 || currentInterfaceInfo->ReceiveOnly != 0)
            continue;
    
        for (auto addressInfo = currentInterfaceInfo->FirstUnicastAddress; addressInfo != nullptr; addressInfo = addressInfo->Next)
        {
            if (!addressInfo->Address.lpSockaddr || addressInfo->Address.lpSockaddr->sa_family != AF_INET /* IP v4 */)
                continue;
    
            auto ipv4AddrInfo = reinterpret_cast<sockaddr_in*>(addressInfo->Address.lpSockaddr);
            // use ipv4AddrInfo->sin_addr as needed...
        }
    }