views:

664

answers:

3

I'm implementing a simple service using datagrams over unix local sockets (AF_UNIX address family, i.e. not UDP). The server is bound to a public address, and it receives requests just fine. Unfortunately, when it comes to answering back, sendto fails unless the client is bound too. (the common error is Transport endpoint is not connected).

Binding to some random name (filesystem-based or abstract) works. But I'd like to avoid that: who am I to guarantee the names I picked won't collide?

The unix sockets' stream mode documentation tell us that an abstract name will be assigned to them at connect time if they don't have one already. Is such a feature available for datagram oriented sockets?

A: 

I'm not so sure I understand your question completely, but here is a datagram implementation of an echo server I just wrote. You can see the server is responding to the client on the same IP/PORT it was sent from.

Here's the code

First, the server (listener)

from socket import *
import time
class Listener:
    def __init__(self, port):
        self.port = port
        self.buffer = 102400

    def listen(self):

        sock = socket(AF_INET, SOCK_DGRAM)
        sock.bind(('', self.port))

        while 1:
            data, addr = sock.recvfrom(self.buffer)
            print "Received: " + data
            print "sending to %s" % addr[0]
            print "sending data %s" % data
            time.sleep(0.25)
            #print addr # will tell you what IP address the request came from and port
            sock.sendto(data, (addr[0], addr[1]))
            print "sent"
        sock.close()

if __name__ == "__main__":
    l = Listener(1975)
    l.listen()

And now, the Client (sender) which receives the response from the Listener

from socket import *
from time import sleep
class Sender:
    def __init__(self, server):
       self.port = 1975
       self.server = server
       self.buffer = 102400

    def sendPacket(self, packet):
        sock = socket(AF_INET, SOCK_DGRAM)
        sock.settimeout(10.75)


        sock.sendto(packet, (self.server, int(self.port)))

        while 1:
            print "waiting for response"
            data, addr = sock.recvfrom(self.buffer)
            sock.close()
            return data



if __name__ == "__main__":
        s = Sender("127.0.0.1")
        response = s.sendPacket("Hello, world!")
        print response
Nick Stinemates
The question was about unix sockets, not internet ones. (i.e., AF_UNIX address family, not AF_INET). Thanks for your answer, I'll edit the question and make that explicit.
JB
Sorry about that, Thanks for the clarification.
Nick Stinemates
+2  A: 

I assume that you are running Linux; I don't know if this advice applies to SunOS or any UNIX.

First, the answer: after the socket() and before the connect() or first sendto(), try adding this code:

struct sockaddr_un me;
me.sun_family = AF_UNIX;
int result = bind(fd, (void*)&me, sizeof(short));

Now, the explanation: the the unix(7) man page says this:

When a socket is connected and it doesn’t already have a local address a unique address in the abstract namespace will be generated automatically.

Sadly, the man page lies.

Examining the Linux source code, we see that unix_dgram_connect() only calls unix_autobind() if SOCK_PASSCRED is set in the socket flags. Since I don't know what SOCK_PASSCRED is, and it is now 1:00AM, I need to look for another solution.

Examining unix_bind, I notice that unix_bind calls unix_autobind if the passed-in size is equal to "sizeof(short)". Thus, the solution above.

Good luck, and good morning.

Rob

Rob Adams
Great analysis, Rob! Thanks a lot!There seems to be different sources to the manpages: at work (some old SuSE), they explicitely restrict the autobind-on-connect effect to stream sockets. At home, I've got the same sentence as you.In the long run, I'll remember this as binding to an empty path.
JB
A: 

A bit of a late response, but for whomever finds this using google as I did. Rob Adam's answer helped me get the 'real' answer to this: simply use set (level SO_SOCKET, see man 7 unix) to set SO_PASSCRED to 1. No need for a silly bind.

I used this in PHP, but it doesn't have SO_PASSCRED defined (stupid PHP). It does still work, though, if you define it yourself. On my computer it has the value of 16, and I reckon that it will work quite portably.

What's so silly about a bind? Naming sockets is done with it, it's not a huge stretch that the autonaming would be forced with it too. From a documentation (implying maintenance) point of view, SO_PASSCRED is probably better. For intuitiveness, though, SO_PASSCRED is the silly one.
JB