3

Spent a couple of hours searching, still puzzled. From what I've found, INADDR_ANY is meant to specify that the socket will accept connections with any address that is assigned to the server. The following, however, results in the client only being able to connect to localhost:7777 from the same machine.

addrinfo hints;
addrinfo* result;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(INADDR_ANY, "7777", &hints, &result);

SOCKET listenSocket = INVALID_SOCKET;
listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

bind(listenSocket, result->ai_addr, (int)result->ai_addrlen);

The only solution that I've found is to change INADDR_ANY to the machine's local IP:

getaddrinfo("192.168.0.105", "7777", &hints, &result);

I need to understand how INADDR_ANY works, because I feel like I'm just misusing it somehow. Any help would be appreciated.

alk
  • 69,737
  • 10
  • 105
  • 255
  • 2
    `INADDR_ANY` has the effect you expect ***when it is passed as a parameter directly to `bind()`***. You're not doing it. You're passing this value to `getaddrinfo()`. Which is a completely different story. Now, why don't you examine what `getaddrinfo()`returns, in this case, and figure this out yourself. If you want to accept connections on any local IP address, you don't need `getaddrinfo()`. Get rid of this whole thing, in its entirety, and call `bind()` directly. – Sam Varshavchik Oct 08 '17 at 01:22
  • 1
    How do you even know if `getaddrinfo()` even *worked*? You're not bothering to check the return value. Per [the POSIX standard](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html): "A zero return value for `getaddrinfo()` indicates successful completion; a non-zero return value indicates failure. The possible values for the failures are listed in the ERRORS section." – Andrew Henle Oct 08 '17 at 01:29
  • `getaddrinfo` can return *several* addresses; and you also need to `freeaddrinfo(result)` yet you ever bind the first one only. – Antti Haapala -- Слава Україні Oct 08 '17 at 04:13
  • Did you read [the documentation](https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520(v=vs.85).aspx)? – alk Oct 08 '17 at 07:49
  • I omitted the error checking for brevity sake, my bad. – Raimonds Rainskis Oct 09 '17 at 04:55
  • @SamVarshavchik Sorry I'm a bit confused: how can we skip `getaddrinfo` and directly use `bind()`? like, if so how do we know the address to bind to? – Crystina Sep 24 '20 at 00:41
  • You know it, @Crystina, because it is what you directly specify to `bind()`. See `bind()`'s documentation for more information. You specify `INADDR_ANY` as the address to bind to. The End. – Sam Varshavchik Sep 24 '20 at 00:59

1 Answers1

6

INADDR_ANY has nothing to do with your issue.

The first parameter of getaddrinfo() is a const char * specifying an IP address or hostname. But INADDR_ANY is an integer instead. The only reason your code even compiles is because INADDR_ANY is defined as integer constant 0, which is the only integer constant that is allowed to be assigned to a pointer. So, you are actually passing NULL to the first parameter. Which is fine in this situation, as that is what you need anyway.

What you are not taking into account is that getaddrinfo() returns a linked list of addresses, which may contain multiple addresses, especially if you use AF_UNSPEC. Using that family tells getaddrinfo() that it can return addresses for both IPv4 and IPv6. But you are only using the first address in the list, which just happens to correspond to localhost (127.0.0.1 in IPv4, ::1 in IPv6).

For a server, you should be creating and binding a separate listening socket for each address in the output list. That will let you bind to all of the addresses that match the criteria you passed to getaddrinfo(), eg:

addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;

addrinfo* result;

if (getaddrinfo(NULL, "7777", &hints, &result) == 0)
{
    for (addrinfo *addr = result; addr != NULL; addr = addr->ai_next)
    {
        SOCKET listenSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (listenSocket != INVALID_SOCKET)
        {
            bind(listenSocket, addr->ai_addr, (int)addr->ai_addrlen);
            listen(listenSocket, ...);
            // store listenSocket in a list for later use... 
        }
    }
    freeaddrinfo(result);
}

// use listening sockets as needed... 

Alternatively, as Sam V mentioned in comments, you can skip getaddrinfo(), it is not really helping you in this situation. You could just create and bind 2 sockets directly, one for IPv4 and one for IPv6:

SOCKET listenSocket4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket4 != INVALID_SOCKET)
{
    sockaddr_in addr = {};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(7777);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(listenSocket4, (sockaddr*) &addr, sizeof(addr));
    listen(listenSocket4, ...);
}

SOCKET listenSocket6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket6 != INVALID_SOCKET)
{
    sockaddr_in6 addr = {};
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(7777);
    addr.sin6_addr = in6addr_any;

    bind(listenSocket6, (sockaddr*) &addr, sizeof(addr));
    listen(listenSocket6, ...);
}

// use listening sockets as needed... 

Or better, create and bind a single dual-stack socket that supports both IPv4 and IPv6:

SOCKET listenSocket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket != INVALID_SOCKET)
{
    BOOL off = FALSE;
    setsockopt(listenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off));

    sockaddr_in6 addr = {};
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(7777);
    addr.sin6_addr = in6addr_any;

    bind(listenSocket, (sockaddr*) &addr, sizeof(addr));
    listen(listenSocket, ...);
}

// use listening socket as needed... 
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you, the last 2 options are what I what I was looking for. The guides I managed to find were all old and narrow in their scope, and this being the first time that I write netcode without a framework to help me, I didn't know anything about how the API is supposed to work. – Raimonds Rainskis Oct 09 '17 at 05:06