Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  • In Linux it means "listen on all interfaces" and for sending - send through default interface
  • In Windows it means "listen on default interface" (0.0.0.1 for second one). Citation: "If this member specifies an IPv4 address of 0.0.0.0, the default IPv4 multicast interface is used" - without mentioning whether it is for listening or for sending.

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;
like image 765
Paul Avatar asked Jan 17 '17 10:01

Paul


People also ask

What is Inaddr_any in socket programming?

This is an IP address that is used when we don't want to bind a socket to any specific IP. Basically, while implementing communication, we need to bind our socket to an IP address. When we don't know the IP address of our machine, we can use the special IP address INADDR_ANY .

What is bind in socket programming?

The bind() function binds a unique local name to the socket with descriptor socket. After calling socket(), a descriptor does not have a name associated with it. However, it does belong to a particular address family as specified when socket() is called. The exact format of a name depends on the address family.

What is Inaddr_loopback?

Macro: uint32_t INADDR_LOOPBACK. You can use this constant to stand for “the address of this machine,” instead of finding its actual address. It is the IPv4 Internet address ' 127.0. 0.1 ', which is usually called ' localhost '.

What is Af_inet socket?

AF_INET is an address family that is used to designate the type of addresses that your socket can communicate with (in this case, Internet Protocol v4 addresses). When you create a socket, you have to specify its address family, and then you can only use addresses of that type with the socket.

What is the difference between inaddr_any and Listen (2) in sockets?

When INADDR_ANY is specified in the bind call, the socket will be bound to all local interfaces. When listen (2) is called on an unbound socket, the socket is automatically bound to a random free port with the local address set to INADDR_ANY.

What is the difference between socket handles in Linux and Windows?

This means that on Linux and macOS, you can generally use all of the general purpose file functions with socket handles (e.g. read (), write () ). On Windows, socket handles can only be used with special socket functions. On Windows you can set some socket features with the ioctlsocket () function.

What is the difference between a socket and a file descriptor?

On Windows 64 bits, the SOCKET type is twice larger in size. The socket descriptor on BSD does not differ from the file descriptor. It means that some system calls accept descriptors of sockets and files simultaneously (for example, such commonly used calls as close (), fcntl (), and ioctl () ).

How does inaddr_any generate a random IP from a socket?

bind () of INADDR_ANY does NOT "generate a random IP". It binds the socket to all available interfaces. For a server, you typically want to bind to all interfaces - not just "localhost".


1 Answers

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);
}
like image 136
dbush Avatar answered Oct 14 '22 05:10

dbush