Search code examples
csocketsgetaddrinfogethostbyname

Connecting a socket, using getaddrinfo, times out


I spent hours trying to find out my issue, just found the solution as I was writing my question (it always help when you need to formalize your issue and explain it). I post it, hopefully it helps someone.

Using getaddrinfo, if I try to connect a socket to my server, doing (what I thought was) exactly what is being explained on tons of website aswell as in the man page sample code of getaddrinfo, it FAILS with a "connection timed out" error message: (Simplifying the code to be more concise)

void connect_UsingGetAddrInfo_Wrong (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.  

    addrinfo hints, *addr;
    //fine-tune hints according to which socket you want to open
    hints.ai_family = domain; 
    hints.ai_socktype = socketType; 
    hints.ai_protocol = 0;           // no enum : possible value can be read in /etc/protocols
    hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;

    x =  getaddrinfo(hostname, NULL, &hints, &addr);
    //shall rather loop on addr linked list, but this is not the topic here.

    socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    x = connect(socketfd, addr->ai_addr, addr->ai_addrlen);
}

However, I was able to connect a socket to the same server, using gethostbyname method.

void connect_UsingGetHostByName_Deprecated (std::string host, unsigned short int port, int& socketfd)
{
    //simplified loops & error handling for concision
    int x;

    int domain = AF_INET;         // IP_v4
    int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.  

    struct hostent DNS, *r;
    char buf[1024];
    x = gethostbyname_r(hostname.c_str(), & DNS, buf, sizeof(buf), & r, & err));
    socketfd = socket(domain, socketType, 0);

    //server.
    sockaddr_in server;
    memset(&server, 0x00, sizeof(server));
    server.sin_family=domain;
    server.sin_port=htons(port);
    memcpy(& server.sin_addr.s_addr, DNS.h_addr, (size_t) DNS.h_length);
    x = connect(socketfd, (struct sockaddr *) & server, sizeof(server));
}

Running code shows that both version correctly retrieve the valid IP address of the server. Still the first one won't connect and will time out. Why ?


Solution

  • The reason why it kept failing was : to retrieve the addrinfo, I had left the field 'service' equals to NULL. It will still return success and provide you an address (which you can map with getnameinfo to the right IP address). Still the address won't be usable to connect your socket !

    I had found an hybrid version of both methods here : https://codereview.stackexchange.com/questions/17863/socket-connect-realization-gethostbyname-or-getnameinfo This one is functional, but i don't buy the casting

    void connect_UsingGetAddrInfo_HYBRID (std::string host, unsigned short int port, int& socketfd)
    {
        //simplified loops & error handling for concision
        int x;
    
        int domain = AF_INET;         // IP_v4
        int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams. 
    
        addrinfo hints, *addr;
        //fine-tune hints according to which socket you want to open
        hints.ai_family = domain; 
        hints.ai_socktype = socketType; 
        hints.ai_protocol = 0;           // no enum : possible value can be read in /etc/protocols
        hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;
    
        x =  getaddrinfo(host, NULL, &hints, &addr);
        socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    
        //here is the hybrid part
        sockaddr_in servAddr;
        memset(&servAddr, 0, sizeof(servAddr));
        servAddr.sin_family = addr->ai_family;
        servAddr.sin_addr.s_addr = *((uint32_t*) & (((sockaddr_in*)addr->ai_addr)->sin_addr));
        servAddr.sin_port        = htons(port);
    
        x=connect(socketfd, (struct sockaddr*) &servAddr, sizeof(servAddr));
    }
    

    In the end, it helped me find the rootcause :

    void connect_UsingGetAddrInfo_FIXED (std::string host, unsigned short int port, int& socketfd)
    {
        //simplified loops & error handling for concision
        int x;
    
        int domain = AF_INET;         // IP_v4
        int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.  
    
        addrinfo hints, *addr;
        //fine-tune hints according to which socket you want to open
        hints.ai_family = domain; 
        hints.ai_socktype = socketType; 
        hints.ai_protocol = 0;           // no enum : possible value can be read in /etc/protocols
        hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;
    
        //Precise here the port !
        const char* service = std::to_string(port).c_str();
    
        x =  getaddrinfo(host, service, &hints, &addr);
        socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        x = connect(socketfd, addr->ai_addr, addr->ai_addrlen);
    }
    

    Hope this will help someone one day !