Networking in C · intermediate · ~20 min
Communicate between processes on the same machine via AF_UNIX sockets.
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.
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.
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.
#include <sys/un.h>
struct sockaddr_un { sa_family_t sun_family; char sun_path[108]; };
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.
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);
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);
unlink the path before bind (EADDRINUSE on second run).ss -xnp lists Unix domain sockets and which process holds each end. lsof /tmp/cplat.sock shows owners.
sun_path is fixed 108 bytes — copy at most sizeof sun_path - 1 bytes and NUL-terminate.
Docker daemon (/var/run/docker.sock), systemd, MySQL, PostgreSQL (local connections), nginx FastCGI.
Berkeley sockets API with a filesystem path. Set permissions on the path; use SO_PEERCRED for auth inside the protocol.