Skip to content
Advertisement

Unnamed unix domain socket has wrong address length

When reading the address of a sending unix datagram domain socket in this minimal example program, I am getting an address length of 0, but the man page for unix domain sockets specifies that:

unnamed: A stream socket that has not been bound to a pathname using bind(2) has no name. Likewise, the two sockets created by socketā€ pair(2) are unnamed. When the address of an unnamed socket is returned, its length is sizeof(sa_family_t), and sun_path should not be inspected.

I was expecting the address length of an unnamed socket to be 2 based on this man page. Am I misinterpreting the man page, does the man page not apply to unix sockets of type SOCK_DGRAM or am I simply reading the length incorrectly?

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>

const char SOCK_NAME[] = { 0, 't', 'e', 's', 't' };
// or for pathnamed socket
// const char SOCK_NAME[] = "/tmp/test.uds";

const char PAYLOAD[] = "Hello!";

int main() {
    int rx_sock = socket(AF_UNIX, SOCK_DGRAM, 0);

    if (rx_sock < 0) {
        perror("Create RX");
        exit(1);
    }

    int tx_sock = socket(AF_UNIX, SOCK_DGRAM, 0);

    if (tx_sock < 0) {
        perror("Create TX");
        exit(1);
    }

    struct sockaddr_un bind_addr;
    bind_addr.sun_family = AF_UNIX;
    memcpy(bind_addr.sun_path, SOCK_NAME, sizeof(SOCK_NAME));

    socklen_t bind_len = sizeof(sa_family_t) + sizeof(SOCK_NAME);

    if (bind(rx_sock, (const struct sockaddr *)&bind_addr, bind_len) != 0) {
        perror("Bind RX");
        exit(1);
    }

    if (sendto(tx_sock, PAYLOAD, sizeof(PAYLOAD), 0, (const struct sockaddr *)&bind_addr, bind_len) < 0) {
        perror("Sendto");
        exit(1);
    }

    // For pathnamed socket
    // unlink(SOCK_NAME);

    struct sockaddr_un recv_addr;
    socklen_t recv_len = sizeof(recv_addr);
    char buffer[1024];

    ssize_t rx_count = recvfrom(rx_sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&recv_addr, &recv_len);

    if (rx_count < 0) {
        perror("Recvfrom");
        exit(1);
    }

    printf("Address size of TX on receiver side: %dn", recv_len); // 0

    recv_len = sizeof(recv_addr);

    if (getsockname(tx_sock, (struct sockaddr *)&recv_addr, &recv_len) != 0) {
        perror("getsockname");
        exit(1);
    }

    printf("Address size of TX on sender side: %dn", recv_len); // 2
}

Advertisement

Answer

You have an unnamed unbound Unix domain datagram socket tx_sock, and an Unix domain datagram socket rx_sock bound to an abstract address in bind_path.

Note: According to man 7 unix, unbound Unix domain stream sockets are unnamed in Linux, and their address lengths will be sizeof (sa_family_t). According to POSIX.1, unbound socket addresses are unspecified, so we really need to be careful about what we expect of unbound socket addresses and their lengths. In this particular case, in Linux, using the man page as a guide, an unbound Unix domain datagram socket has no address, so its length is zero. (It even makes sense: zero address length indicates you cannot reply to the sender. With an unbound stream socket, there is a connection back to the sender, but no other way to reply to the sender but the connection itself; that is why in that case the address length is then nonzero, sizeof (sa_family_t).)

Abstract Unix domain socket addresses are a Linux extension; they begin with a NUL byte (), and are not visible in the filesystem.

You use sendto(tx_sock, msg, msg_len, 0, bind_path, bind_path_len) to send a message.

You use recvfrom(rx_sock, buffer, sizeof buffer, 0, &recv_addr, &recv_addrlen) to receive the message (with recv_addrlen properly initialized to sizeof recv_addr).

Because tx_sock is not bound to any address, it is unnamed unbound: it has no address, cannot be replied to, and therefore recv_addrlen == 0.

If you add code to bind tx_sock to some other abstract address, you’ll receive that address in recv_addr and recv_addrlen.

(I verified this behaviour with a test program using four threads, where each thread sends a message to each other thread, and prints all received messages and where they are from, with both standard addresses and abstract addresses. Everything works as documented.)

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement