Paul
Paul

Reputation: 26680

Difference between INADDR_ANY in Linux and Windows socket programming

My Winsock Delphi application should listen on all network interfaces for multicast UDP/IP stream. It listened normally until I tried it on another PC with different network adapters' priority order.

Then I started to research problem and found on some forums that INADDR_ANY (or 0.0.0.0) has different meaning in Windows and Linux:

Could you confirm or deny this?

How to listen really on all interfaces?

Here is a little piece of my code:

TMulticastListener = class(TThread)
private
  mreq: ip_mreq;
  ............
end;

constructor TMulticastListener.Create;
var err: Integer;
    wData: WsaData;
    reuse: Integer;
begin
  inherited Create(true);

  err := WSAStartup(MAKEWORD(2, 2), wData);
  if err = SOCKET_ERROR then begin
    // Tell the user that we could not find a usable Winsock DLL
    perror('WSAStartup');
    Exit;
  end;

  // create what looks like an ordinary UDP socket
  fd := socket(AF_INET, SOCK_DGRAM, 0);
  if fd = INVALID_SOCKET then begin
    perror('socket');
      Exit;
  end;

  reuse := 1;

  // allow multiple sockets to use the same PORT number
  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, Pointer(@reuse), SizeOf(reuse)) < 0) then begin
    perror('Reusing ADDR failed');
    Exit;
  end;

  // set up destination address
  FillChar(addr, sizeof(addr), 0);
  addr.sin_family := AF_INET;
  addr.sin_addr.s_addr := htonl(INADDR_ANY); // N.B.: differs from sender
  addr.sin_port := htons(HELLO_PORT);

  // bind to receive address
  if (bind(fd, addr, sizeof(addr)) < 0) then begin
      perror('bind');
      Exit;
  end;

  // use setsockopt() to request that the kernel join a multicast group
  mreq.imr_multiaddr.s_addr := inet_addr(HELLO_GROUP);
  mreq.imr_interface.s_addr := htonl(INADDR_ANY); //inet_addr('0.0.0.0');
  if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, @mreq, sizeof(mreq)) < 0) then begin
      perror('setsockopt');
      Exit;
  end;
end;

Upvotes: 6

Views: 2580

Answers (2)

dbush
dbush

Reputation: 224457

Windows and Linux actually behave the same regarding the use of INADDR_ANY. The confusion here is because the two links you provide are being used in different contexts.

When using the bind function to bind to an address/port, specifying INADDR_ANY means that the socket will be able to receive packets on the given port from any interface. However, doing so does not set up anything regarding multicast.

In the context of the IP_ADD_MEMBERSHIP call to setsockopt, setting the interface to INADDR_ANY will have the system join the given multicast group on the default network interface.

The Linux link you gave refers to bind, while the Windows link refers to setsockopt and IP_ADD_MEMBERSHIP.

If you want to join the multicast group on all interfaces, you need to retrieve the list of interfaces on the system and join each one. On Windows, the GetAdaptersAddresses() function will give you the list of interfaces. On Linux, use the getifaddrs() function.

Here's an example of how to use the GetAdaptersAddresses() function in C:

struct iflist {
    char name[50];
    struct sockaddr_in sin;
    int isloopback;
    int ismulti;
    int ifidx;
};

void getiflist(struct iflist *list, int *len)
{
    IP_ADAPTER_ADDRESSES *head, *curr;
    IP_ADAPTER_UNICAST_ADDRESS *uni;
    char *buf;
    int buflen, err, i;

    buflen = 100000;
    buf = calloc(buflen, 1);
    head = (IP_ADAPTER_ADDRESSES *)buf;
    if ((err = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, head,
                                    &buflen)) != ERROR_SUCCESS) {
        char errbuf[300];
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
                      0, errbuf, sizeof(errbuf), NULL);
        printf("GetAdaptersAddresses failed: (%d) %s", err, errbuf);
        free(buf);
        return;
    }
    for (*len = 0, curr = head; curr; curr = curr->Next) {
        if (curr->IfType == IF_TYPE_TUNNEL) continue;
        for (uni = curr->FirstUnicastAddress; uni; uni = uni->Next) {
            if (curr->OperStatus == IfOperStatusUp) {
                memset(&list[*len], 0, sizeof(struct iflist));
                strncpy(list[*len].name, (char *)curr->AdapterName,
                        sizeof(list[i].name) - 1);
                memcpy(&list[*len].sin, uni->Address.lpSockaddr,
                        uni->Address.iSockaddrLength);
                list[*len].isloopback =
                        (curr->IfType == IF_TYPE_SOFTWARE_LOOPBACK);
                list[*len].ismulti =
                        ((curr->Flags & IP_ADAPTER_NO_MULTICAST) == 0);
                if (uni->Address.lpSockaddr->sa_family == AF_INET6) {
                    list[*len].ifidx = curr->Ipv6IfIndex;
                } else {
                    list[*len].ifidx = curr->IfIndex;
                }
                (*len)++;
            }
        }
    }
    free(buf);
}

Upvotes: 6

specializt
specializt

Reputation: 1911

your source is completely oblivious of the fact that the internet protocol itself does not know anything about "ports" and "interfaces", the aforementioned statement ("listen on all interfaces") doesnt even make any sense, its completely made up but packets to broadcast addresses typically are routed onto multiple interfaces, more on this below:

0.0.0.0 is a special, reserved IPv4 address called "network identifier" - in fact IPv4 adresses which end with 0 typically are reserved - it is typically not usable except for broadcast and network purposes. Operating systems usually reserve 0.0.0.0 for broadcasts within one single transport protocol

Now : these broadcast addresses always receive broadcasts for one single transport protocol via the default route which may point to multiple (or all) network interfaces. What you probably were reading about is something completely different : Multicast - thats yet another can of worms, it is possible to send singular packets to multiple, designated receivers - Microsoft Windows has a default multicast route and Linux typically has to be configured for multicast in order to work (AFAIK) - but you dont want that.

Conclusion : for your purposes, 0.0.0.0 is identical on Windows and Linux - its a broadcast address for your chosen transport protocol, there is no difference

Upvotes: -1

Related Questions