Reputation: 1401
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
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