Introduction

Stream Socket and Datagram Socket are usually based on tcp and udp protocols for data transfer respectively. Both sockets have one common feature, that is, they require an IP address and a port to establish a connection between the client and the server.

This article explains a special socket that does not require a traditional IP address and port, but uses the file system for data interaction between programs, and can only be used on unix systems. Such a socket is the Unix domain socket that we will be talking about today.

What is a Unix domain socket

What is a Unix domain socket? We can see from the name that this socket is related to the unix domain, which means that this socket needs to use some special functions under unix.

Let’s consider the common windows system and unix system, where is the biggest difference between them?

In fact, the biggest difference is that everything in the unix operating system can be regarded as a file, including some information about the operation of the program.

So can we directly use the files generated by these programs to interact with the data between different programs? The answer is yes. This is the Unix domain socket that we will discuss today.

Unix domain sockets can be abbreviated as UDS, where data between different programs can be exchanged at the operating system level, with the help of the file system.

For the program itself, it only needs to read and write to the shared socket file, which means that different programs interact with each other through the socket file.

Like IP and port-based sockets, Unix domain sockets can be divided into Stream sockets and Datagram sockets.

Probably the place where we see Unix domain sockets the most is docker. As a container technology, docker requires fast data transfer and information exchange with physical machines. In general UDS files end with .socket, we can find these files by using the following command under the /var/run directory.

1
find . -name "*.sock"

If you have docker running, you will see output similar to the following.

1
2
3
./docker.sock
./docker/libnetwork/6d66a24bfbbfa231a668da4f1ed543844a0514e4db3a1f7d8001a04a817b91fb.sock
./docker/libcontainerd/docker-containerd.sock

You can see that docker is communicating through the 3 sock files above.

Use socat to create Unix Domain Sockets

socat is a universal tool that can create not only tcp listening servers, but also udp listening servers, and of course for UDS. Let’s look at the parameters needed to create a UDS server using socat.

1
2
unix-listen:<filename>    groups=FD,SOCKET,NAMED,LISTEN,CHILD,RETRY,UNIX
unix-recvfrom:<filename>  groups=FD,SOCKET,NAMED,CHILD,RETRY,UNIX

Here we have to use the unix-listen and unix-recvfrom parameters. unix-listen indicates the creation of a stream-based UDS service, while unix-recvfrom indicates the creation of a datagram-based UDS.

You can see that both parameters need to be followed by a filename, which indicates the address of the UDS socket.

We can use it like this.

1
2
socat unix-listen:/tmp/stream.sock,fork /dev/null&
socat unix-recvfrom:/tmp/datagram.sock,fork /dev/null&

Here we use /tmp/datagram.sock to represent this socket information.

The fork parameter indicates that the program continues to run after receiving the packet, and if fork is not used, then the program will exit automatically.

The socat is supposed to be followed by a bi-address, here we use /dev/null to discard all the incoming information.

After running we may get the following result.

1
2
[1] 27442
[2] 27450

Indicates that the program has been successfully executed, and the pid of the program is returned.

Use the ss command to view Unix domain sockets

Before using the ss command, let’s look at the two files generated using socat.

1
2
srwxrwxr-x   1 flydean flydean    0 Mar  2 21:58 stream.sock
srwxrwxr-x   1 flydean flydean    0 Mar  2 21:59 datagram.sock

You can see the permissions of the two files, rwx we all understand, respectively, read, write and execute permissions. So what is the top s?

The top one indicates the file type, and the s indicates the socket file.

To expand a bit, there are several other options for this position: p, d, l, s, c, b and -:

Where p indicates a named pipe file, d indicates a directory file, l indicates a symbolic link file, - indicates a normal file, s indicates a socket file, c indicates a character device file, and b indicates a block device file.

Next, we use the ss command to check the previously created UDS service.

The following parameters are used here.

1
2
3
   -n, --numeric       don't resolve service names
   -l, --listening     display listening sockets
   -x, --unix          display only Unix domain sockets

Here we need to use the above 3 options, x means display UDS, because it is listening, so use the -l parameter, and finally we want to see the specific number instead of being parsed as a service name, so use the -n parameter here.

We can try to execute the following command.

1
ss -xln

The output will be a lot, and we can grep the sockets we need. This is shown below.

1
2
3
ss -xln | grep tmp
u_str  LISTEN     0      5      /tmp/stream.sock 11881005              * 0                  
u_dgr  UNCONN     0      0      /tmp/datagram.sock 11882190              * 0  

u_str represents a UDS stream socket, while u_dg represents a UDS datagram socket.

We can use the stat command to view the specific information of the socket file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
stat /tmp/stream.sock /tmp/datagram.sock
  File: '/tmp/stream.sock'
  Size: 0               Blocks: 0          IO Block: 4096   socket
Device: fd02h/64770d    Inode: 134386049   Links: 1
Access: (0775/srwxrwxr-x)  Uid: ( 1002/    flydean)   Gid: ( 1002/    flydean)
Access: 2022-03-01 22:33:21.533000000 +0800
Modify: 2022-03-01 22:33:21.533000000 +0800
Change: 2022-03-01 22:33:21.533000000 +0800
 Birth: -
  File: '/tmp/datagram.sock'
  Size: 0               Blocks: 0          IO Block: 4096   socket
Device: fd02h/64770d    Inode: 134386050   Links: 1
Access: (0775/srwxrwxr-x)  Uid: ( 1002/    flydean)   Gid: ( 1002/    flydean)
Access: 2022-03-01 22:33:22.306000000 +0800
Modify: 2022-03-01 22:33:22.306000000 +0800
Change: 2022-03-01 22:33:22.306000000 +0800
 Birth: -

Connecting to Unix domain socket services using nc

nc is a very powerful tool that can connect to UDS in addition to TCP and UDP connections. We need to use to the following parameters.

1
2
3
  -U, --unixsock             Use Unix domain sockets only
  -u, --udp                  Use UDP instead of default TCP
  -z                         Zero-I/O mode, report connection status only

-U indicates that the connection is to a unixsocket. -u indicates that it is a UDP connection.

By default nc uses a TCP connection, so no additional arguments are needed.

Also we establish the connection directly and don’t send any data, so the -z parameter is used here.

First connect to Stream UDS to see.

1
nc -U -z /tmp/stream.sock

If no abnormal data is output, the connection is successful.

Then connect the Datagram UDS again to see.

1
nc -uU -z /tmp/datagram.sock

Again, if there is no abnormal data, it means the socket connection is successful.

Summary

In this chapter we have introduced the meaning of Unix Domain Socket and implemented UDS creation, detection and connection using some tools in unix. Basically the usage of UDS is described.