Automated naming of AF_UNIX local datagram sockets?
Question
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?
Solution
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
OTHER TIPS
The unix(7) man page I referenced had this information about autobind UNIX sockets:
If a bind(2) call specifies addrlen as sizeof(sa_family_t), or the SO_PASSCRED socket option was specified for a socket that was not explicitly bound to an address, then the socket is autobound to an abstract address.
This is why the Linux kernel checks the address length is equal to sizeof(short) because sa_family_t is a short. The other unix(7) man page referenced by Rob's great answer says that client sockets are always autobound on connect, but because SOCK_DGRAM sockets are connectionless (despite calling connect on them) I believe this only applies to SOCK_STREAM sockets.
Also note that when supplying your own abstract namespace socket names, the socket's address in this namespace is given by the additional bytes in sun_path that are covered by the specified length of the address structure.
struct sockaddr_un me;
const char name[] = "\0myabstractsocket";
me.sun_family = AF_UNIX;
// size-1 because abstract socket names are not null terminated
memcpy(me.sun_path, name, sizeof(name) - 1);
int result = bind(fd, (void*)&me, sizeof(me.sun_family) + sizeof(name) - 1);
sendto() should likewise limit the address length, and not pass sizeof(sockaddr_un).
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.
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