0

I have an application installed locally (not developed by me), which broadcasts UDP packets every second. Reading the packets from my application (developed in C++ in Windows) which also is locally installed, works fine.

WSADATA data;
WORD version = MAKEWORD(2, 2);
int wsOK = WSAStartup(version, &data);

SOCKET serverIn = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in serverHint;
serverHint.sin_addr.S_un.S_addr = INADDR_ANY;
serverHint.sin_family = AF_INET;
serverHint.sin_port = htons(UDP_RECEIVE_PORT);

bind(serverIn, (sockaddr*)&serverHint, sizeof(serverHint));

sockaddr_in client;
int clientSize = sizeof(client);

int RECIEVE_BUFFER_SIZE = 65507;
char* recieveBuffer = new char[RECIEVE_BUFFER_SIZE];

while(updating)
{
    int bytesIn = recvfrom(serverIn, recieveBuffer, RECIEVE_BUFFER_SIZE, 0, (sockaddr*)&client, &clientSize);
}

closesocket(serverIn);
WSACleanup();

But I recently noticed while I was testing some code, while my app was running, that the bind(...) function returned an error code of 10048 (WSAEADDRINUSE). Hence, it seems the first client bound to listen for the UDP packets is the only one who can listen, and the other clients is unable to read the broadcasted UDP packets.

So then I added the SO_REUSEADDR option before calling the bind(...) function to be able to bind successfully to the socket:

BOOL bOptVal = TRUE;
int bOptLen = sizeof(BOOL);
setsockopt((SOCKET)serverIn, SOL_SOCKET, SO_REUSEADDR, (char*)&bOptVal, bOptLen);

That works, but the recvfrom(...) function then does not recieve any data at all! I guess it waits for the other client to close its socket.

Next solution is to initialize the socket with SOCK_RAW instead. The above option SO_REUSEADDR is now not needed, and remove it:

SOCKET serverIn = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);

This works, I can read the data now! Though, Windows now requires the adminstrator rights for my application. Also I do recieve the UDP information in the data which I do not need.

Is there any better method to do this without requiring administrator rights, any possibility to discard the header information in the buffer?

  • How specifically is that question related to c++, as all that code shown is plain c? – πάντα ῥεῖ Jan 29 '23 at 15:41
  • 2
    I don't get it, why are you binding multiple sockets to the same port? – Quimby Jan 29 '23 at 15:52
  • 1
    @Quimby That's most likely the issue. The client sockets normally shouldn't bind at all (or at least do not need to), instead should call `connect` function. Nicely illustrated [here](https://stackoverflow.com/questions/27014955/socket-connect-vs-bind). Even duplicate? – Aconcagua Jan 29 '23 at 16:12
  • I am still learning, so I am sorry if do not understand everything completely. @Aconcagua, thanks! I am testing connect instead of bind, but client does not find any broadcasted packets in the port while sniffing when using connect. With the connect function I had to initialize the S_addr in a different way: `int rInet = InetPton(AF_INET, L"127.0.0.1", &serverHint.sin_addr.S_un.S_addr);` No error codes or complaints. I will continue investigating/learning. – Johannes Bergmark Jan 29 '23 at 17:07
  • 2
    Remember that UDP lacks connections. Anyone can send messages to any UDP port and it's up to the receiver to sort out who's who. All of the clients can use the same port to talk. – user4581301 Jan 29 '23 at 17:18
  • Yes, strange though I am not able to read any data, if I do not bind the socket (which locks access for other clients which uses the same method, with the exception of SOCK_RAW which requires administrator rights). – Johannes Bergmark Jan 29 '23 at 17:27
  • 1
    @JohannesBergmark What "other clients"? Please explain the communication protocol - who communicates with whom and how. I think you are misusing sockets for something they are not meant to do. On a single computer with a single IP, there can only be a single listening UDP socket per port. You do not want to have more and abusing raw sockets or `SO_REUSEADDR` won't help you. Moreover any UDP packet only has a single receiver per network interface, you cannot have more. – Quimby Jan 29 '23 at 17:42
  • 1
    @Quimby Then, I think I have misunderstood everything about UDP. I thought several clients, connectionless, could read the stream of packets sent via UDP. Hence I pictured it as a lighthouse, where all the ships on sea could see and read (it seems possible though via RAW_SOCKETS). Oh well, I made a fool of myself today ... I am sorry for the confusion I made. – Johannes Bergmark Jan 29 '23 at 18:17
  • @JohannesBergmark No need to apologize. Well, there are UDP multicasts/broadcast but they are used practically for multiple IP addresses/computers on the same port. Other than that, both TCP and UDP are always point-to-point - a single sender a single receiver. If you need to send a single datagram to multiple clients, the server could store a "subscriber list". Each client can register its IP+port (e.g. by sending a special UDP datagram to the server) and then the server sends each outgoing datagram to all endpoints in the list. I guess some form of unsubscribe/timeout should be present. – Quimby Jan 29 '23 at 18:30
  • 1
    Have the server (and clients) use IGMP/multicast instead of unicast – Craig Estey Jan 29 '23 at 18:31
  • @JohannesBergmark for broadcast and multicast UDP packets (but notably *not* for unicast UDP packets), it is indeed possible for multiple clients to receive the same UDP packet, even if the clients are all running on the same host. I'm not sure why it's not working for you -- the SO_REUSEADDR step should be enough to enable that behavior. – Jeremy Friesner Jan 29 '23 at 18:36
  • 1
    Btw I recommend that you **not** use the `connect()` function in conjunction with UDP broadcast or multicast. All `connect()` does when called on a UDP socket is set a private variable inside the local network stack telling the OS where to send outgoing UDP packets when `send()` is called (which is fine, since otherwise you'd have to call `sendto()` all the time), but it also installs that IP address as a filter for incoming UDP packets, so that only packets whose source IP address is equal to the IP address specified in the `connect()` call will be seen by `recvfrom()` -- that's bad. – Jeremy Friesner Jan 29 '23 at 18:52

1 Answers1

1

Below is a little program I wrote to demonstrate that IPv4 UDP broadcast can and does work as expected under Windows (i.e. without requiring raw-sockets or Administrator privileges).

Run it with the command line argument "server" and it will send out one broadcast UDP packet per second.

Then also run several more instances of the same program, with no command line arguments, to receive the UDP packets and print a line of text to stdout whenever they do. The expected behavior should look like this:

screenshot of expected behavior in several DOS boxes

As for why it's not working for you -- one possible guess is that your UDP-packet-sending program is actually sending out unicast UDP packets rather than broadcast. If that's the case, then I would expect that only one client program would receive packets (even if multiple clients are bound to the same port). A network trace tool like Wireshark might be able to help you determine if the UDP packets being sent are broadcast or unicast.

Anyway, here's the code:

#include <stdio.h>
#include <ws2tcpip.h>

#pragma comment(lib,"WS2_32")

static int BindUDPSocket(SOCKET sock, unsigned short port, bool allowPortSharing)
{
   if (sock == INVALID_SOCKET) return -1;

   if (allowPortSharing)
   {
      const BOOL trueValue = true;
      if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &trueValue, sizeof(trueValue)) < 0) return -1;
   }

   struct sockaddr_in bindAddr; memset(&bindAddr, 0, sizeof(bindAddr));
   bindAddr.sin_family      = AF_INET;
   bindAddr.sin_addr.s_addr = INADDR_ANY;  // aka 0.0.0.0
   bindAddr.sin_port        = htons(port);
   return bind(sock, (struct sockaddr *) &bindAddr, sizeof(bindAddr));
}

int main(int argc, char ** argv)
{
   WSADATA data;
   WORD version = MAKEWORD(2, 2);
   (void) WSAStartup(version, &data);

   const unsigned short TEST_PORT = 12345;

   SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
   if (sock<0) {printf("socket() failed\n"); exit(10);}

   if ((argc > 1)&&(strcmp(argv[1], "server") == 0))
   {
      if (BindUDPSocket(sock, 0, false)<0) {printf("BindUDPSocket() failed for server\n"); exit(10);}

      const BOOL allowBroadcast = true;
      if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char *) &allowBroadcast, sizeof(allowBroadcast)) < 0)
      {
         printf("setsockopt(SO_BROADCAST) failed\n");
         exit(10);
      }

      const char buf[] = {0x01, 0x02, 0x03, 0x04};  // dummy data
      struct sockaddr_in toAddr; memset(&toAddr, 0, sizeof(toAddr));
      toAddr.sin_family      = AF_INET;
      toAddr.sin_addr.s_addr = INADDR_BROADCAST;  // aka 255.255.255.255
      toAddr.sin_port        = htons(TEST_PORT);

      printf("Sending outgoing broadcast UDP sockets on port %u, once per second\n", TEST_PORT);
      while(true)
      {
         if (sendto(sock, buf, sizeof(buf), 0, (const sockaddr *) &toAddr, sizeof(toAddr)) == sizeof(buf))
         {
            printf("Sent %zu bytes of broadcast UDP data\n", sizeof(buf));
         }
         else printf("sendto() failed!\n");

         Sleep(1000);  // wait 1 second
      }
   }
   else
   {
      if (BindUDPSocket(sock, TEST_PORT, true)<0) {printf("BindUDPSocket() failed for client\n"); exit(10);}

      printf("Waiting to receive incoming broadcast UDP sockets on port %u\n", TEST_PORT);
      while(true)
      {
         char buf[1024];
         const int ret = recv(sock, buf, sizeof(buf), 0L);
         printf("Received %i bytes of incoming UDP data\n", ret);
      }
   }
}
Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
  • Thanks! I have searched the sourcecode of the application which uses C#, and indeed there is no setting for using broadcast (EnableBroadcast = true). So I guess I need to contact the developer then. – Johannes Bergmark Jan 31 '23 at 07:11