Reputation: 19375
The manual of connect
says:
If the socket sockfd is of type SOCK_DGRAM then addr is the address to which datagrams are sent by default, and the only address from which datagrams are received.
The following C program seems to reveal an error in the Linux implementation of connect
/recv
on UNIX domain sockets, as it receives a message from an address (@"PWE0"
) other than the one it has connected to (@"STAR"
), and terminates. In contrast to that, the behaviour I expect is that the program doesn't terminate, because it faithfully waits for a message from the address it has connected to, which never arrives. (I deliberately didn't clutter the example with return value checks and print statements, since it can easily be examined with strace
; in real life, the calls would be made by different processes.)
#include <sys/socket.h>
char header[] = "PLD ";
int main()
{
#define addrlen sizeof(unsigned short) + sizeof (char [1+4]) + sizeof(int)
int fd = socket(PF_UNIX, SOCK_DGRAM, 0);
bind(fd, &(struct sockaddr){ AF_UNIX, "\0PLD " }, addrlen);
int gd = socket(PF_UNIX, SOCK_DGRAM, 0);
bind(gd, &(struct sockaddr){ AF_UNIX, "\0STAR" }, addrlen);
int hd = socket(PF_UNIX, SOCK_DGRAM, 0);
bind(hd, &(struct sockaddr){ AF_UNIX, "\0PWE0" }, addrlen);
sendto(hd, "PWE0\0\0\0\n\1h\0\0\0\1\0\0\0\1", 18, 0,
&(struct sockaddr){ AF_UNIX, "\0PLD " }, addrlen);
connect(fd, &(struct sockaddr){ AF_UNIX, "\0STAR" }, addrlen);
char buf[32];
return recv(fd, buf, sizeof buf, 0);
}
So, is Linux [as well as HP-UX (with file system pathnames)] violating the specification, or am I missing something?
Upvotes: 1
Views: 1422
Reputation: 2020
EDIT I noticed my first answer was wrong. So I edited a lot of the text.
EDIT2 Fixed addrlen taking into account that abstract address space is being used. The behaviour of the code remained the same.
Your definition of the addrlen
is a bit baffling.
You seem to only reserve 4+1 bytes for the address path. I figure, that "\0|P|L|D| |\0" is 6 bytes.
Where does the sizeof(int)
come from? I could not figure out what you meant by it.
However, I think the reason the sendto goes through, is the order of the sendto()
and connect()
. If you move the connect before the sendto, you get the behaviour you expected. Unix sockets buffer the datagrams. The sent datagram goes through before the connect "starts filtering". In the case where the connect and sendto are swapped, you can see the datagram being filtered when its received by the OS (from the sender). Seems there is no more filtering, when the user space application calls recv.
Here is an strace output (last lines only) of the modified program.
socket(PF_FILE, SOCK_DGRAM, 0) = 3
bind(3, {sa_family=AF_FILE, path=@"PLD "}, 8) = 0
socket(PF_FILE, SOCK_DGRAM, 0) = 4
bind(4, {sa_family=AF_FILE, path=@"STAR"}, 8) = 0
socket(PF_FILE, SOCK_DGRAM, 0) = 5
bind(5, {sa_family=AF_FILE, path=@"PWE0"}, 8) = 0
connect(3, {sa_family=AF_FILE, path=@"STAR"}, 8) = 0
sendto(5, "PWE0\0\0\0\n\1h\0\0\0\1\0\0\0\1", 18, 0, {sa_family=AF_FILE, path=@"PLD "}, 8) = -1 EPERM (Operation not permitted)
recvfrom(3, ^C <unfinished ...>
I changed the compact way of defining the addresses inside the function calls. Here is my modified code:
#include <sys/socket.h>
#include <sys/un.h>
int main()
{
const char hdr [] = "\0PLD ";
socklen_t addrlen = sizeof(sa_family_t) + sizeof(hdr);
int fd = socket(PF_UNIX, SOCK_DGRAM, 0);
bind(fd, &(struct sockaddr){ AF_UNIX, "\0PLD " }, addrlen);
int gd = socket(PF_UNIX, SOCK_DGRAM, 0);
bind(gd, &(struct sockaddr){ AF_UNIX, "\0STAR" }, addrlen);
int hd = socket(PF_UNIX, SOCK_DGRAM, 0);
bind(hd, &(struct sockaddr){ AF_UNIX, "\0PWE0" }, addrlen);
connect(fd, &(struct sockaddr){ AF_UNIX, "\0STAR" }, addrlen);
sendto(hd, "PWE0\0\0\0\n\1h\0\0\0\1\0\0\0\1", 18, 0,
&(struct sockaddr){ AF_UNIX, "\0PLD " }, addrlen);
char buf[32];
return recv(fd, buf, sizeof buf, 0);
}
I do admit it is a bit baffling. There must be some undefined behaviour relating to the addrlen
parameter of bind()
, since if I only change your original codes addrlen to sizeof(struct sockaddr_un), I get a different behaviour, where the connect() is refused and the recv blocks (maybe my modified code has a bug I did not see).
Upvotes: 2