Sockets: Internet Domains UNIX Domain vs Internet Domain Sockets – When to Use Which

 

Chapter 59 – Sockets: Internet Domains
Part 3: UNIX Domain vs Internet Domain Sockets – When to Use Which
59.14
Section
AF_UNIX
vs AF_INET
IPC
Choice

Chapter 59 Series
Part 1: gethostbyname() Part 2: getservbyname() ▶ Part 3: UNIX vs Internet Sockets Part 4: Chapter Summary

Key Terms in This Part
AF_UNIX AF_INET UNIX domain socket Internet domain socket IPC file permissions credential passing file descriptor passing authentication performance

The Question: When Two Processes Are on the Same Machine

When you need two processes on different machines to communicate, you have no choice — you must use Internet domain sockets (AF_INET or AF_INET6) with TCP or UDP.

But what if both processes are on the same machine? You then have two options:

CHOICE: Two processes on same machine need to talk
Option A: Internet Domain Socket
AF_INET or AF_INET6
Uses TCP/IP stack
Works over network too
Same API as remote communication
Option B: UNIX Domain Socket
AF_UNIX (or AF_LOCAL)
Uses filesystem path
Same-host only
Extra Linux features

The TLPI text says that writing with only Internet domain sockets is often the simplest approach since it works on both single host and across a network — but there are specific reasons to prefer UNIX domain sockets for local IPC.

How Each Socket Type Works — A Quick Recap
Internet Domain Socket

Address = IP address + port number

Data travels through the TCP/IP networking stack

Even on the same machine, data goes through the loopback interface (127.0.0.1)

Supports stream (TCP) and datagram (UDP)

UNIX Domain Socket

Address = a path in the filesystem (e.g., /tmp/mysock)

Data travels through kernel memory only

No TCP/IP processing overhead

Supports stream and datagram too

Reason 1: Performance

On many Linux and Unix implementations, UNIX domain sockets are faster than Internet domain sockets for local IPC.

Why? With Internet domain sockets on the same machine, data still goes through the full TCP/IP stack — TCP segmentation, IP header processing, checksum calculation, loopback routing — even though the data never leaves the machine. With UNIX domain sockets, the kernel skips all of this and copies data directly through kernel memory.

Data path for local IPC:
Internet Socket (AF_INET) — same machine:

Process A → TCP layer → IP layer → Loopback (127.0.0.1) → IP layer → TCP layer → Process B

(multiple layers, checksums, routing)
UNIX Socket (AF_UNIX) — same machine:

Process A → Kernel buffer → Process B

(direct memory copy, no TCP/IP stack)

Important caveat: Modern Linux kernels have significantly optimized the loopback path. The performance difference may be small for many workloads. Always benchmark your specific use case before making assumptions.

Reason 2: Access Control via File Permissions

This is one of the strongest reasons to use UNIX domain sockets for local IPC. Because a UNIX socket appears as a file in the filesystem, you can use standard file permission bits and ownership to control which users or groups can connect to it.

/* Example: Create a UNIX domain socket accessible only to a specific group */
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define SOCKET_PATH "/var/run/myapp.sock"

int create_restricted_socket(void)
{
    int sfd;
    struct sockaddr_un addr;

    sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("socket");
        return -1;
    }

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);

    unlink(SOCKET_PATH);   /* Remove old socket file if it exists */

    if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror("bind");
        return -1;
    }

    /* Set permissions: owner can read/write, group can read/write, others nothing */
    /* This means only processes running as the socket owner or in its group       */
    /* can connect — a simple form of authentication                               */
    if (chmod(SOCKET_PATH, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1) {
        perror("chmod");
        return -1;
    }

    return sfd;
}
What this gives you: Only a process running as the socket owner or a member of the owner’s group can call connect() to this socket. The kernel enforces this automatically. With Internet domain sockets, you would have to implement your own authentication protocol (tokens, certificates, etc.).

On Linux, the socket file can also be placed in a directory with restricted access, adding another layer of control based on directory permissions.

Reason 3: Passing File Descriptors and Credentials

UNIX domain sockets support two capabilities that are impossible with Internet domain sockets:

File Descriptor Passing

Process A can send an open file descriptor (a socket, a file, a pipe) to Process B over a UNIX socket using sendmsg() with SCM_RIGHTS ancillary data. Process B gets a fully usable copy of the descriptor — it can read or write the file as if it opened it itself.

Real-world use: systemd socket activation, privilege separation daemons.

Credential Passing (SCM_CREDENTIALS)

A process can send its PID, UID, and GID over a UNIX socket using SCM_CREDENTIALS. The receiving process can verify who it is talking to. Unlike Internet sockets where a client can claim any identity, the kernel verifies these credentials — they cannot be faked.

Real-world use: D-Bus, PolicyKit, Xorg authentication.

/* Skeleton: Receiving peer credentials on Linux */
#include <sys/socket.h>
#include <sys/un.h>

int get_peer_credentials(int conn_fd)
{
    struct ucred cred;
    socklen_t len = sizeof(cred);

    /* SO_PEERCRED is a Linux-specific socket option */
    if (getsockopt(conn_fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) {
        perror("getsockopt SO_PEERCRED");
        return -1;
    }

    printf("Peer PID: %ld\n", (long)cred.pid);
    printf("Peer UID: %ld\n", (long)cred.uid);
    printf("Peer GID: %ld\n", (long)cred.gid);

    return 0;
}
Neither of these features (FD passing or credential passing) is available with Internet domain sockets. They are UNIX domain socket-exclusive capabilities.

Full Comparison: UNIX vs Internet Domain Sockets
Feature UNIX Domain (AF_UNIX) Internet Domain (AF_INET/6)
Address Filesystem path (e.g., /tmp/app.sock) IP address + port number
Works across network No — same machine only Yes — any TCP/IP network
Performance (local) Generally faster (no TCP/IP stack) Slightly slower (loopback path)
Access control File permissions + ownership Must implement manually (tokens, TLS)
FD passing Yes (SCM_RIGHTS) No
Credential passing Yes (SCM_CREDENTIALS / SO_PEERCRED) No (client can spoof identity)
Stream sockets Yes (SOCK_STREAM) Yes (TCP)
Datagram sockets Yes (SOCK_DGRAM) Yes (UDP)
Code complexity Slightly more (manage socket file) Simpler (no filesystem cleanup)

Decision Guide: Which Socket Should You Use?
Use Internet Domain Sockets when:

  • Your application must communicate across a network (different machines)
  • You want a single codebase that works for both local and remote communication
  • Simplicity is more important than raw local performance
  • You are building a client-server system that may be distributed in the future
Use UNIX Domain Sockets when:

  • Communication is strictly local (same machine, always)
  • You need fine-grained access control without building an auth protocol
  • You need to pass open file descriptors between processes
  • You need to verify the identity (UID/GID/PID) of the connecting process
  • Maximum local throughput is critical (high-frequency IPC)
Real-world examples using UNIX sockets: systemd, D-Bus, Xorg/Wayland, Docker daemon, MySQL (local connections), PostgreSQL (local connections), Chrome between renderer and browser processes.

Quick Code Comparison: Same Server in Both Domains

The socket API is deliberately similar for both domains. Here is how a server creation looks side-by-side:

/* ====== Internet Domain Server ====== */
int create_inet_server(int port)
{
    int sfd;
    struct sockaddr_in addr;

    sfd = socket(AF_INET, SOCK_STREAM, 0);  /* AF_INET for internet */

    memset(&addr, 0, sizeof(addr));
    addr.sin_family      = AF_INET;
    addr.sin_port        = htons(port);       /* Port number         */
    addr.sin_addr.s_addr = INADDR_ANY;        /* All interfaces      */

    bind(sfd, (struct sockaddr *)&addr, sizeof(addr));
    listen(sfd, 5);
    return sfd;
}

/* ====== UNIX Domain Server ====== */
int create_unix_server(const char *path)
{
    int sfd;
    struct sockaddr_un addr;

    sfd = socket(AF_UNIX, SOCK_STREAM, 0);   /* AF_UNIX for local  */

    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;                /* Address family      */
    strncpy(addr.sun_path, path,              /* Filesystem path     */
            sizeof(addr.sun_path) - 1);

    unlink(path);   /* Clean up old socket file if it exists */
    bind(sfd, (struct sockaddr *)&addr, sizeof(addr));
    listen(sfd, 5);
    return sfd;
}

The key differences are just the address family (AF_INET vs AF_UNIX) and the address structure (sockaddr_in vs sockaddr_un). The bind(), listen(), accept(), read(), write() calls are identical.

One extra step for UNIX sockets: you should call unlink(path) before binding to remove a stale socket file from a previous run.

Interview Questions & Answers
Q1: What is the main structural difference between a UNIX domain socket address and an Internet domain socket address?

A UNIX domain socket uses a filesystem path (stored in struct sockaddr_un, field sun_path) as its address. An Internet domain socket uses an IP address and port number (stored in struct sockaddr_in). UNIX sockets are local-only; Internet sockets can communicate across a network.

Q2: Why are UNIX domain sockets generally faster than Internet domain sockets for local IPC?

Internet domain sockets on the same machine still go through the full TCP/IP networking stack — including TCP segmentation, IP header processing, checksum computation, and routing through the loopback interface. UNIX domain sockets bypass all of this; the kernel copies data directly through shared memory buffers without any network protocol processing.

Q3: How do UNIX domain sockets provide authentication that Internet sockets cannot?

In two ways. First, the socket file appears in the filesystem, so standard Unix file permissions (read/write bits, ownership) control who can connect. Second, on Linux you can use SO_PEERCRED with getsockopt() to retrieve the PID, UID, and GID of the connecting process. These credentials are provided by the kernel and cannot be faked. Internet domain sockets have no equivalent — a client can connect from any IP and claim any identity.

Q4: What is SCM_RIGHTS and why is it only available with UNIX domain sockets?

SCM_RIGHTS is a type of ancillary (control) message sent with sendmsg() over a UNIX domain socket that transfers open file descriptors from one process to another. It works because the kernel can look up file descriptors in the sender’s file descriptor table and duplicate them into the receiver’s table — a kernel-internal operation. This is impossible with Internet domain sockets because the remote host has its own separate kernel and file descriptor table.

Q5: Name three well-known Linux daemons or systems that use UNIX domain sockets and why.

(1) D-Bus — uses UNIX sockets for inter-process messaging on the desktop; needs credential passing to enforce policy decisions.
(2) systemd — uses UNIX sockets for socket activation (passing pre-opened sockets to services via FD passing with SCM_RIGHTS).
(3) MySQL / PostgreSQL — create a UNIX socket for local client connections because it is faster and more secure than loopback TCP for clients on the same machine.

Q6: What extra cleanup step is required for UNIX domain sockets that is not needed for Internet sockets?

A UNIX domain socket creates a socket file in the filesystem when bind() is called. If the server crashes or exits without calling unlink(), this file remains. The next time the server starts and tries to call bind() with the same path, it will fail with EADDRINUSE. The fix is to call unlink(path) before bind() to remove any stale socket file. Internet domain sockets do not create filesystem entries so they do not have this problem.

Next: Chapter 59 Summary & Exercises

Review everything covered in Chapter 59 — Internet domain sockets, DNS, data representation, and more.

← Part 2 Part 4: Summary & Exercises →

Leave a Reply

Your email address will not be published. Required fields are marked *