Search code examples
c++socketsargvhtonlinet-aton

I'm developing a server and ip passed as parameter not working


I'm using:

struct in_addr ip;                                         
#define HOST_PORT atoi(argv[2])
#define HOST_IP inet_aton(argv[1], &ip)

sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = htonl(HOST_IP);
sockaddr.sin_port = htons(HOST_PORT);

To get the ip and port numbers in cli. Then i bind, listen, accept and read.

I do compile and when i use ./server localhost 8080 everything is working as intended.

But if i use ./server 127.0.0.1 8080 or ./server 192.168.30.1 8080 seems like is bind and listening, no errors but i don't recive the request.


Solution

  • You are misusing inet_aton().

    You are assigning the return value of inet_aton() to your sockaddr variable, rather than assigning the in_addr struct that inet_aton() outputs. The return value is NOT an IP address!

    inet_aton() returns a non-zero value on success, and 0 on failure. In other words, its return value is basically a boolean value.

    Calling bind() with an IP address of 0.0.0.0 (aka INADDR_ANY) instructs it to bind an AF_INET socket to all local network interfaces on the specified port, which is very common (and generally preferred) for a server to do.

    Calling inet_aton("localhost") fails because you can't pass a hostname to inet_aton(), so it returns 0, and then you bind() the socket using a sockaddr_in whose s_addr is set to INADDR_ANY, which is OK, so your code seems to work with "localhost", but only as a fluke.

    Calling inet_aton("127.0.0.1") or inet_aton("192.168.30.1") succeeds, so it returns non-zero, and then you bind() the socket using a sockaddr_in whose s_addr is set to an invalid IP, causing bind() to either fail outright, or worse to bind to an existing network interface that is different than the one you are expecting.

    Also, the in_addr that inet_aton() outputs is already in network byte order, so there is no need to call htonl() on its output.

    Try this instead:

    struct in_addr ip;
    #define HOST_PORT atoi(argv[2])
    #define HOST_IP ((inet_aton(argv[1], &ip) != 0) ? ip.s_addr : 0)
    
    struct sockaddr_in sockaddr;
    memset(&sockaddr, 0, sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = HOST_IP;
    sockaddr.sin_port = htons(HOST_PORT);
    
    // use sockaddr as needed ...
    

    Or, simply get rid of your HOST_IP and HOST_PORT macros, as they are not really helping you:

    struct sockaddr_in sockaddr;
    
    memset(&sockaddr, 0, sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    inet_aton(argv[1], &(sockaddr.sin_addr));
    sockaddr.sin_port = htons(atoi(argv[2]));
    
    // use sockaddr as needed ...
    

    That being said, you really should be using getaddrinfo() instead, eg:

    struct addrinfo hints, *res;
    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    
    if (getaddrinfo(argv[1], argv[2], &hints, &res) == 0)
    {
        // use res->ai_addr and res->ai_addrlen with socket() and bind() as needed ...
        freeaddrinfo(res);
    }
    

    getaddrinfo() will parse both IP address strings and hostname strings into appropriate sockaddr_... structs for you, and will happily handle strings that are related to the local host, like "localhost", "127.0.0.1", "192.168.30.1", etc.