dangeroushobo
dangeroushobo

Reputation: 1401

Abstract Unix socket between C and Go

I'm using an abstract Unix socket for passing data between a C and Go program. The C program is creating the socket and the Go program connects to it. The issue is the Go program fails to connect to the socket, and I receive the following error message:

UDS connection failed: dial unixgram @uds-js: connect: connection refused

Here is the C program:

#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

/* Buffer size for the receive socket */
#define BUFFER_SIZE 4096

/* The abstract Unix domain socket address name */
#define UDS_ADDRESS_NAME "#uds-js"

int main() {
    int socket_fd;
    int bytes_received;
    char buffer[BUFFER_SIZE];
    struct sockaddr_un server_address;
    struct sockaddr_un client_address;
    socklen_t address_length = sizeof(struct sockaddr_un);

    /* Create local unix socket */
    if ( ( socket_fd = socket ( AF_UNIX, SOCK_DGRAM, 0 ) ) < 0 ) {
        printf ( "socket error\n" );
        return 1;
    }

    /* Set an abstract socket address */
    memset( &server_address, 0, sizeof(server_address) );
    server_address.sun_family = AF_UNIX;
    strcpy( server_address.sun_path, UDS_ADDRESS_NAME );
    server_address.sun_path[0] = '\0';

    /* Bind socket to address */
    if ( bind ( socket_fd, (const struct sockaddr *) &server_address, address_length ) < 0 ) {
        close ( socket_fd );
        printf ( "socket bind error\n" );
        return 1;
    }
    bytes_received =
            recvfrom(
                socket_fd,
                &buffer,
                BUFFER_SIZE,
                0,
                (struct sockaddr *) &client_address,
                &address_length );
    printf ( "Received: %s\n", buffer );
    return 0;
}

And the Go program:


import (
    "fmt"
    "net"
    "os"
)

func main() {
    addr, _ := net.ResolveUnixAddr("unixgram", "@uds-js")
    udsSock, err := net.DialUnix("unixgram", nil, addr)
    if err != nil {
        fmt.Printf("UDS connection failed: %v\n", err)
        os.Exit(1)
    }
    defer udsSock.Close()
    if _, err := udsSock.Write([]byte("{\"test\":100}")); err != nil {
        fmt.Printf("Failed to send message on UDS: %v\n", err)
    }
}

In the C program I set the first byte in the socket name to a null byte, as to spec. From what I've gathered in Go the name needs to start with a @.

Running netstat I can see the socket was created:

$ netstat -ax | grep DGRAM
unix  2      [ ]         DGRAM                    12411992 @uds-js@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Why does the Go program fail to connect to the socket?

EDIT:

Changing the name to a pathname, /tmp/uds-js, and this does work as expected.

EDIT2:

I created a server in Go and a client in C for the abstract socket and the two C programs and the two Go programs work okay together. The issue seem to be when going from C to Go using abstract sockets.

Upvotes: 3

Views: 2688

Answers (1)

pilcrow
pilcrow

Reputation: 58681

The Go program is connecting to the wrong abstract socket (or, equivalently, the C program is binding to the wrong abstract socket).

Your bind() is made against an abstract namespace UNIX socket address of length sizeof(struct sockaddr_un). If I am reading the Go implementation correctly, however, your connect() is performed against a socket address of length nine: two bytes for sa_family, and seven bytes, in this case, for len(name).

Those are different sockets.

In the abstract namespace, NULLs aren't special, so "\0uds-js" is one valid socket address, and "\0uds-js\0\0\0\0\0\0\0\0..." is a different valid address.

strace both processes looking at the C program's bind() and the Go program's connect(). I expect you will see that they call those functions with different socklen_t arguments.

Upvotes: 5

Related Questions