Networking in C · intermediate · ~20 min

Unix domain sockets — local IPC

Communicate between processes on the same machine via AF_UNIX sockets.

Overview

AF_UNIX sockets give you the Berkeley API for local IPC: same socket/bind/listen/accept/connect/read/write calls, but the address is a filesystem path. Faster than localhost TCP, and the OS can identify the peer process.

Why it matters

Every Linux init system, every container runtime, every database that exposes a local-only protocol uses Unix sockets. Permissions on the socket path become your access control.

Core concepts

Stream and datagram. SOCK_STREAM like TCP; SOCK_DGRAM like UDP. Stream is almost always what you want.

Path-based address. sun_path is a fixed 108-byte buffer. The socket appears as a file; chmod/chown apply.

Abstract namespace (Linux-only). Prefix sun_path with a NUL byte to create an abstract socket that lives in kernel space, not on disk. No cleanup needed.

Peer credentials. getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) gives you the peer's uid/gid/pid. Use this for access control inside the protocol.

SCM_RIGHTS. Pass file descriptors between processes by sending them as ancillary data in sendmsg/recvmsg. The systemd socket-activation pattern.

Pentester mindset. Permissions on the socket path control who can connect. World-writable Unix socket = anyone on the box. SO_PEERCRED is your auth knob inside the protocol.

Defensive coding habit. Chmod the socket file 0660 (or 0600). Unlink the path before bind, OR use abstract namespace. Validate SO_PEERCRED on every accept.

Syntax notes

#include <sys/un.h>
struct sockaddr_un { sa_family_t sun_family; char sun_path[108]; };

Lesson

Unix domain sockets (AF_UNIX) speak the same API as TCP, but the endpoint is a filesystem path instead of an IP. Faster, no network roundtrip, and the OS can tell you who's on the other end via SO_PEERCRED.

Code examples

int s = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strcpy(addr.sun_path, "/tmp/cplat.sock");
bind(s, (struct sockaddr *)&addr, sizeof addr);
listen(s, 4);

Line by line

int s = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr = { .sun_family = AF_UNIX };
strncpy(addr.sun_path, "/tmp/cplat.sock", sizeof addr.sun_path - 1);
unlink(addr.sun_path);                              /* stale from prior run */
bind(s, (struct sockaddr *)&addr, sizeof addr);
chmod(addr.sun_path, 0660);                         /* who can connect? */
listen(s, 4);

Common mistakes

  • Forgetting to unlink the path before bind (EADDRINUSE on second run).

Debugging tips

ss -xnp lists Unix domain sockets and which process holds each end. lsof /tmp/cplat.sock shows owners.

Memory safety

sun_path is fixed 108 bytes — copy at most sizeof sun_path - 1 bytes and NUL-terminate.

Real-world uses

Docker daemon (/var/run/docker.sock), systemd, MySQL, PostgreSQL (local connections), nginx FastCGI.

Practice tasks

  1. Build a UNIX echo server in /tmp/echo.sock. 2. Use SO_PEERCRED to print the connecting uid. 3. Reject any peer whose uid != 1000.

Summary

Berkeley sockets API with a filesystem path. Set permissions on the path; use SO_PEERCRED for auth inside the protocol.

Practice with these exercises