Search code examples
c++cwinsock2

inet_ntop returns address that doesnt exist


#include <WinSock2.h>
#include <WS2tcpip.h>
#include "stdio.h"

int main(int argc, wchar_t *argv[]) {

    //init Windows Sockets
    //IResult
    int IResult = NULL;
    WSADATA wsaDataStruct;
    IResult = WSAStartup(MAKEWORD(2, 2), &wsaDataStruct);
    if (IResult != 0) // Error
    {
        //printf u are fucked;
        printf("Error: Winsock2 Init, %d", IResult);
        return 1;
    }

    //Result is output of getaddrinfo, hints is a helper struct
    struct addrinfo* 
        result = NULL, //Linked list of addresses
        * ptr = NULL, 
        hints = {};

    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    //Hostname
    char hostname[64] = {};
    gethostname(hostname, 64);

    IResult = getaddrinfo(hostname, "4500", &hints, &result);
    if (IResult != 0) { //Smth bad happened get Error
        printf("Error: Winsock2 addrinfo, %d", IResult);
        WSACleanup();
        return 2;
    }

    //Loop addresses
    addrinfo* addr = result;
    while (addr!=nullptr)
    {
        char ip[16];
        inet_ntop(addr->ai_family, addr->ai_addr, ip, 16);
        printf("Address found: %s", ip);
        addr = addr->ai_next;
    }

    return 0;
}

I am looping through the linked list of addrinfo structs and so far it returns one struct only which has an address of 2.0.17.148, while my machine has a local address of 192.168.2.1

I have only 1 network interface, and addrinfo returns a valid struct.


Solution

  • inet_ntop's argument should be a pointer to an in_addr (IPv4) or an in6_addr (IPv6).

    You are passing it to a sockaddr, which will be a sockaddr_in for IPv4 or a sockaddr_in6 for IPv6. (You won't get any IPv6 addresses because you only asked for IPv4 addresses using AF_INET). The sockaddr contains the family code, the port, and the address.

    To get the in_addr you need to access it within the sockaddr_in structure: First cast addr->ai_addr to sockaddr_in*, then access the sin_addr member, then get a pointer to it.

    inet_ntop(addr->ai_family, &((sockaddr_in*)addr->ai_addr)->sin_addr, ip, 16);
    

    Note: this only works for IPv4 (AF_INET family). To support IPv6 as well, you need to check whether the family is AF_INET or AF_INET6, and use sockaddr_in6 for AF_INET6, and allocate more space for ip because the addresses are longer.