1

I'm trying to implement a basic UDP protocol wherein a Sender sends a UDP datagram to a Service, which then sends a response datagram back, using the source address and source port from the incoming datagram.

Normally you'd have the Sender also listen for the response on that port. But I want the response to be picked up by a separate program (the Listener) also running on that host. So:

  1. On host A, Listener starts and binds to port 12345, and blocks on recvfrom.
  2. On host A, Sender sends datagram to Service running on host B, setting the source address and port to host A, port 12345.
  3. Service on host B sends a response to host A port 12345.
  4. Response is picked up by Listener.

Setting the source address and port is done by binding to them. So I need both Sender and Listener to bind to the same port. Setting SO_REUSEADDR in both allows this. Note that I'm not using multicast here.

But the responses aren't reliably being picked up by Listener. There are two exceptions I've observed:

I find that if the Sender closes the socket immediately after sending the first datagram, then the response will get to the Listener.

Alternatively, if the Sender is started first and binds before the Listener, the responses will get picked up by the Listener.

I've been working from examples from the internet and haven't found documentation that clearly describes what should happen. But a few places I've seen have hinted that, for Unicast, only the most recent process to bind to the port will receive datagrams sent to it.

My question is, can I send UDP datagrams so that responses (sent using the source address and port) will be picked up by another process? If the above process can't be made to work, is there a way to set the source information on an outgoing datagram without binding to that port?

A few other points:

  • Each process should be started independently and be able to be restarted without interfering with the other. So I don't think I can have one open the socket and spawn the other.
  • I don't need to receive packets from both processes. One process only sends, and the other only receives.
  • Ideally, the solution would be portable enough to run on common Unixes and Windows.
  • Finally, if it's simply not possible then I'll fall back to using a single process to perform both functions. I'm not too stressed about it but I am interested in doing it if it is possible somehow. :-)

Networking code follows...

Sender code

void run(Options *options)
{
    struct sockaddr_in si_me, si_other;
    int s;
    socklen_t slen = sizeof(si_other);
    int reuse = 1;
    struct hostent *he;

    if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
        die("socket");

    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0)
        die("setsockopt");

    // Bind to the "listen port", so that outgoing datagrams have the correct source information
    memset((char *) &si_me, 0, sizeof(si_me));
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(options->listen_port);
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(s, (struct sockaddr *) &si_me, sizeof(si_me)) != 0)
        die("bind");

    memset((char *) &si_other, 0, sizeof(si_other));
    si_other.sin_family = AF_INET;
    si_other.sin_port = htons(options->service_port);

    if (!(he = gethostbyname2(options->service_host, AF_INET)))
        die("gethostbyname2");

    memmove(&si_other.sin_addr.s_addr, he->h_addr, he->h_length);

    while (1)
    {
        int len;
        char *buf;

        // Create outgoing message in buf
        ...

        if (sendto(s, buf, len, 0, (struct sockaddr *) &si_other, slen) == -1)
            die("sendto");
    }
    close(s);
}

Listener code

static void run(Options *options)
{
    struct sockaddr_in si_me, si_other;
    int s;
    socklen_t slen = sizeof(si_other);
    char buf[BUFLEN];
    int reuse = 1;

    if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
        die("socket");

    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) != 0)
        die("setsockopt");

    // Bind to the same "listen port" to pick up responses to datagrams sent by Sender
    memset((char *) &si_me, 0, sizeof(si_me));
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(options->listen_port);
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(s, (struct sockaddr *) &si_me, sizeof(si_me)) == -1)
        die("bind");

    while (1)
    {
        int nr;

        nr = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen);
        if (nr == -1)
            die("recvfrom");

        // Process the received message
        ...
    }

    close(s);
}

A related question is Using netcat to send a UDP packet without binding, where one answer seems to suggest it should be possible using SO_SOCKADDR, but did not quite explain how it would work in my case.

Community
  • 1
  • 1
Edmund
  • 10,533
  • 3
  • 39
  • 57

2 Answers2

0

is there a way to set the source information on an outgoing datagram without binding to that port?

There is no portable way. A solution for Linux, using IP_PKTINFO, is this answer to How to re bind a udp socket in Linux.

Community
  • 1
  • 1
Armali
  • 18,255
  • 14
  • 57
  • 171
-1

1: You can send from different port on B

A binds 12345 sends to B:12345

B:12345 - process 1 - recv 
B:12346 - process 2 - send to A:12345

2: You can construct packet with fake back address with raw sockets

First solution is better

Max
  • 6,286
  • 5
  • 44
  • 86
  • Can you clarify option 1? Note that the Service on host B listens on a completely different port already, and simply sends responses to the source address/port on the datagrams sent to it. And it's out of my control. Also a small code sample for option 2 would be nice. – Edmund Sep 02 '12 at 23:15
  • 1
    How could sending from a different port on B fix this problem? – user207421 Sep 02 '12 at 23:16
  • 1
    I can't write small sample in raw socket (I have not written code in raw sockets) but raw socket permit to construct any package if application has privileges. Raw sockets should not be used in regular applications like yours as you can use UDP. Raw sockets often used to demonstrate exploits. Microsoft removed raw sockets from Windows XP because of that. – Max Sep 02 '12 at 23:25
  • 1
    @Max How will sending from a different port in host B overcome the port contention problem in port A? And how does sending via raw sockets and faking the back address solve it? The underlying problem here is the two listeners on the same port in host A. Nothing you have said here addresses that. – user207421 Sep 17 '12 at 09:57