This document summarizes the std.crypto module in Zig's standard library, focusing on common cryptographic operations and how to use them. The library provides a wide range of cryptographic primitives and high-level APIs designed for security and ease of use.
- Hashing: Creating fixed-size digests of data (e.g., for integrity checks). See
`crypto.hash`. - Authentication (MAC): Creating tags to verify data integrity and authenticity using a secret key. See
`crypto.auth`. - Password Hashing: Securely hashing passwords for storage, designed to be slow and resource-intensive. See
`crypto.pwhash`. - Encryption (AEAD): Encrypting data while also providing integrity and authenticity verification (Authenticated Encryption with Associated Data). Preferred over raw stream/block ciphers. See
`crypto.aead`. - Signatures: Proving data origin and integrity using public-key cryptography. See
`crypto.sign`. - Key Exchange (DH): Securely establishing a shared secret between parties over an insecure channel. See
`crypto.dh`. - Key Derivation (KDF): Deriving cryptographic keys from a master secret or password. See
`crypto.kdf`. - Key Encapsulation (KEM): Public-key mechanism to securely transport a symmetric key. See
`crypto.kem`. - Randomness: Secure random number generation via
`crypto.random`. - Utilities: Secure memory zeroing (
`crypto.secureZero`) and constant-time operations (`crypto.timing_safe`). - High-Level API: Simplified NaCl/libsodium-inspired APIs. See
`crypto.nacl`.
Computes a fixed-size digest (hash) of input data. Useful for integrity checks, data identification, etc.
- Common Algorithms:
`crypto.hash.Sha256`,`crypto.hash.Sha384`,`crypto.hash.Sha512`: SHA-2 family.`crypto.hash.Sha3_256`,`crypto.hash.Sha3_512`: SHA-3 family.`crypto.hash.Blake3`: Modern, very fast hash function.`crypto.hash.Md5`,`crypto.hash.Sha1`: Deprecated and insecure for cryptographic use.
- Usage Patterns:
- One-shot: Easiest for complete data via the
`hash`function. - Incremental: Useful for streaming data via
`init`,`update`, and`final`methods.
- One-shot: Easiest for complete data via the
- Example (One-shot SHA-256):
const crypto = @import("std").crypto; const message = "hello world"; var digest: [crypto.hash.sha2.Sha256.digest_length]u8 = undefined; crypto.hash.sha2.Sha256.hash(message, &digest, .{});
- Example (Incremental Blake3):
const crypto = @import("std").crypto; const message_part1 = "hello "; const message_part2 = "world"; var hasher = crypto.hash.Blake3.init(.{}); // options can include a key for keyed hashing hasher.update(message_part1); hasher.update(message_part2); var digest: [crypto.hash.Blake3.digest_length]u8 = undefined; hasher.final(&digest);
Creates a Message Authentication Code (tag) using a secret key to verify data integrity and authenticity.
- Common Algorithms:
`crypto.auth.hmac.sha2.HmacSha256`: HMAC using SHA-256. Widely used.`crypto.auth.siphash`: Fast MAC, often used for hash tables (requires key).`crypto.auth.aegis`: High-performance MACs.`crypto.onetimeauth.Poly1305`: Fast one-time MAC (key must never be reused). Often used with stream ciphers in AEAD modes.
- Usage: Similar
`init`/`update`/`final`or one-shot (`mac`) patterns as hashing, but requires a secret`key`. - Key Constants:
`key_length`,`mac_length`(tag length). - Verification: Compare the computed tag with the received tag using
`crypto.timing_safe.eql`. Failure implies tampering or wrong key. Related error:`crypto.errors.AuthenticationError`. - Example (HMAC-SHA256):
const crypto = @import("std").crypto; const message = "authenticate me"; var key: [crypto.auth.hmac.sha2.HmacSha256.key_length]u8 = undefined; // Fill 'key' with a secure random key crypto.random.bytes(&key); var tag: [crypto.auth.hmac.sha2.HmacSha256.mac_length]u8 = undefined; // One-shot crypto.auth.hmac.sha2.HmacSha256.create(&tag, message, &key);
Securely hashes passwords for storage. Designed to be slow and memory/CPU intensive to resist brute-force attacks.
- Algorithms:
`crypto.pwhash.argon2`: Modern, recommended algorithm (Argon2id, Argon2i, Argon2d modes). Requires an`Allocator`.`crypto.pwhash.bcrypt`: Widely used, robust algorithm.`crypto.pwhash.scrypt`: Memory-hard algorithm. Requires an`Allocator`.
- Key Functions:
`strHash`: Computes the hash and encodes it into a standard string format (e.g., PHC format) including algorithm, parameters, salt, and hash.`strVerify`: Verifies a password against a previously generated hash string.
- Parameters: Algorithms require parameters like rounds/iterations (
`time_cost`), memory cost (`memory_kib`), parallelism (`parallelism`), and a unique random`salt`per password. Use predefined constants (e.g.,`argon2.Params.owasp_2id`) or tune carefully. - Error:
`crypto.errors.PasswordVerificationError`on mismatch during`strVerify`. - Example (Argon2id):
const crypto = @import("std").crypto; const std = @import("std"); const allocator = std.heap.page_allocator; const password = "correct horse battery staple"; var output_buf: [200]u8 = undefined; const params = crypto.pwhash.argon2.Params.owasp_2id; const hash_options = crypto.pwhash.argon2.HashOptions{ .allocator = allocator, .params = params, .mode = .argon2id, }; const hash_str = try crypto.pwhash.argon2.strHash(password, hash_options, &output_buf); std.debug.print("Hash string: {s}\n", .{hash_str}); // Store this string // Verification const stored_hash_str = hash_str; // Retrieve stored hash string const verify_options = crypto.pwhash.argon2.VerifyOptions{ .allocator = allocator }; try crypto.pwhash.argon2.strVerify(stored_hash_str, password, verify_options); std.debug.print("Password verified!\n", .{}); // Example with wrong password (will error) const wrong_password = "wrong password"; _ = try crypto.pwhash.argon2.strVerify(stored_hash_str, wrong_password, verify_options);
Encrypts data and provides integrity/authenticity. This is generally preferred over using stream/block ciphers directly.
- Common Algorithms:
`crypto.aead.chacha_poly.ChaCha20Poly1305`: ChaCha20 stream cipher + Poly1305 MAC. Widely used, good performance.`XChaCha20Poly1305`supports larger nonces.`crypto.aead.aes_gcm.Aes128Gcm`,`crypto.aead.aes_gcm.Aes256Gcm`: AES in GCM mode. Hardware accelerated on many platforms.`crypto.aead.aegis`: High-performance AEAD algorithms.
- Key Parameters:
`key`: Secret key (size:`key_length`).`nonce`: Number used once per key (size:`nonce_length`). Critical for security. Must be unique for each encryption with the same key.`plaintext`: Data to encrypt.`ciphertext`: Encrypted data output.`associated_data`(AD): Optional data that is authenticated but not encrypted (e.g., headers).`tag`: Authentication tag output (during encryption) or input (during decryption) (size:`tag_length`).
- Key Functions:
`encrypt`,`decrypt`. - Error:
`crypto.errors.AuthenticationError`if decryption fails (tag mismatch or ciphertext corruption). - Example (ChaCha20Poly1305):
const crypto = @import("std").crypto; const aead = crypto.aead.chacha_poly.ChaCha20Poly1305; var key: [aead.key_length]u8 = undefined; crypto.random.bytes(&key); var nonce: [aead.nonce_length]u8 = undefined; crypto.random.bytes(&nonce); // Nonce MUST be unique per key const plaintext = "secret message"; const associated_data = "metadata"; var ciphertext: [plaintext.len]u8 = undefined; var tag: [aead.tag_length]u8 = undefined; // Encryption aead.encrypt(&ciphertext, &tag, plaintext, associated_data, nonce, key); // Transmit ciphertext, tag, nonce, associated_data // Decryption var decrypted_plaintext: [plaintext.len]u8 = undefined; aead.decrypt(&decrypted_plaintext, &ciphertext, tag, associated_data, nonce, key) catch |err| { // If err == error.AuthenticationFailed, the data is invalid/tampered! return err; }; // 'decrypted_plaintext' matches 'plaintext' if successful
Verify data integrity and origin using public-key cryptography. A private key is used to sign, and the corresponding public key is used to verify.
- Algorithms:
`crypto.sign.Ed25519`: Modern, fast signature scheme based on Edwards curves. Recommended.`crypto.sign.ecdsa`: ECDSA implementation (e.g.,`EcdsaP256Sha256`).
- Key Concepts:
`KeyPair`: Holds both`public_key`and`secret_key`.`SecretKey`: The private key used for signing.`PublicKey`: The public key used for verification.`Signature`: The signature data produced.
- Key Functions:
`KeyPair.generate()`: Create a new random key pair.`KeyPair.generateDeterministic(seed)`: Create a key pair from a seed.`keypair.sign(message, noise)`: Create a signature.`noise`can be`null`for deterministic signatures or random data for resilience against some attacks.`signature.verify(message, public_key)`: Verify a signature.
- Error:
`crypto.errors.SignatureVerificationError`if verification fails. - Example (Ed25519):
const crypto = @import("std").crypto; const Ed25519 = crypto.sign.Ed25519; // Key Generation const key_pair = Ed25519.KeyPair.generate(); const public_key = key_pair.public_key; // Share this public key // Signing const message = "message to sign"; const signature = try key_pair.sign(message, null); // Deterministic signature // Verification (by someone with the public key) try signature.verify(message, public_key); // Verification with wrong message (will error) const wrong_message = "different message"; _ = try signature.verify(wrong_message, public_key);
Allows two parties to establish a shared secret over an insecure channel using public-key cryptography.
- Algorithm:
`crypto.dh.X25519`: Based on Curve25519. Modern and fast.
- Key Concepts: Each party generates an X25519
`KeyPair`. They exchange public keys (`public_key`). Each party computes the shared secret using their own`secret_key`and the other party's`public_key`. - Key Function:
`crypto.dh.X25519.scalarmult(my_secret_key, their_public_key)`computes the shared secret. - Output: The raw output of
`scalarmult`should typically be hashed or used as input to a KDF (like HKDF) before being used as a symmetric key. - Example (X25519):
const std = @import("std"); const crypto = std.crypto; const X25519 = crypto.dh.X25519; // Alice generates keys const alice_key_pair = X25519.KeyPair.generate(); // Bob generates keys const bob_key_pair = X25519.KeyPair.generate(); // They exchange public keys (alice_key_pair.public_key, bob_key_pair.public_key) // Alice computes shared secret const alice_shared_secret = try X25519.scalarmult(alice_key_pair.secret_key, bob_key_pair.public_key); // Bob computes shared secret const bob_shared_secret = try X25519.scalarmult(bob_key_pair.secret_key, alice_key_pair.public_key); // alice_shared_secret and bob_shared_secret will be identical std.debug.assert(std.mem.eql(u8, &alice_shared_secret, &bob_shared_secret)); // Use the shared secret (e.g., derive keys using HKDF) var derived_key: [32]u8 = undefined; crypto.kdf.hkdf.HkdfSha256.expand(&derived_key, "context", alice_shared_secret);
Derives one or more cryptographic keys from a master secret or password.
- Algorithm:
`crypto.kdf.hkdf`: HKDF (HMAC-based KDF). Standardized and widely used. Requires a`Hmac`type (e.g.,`HmacSha256`).
- Key Function:
`Hkdf(HmacType).kdf(input_key_material, salt, info, output_key_buffer)``input_key_material`: The master secret (IKM).`salt`: Optional non-secret random value. Recommended.`info`: Optional context/application-specific string.`output_key_buffer`: Buffer to store derived key(s). Length determines output size.
- Example (HKDF-SHA256):
const crypto = @import("std").crypto; const HkdfSha256 = crypto.kdf.hkdf.HkdfSha256; const ikm = "initial keying material"; // e.g., output from DH or a master secret const salt = "random salt"; // Can be empty, but recommended const info = "my application context"; // Context helps separate keys // Step 1: Extract to get the PRK const prk = HkdfSha256.extract(salt, ikm); // Step 2: Expand to get the derived key var derived_key1: [32]u8 = undefined; HkdfSha256.expand(&derived_key1, info, prk); // For longer key var longer_derived_key: [64]u8 = undefined; HkdfSha256.expand(&longer_derived_key, info, prk);
Provides simplified APIs inspired by NaCl/libsodium for common cryptographic tasks, combining primitives.
`crypto.nacl.SecretBox`: Symmetric authenticated encryption (like AEAD, uses XSalsa20Poly1305). Requires a shared secret key.`seal(ciphertext, plaintext, nonce, key)``open(plaintext, ciphertext, nonce, key)`
`crypto.nacl.Box`: Public-key authenticated encryption (uses X25519 + XSalsa20Poly1305). Requires sender's secret key and recipient's public key.`seal(ciphertext, plaintext, nonce, recipient_public_key, sender_secret_key)``open(plaintext, ciphertext, nonce, sender_public_key, recipient_secret_key)`
`crypto.nacl.SealedBox`: Anonymous public-key authenticated encryption. Only recipient's public key is needed to encrypt; sender identity is not revealed/authenticated by the primitive itself.`seal(ciphertext, plaintext, recipient_public_key)``open(plaintext, ciphertext, recipient_keypair)`
`crypto.ecc`: Low-level elliptic curve arithmetic (Curve25519, Edwards25519, P-256, Secp256k1). Used internally by`sign`,`dh`. Generally not used directly unless implementing custom protocols.`crypto.stream`: Stream ciphers like`ChaCha20`,`Salsa20`. AEAD modes are strongly preferred as stream ciphers alone provide no integrity or authentication.`crypto.random`: Provides access to the system's cryptographically secure random number generator (interface compatible with`std.rand.Random`). Use`crypto.random.bytes(slice)`to fill a slice with random bytes.`crypto.secureZero(T, slice)`: Zeros memory securely, preventing compiler optimizations that might remove the zeroing operation (important for secrets).`crypto.timing_safe`: Functions for constant-time comparison (`eql`,`compare`), addition (`add`), subtraction (`sub`). Crucial for comparing secret data like MAC tags or passwords without leaking timing information.`crypto.asn1`: ASN.1 (especially DER) encoding/decoding, used primarily for X.509 certificates and keys.`crypto.Certificate`: Parsing and verification of X.509 certificates. Includes`Bundle`for managing trusted Certificate Authorities (CAs).`crypto.tls`: Underlying types and logic for implementing TLS clients/servers (complex).`Client`provides a high-level stream interface.`crypto.errors`: Defines common error sets like`AuthenticationError`,`SignatureVerificationError`,`PasswordVerificationError`,`EncodingError`.
This document summarizes the std.net module in Zig's standard library. It provides cross-platform abstractions for networking tasks, primarily focusing on TCP/IP (v4 and v6) and Unix domain sockets (where available).
- Represents a network address, capable of holding various address types.
- Key fields:
`in`(`Ip4Address`),`in6`(`Ip6Address`),`un`(`posix.sockaddr.un`for Unix sockets, ifhas_unix_socketsis true),`any`(raw`posix.sockaddr`). - Used to specify addresses for connecting, listening, or representing remote endpoints. Created via parsing functions or initializers like
`initIp4`/`initIp6`.
- Represent specific IPv4 and IPv6 socket addresses, including IP and port.
- Contain the underlying platform-specific address structures (e.g.,
`posix.sockaddr.in`). - Can be created via parsing (e.g.,
`Address.parseIp`) or initialization (e.g.,`Address.initIp4`).
- Represents an active, connected network stream (like a TCP connection or a connected Unix socket).
- Holds the underlying socket handle (
`handle: posix.socket_t`). - Provides methods for reading (
`read`,`readAll`, etc.), writing (`write`,`writeAll`, etc.), and closing (`close`). - Obtained via client connection functions (e.g.,
`tcpConnectToHost`) or server accept (`Server.accept`). - Provides
`reader()`and`writer()`methods returning`std.io.Reader`and`std.io.Writer`interfaces.
- Represents a listening network server socket.
- Fields:
`listen_address`(`Address`),`stream`(`std.net.Stream`representing the listening socket itself). - Created by calling
`Address.listen()`. - Primary method:
`accept()`to wait for and accept incoming connections. - Must be cleaned up with
`deinit()`.
- Holds a list of resolved
`Address`objects, typically the result of a DNS lookup via`getAddressList`. - Fields:
`addrs`(`[]Address`),`canon_name`(`?[]u8`). - Managed by an
`std.mem.Allocator`(specifically`ArenaAllocator`internally). - Must be cleaned up with
`deinit()`to free the allocator's memory.
- Configuration for
`Address.listen()`. - Fields:
`kernel_backlog`(connection queue size),`reuse_address`,`reuse_port`(socket option flags),`force_nonblocking`.
- Returned by
`Server.accept()`. - Represents an accepted client connection.
- Fields:
`stream`(`std.net.Stream`for I/O),`address`(`Address`of the connected client).
`Address.initIp4(addr: [4]u8, port: u16) Address`: Creates an IPv4`Address`.`Address.initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address`: Creates an IPv6`Address`.`Address.initUnix(path: []const u8) !Address`: Creates a Unix domain socket`Address`(if supported).`Address.parseIp(name: []const u8, port: u16) !Address`: Parses an IP address string (v4 or v6) into an`Address`. Use`resolveIp`for better IPv6 link-local handling.`Address.parseIp4(buf: []const u8, port: u16) IPv4ParseError!Address`: Parses only IPv4 strings.`Address.parseIp6(buf: []const u8, port: u16) IPv6ParseError!Address`: Parses only IPv6 strings (numeric scope ID).`Address.resolveIp(name: []const u8, port: u16) !Address`: Resolves an IP string (v4/v6), handling IPv6 scope IDs/interface names. Recommended over`parseIp`for user input.`Address.resolveIp6(buf: []const u8, port: u16) IPv6ResolveError!Address`: Resolves IPv6 strings, handling interface names for scope IDs.
`getAddressList(allocator: mem.Allocator, name: []const u8, port: u16) GetAddressListError!*AddressList`: Performs DNS lookup for a hostname and port, returning a list of possible addresses. Result must be`deinit()`ed.`isValidHostName(hostname: []const u8) bool`: Checks if a string has valid hostname syntax.
`tcpConnectToHost(allocator: mem.Allocator, name: []const u8, port: u16) TcpConnectToHostError!Stream`: Common Case. Resolves hostname (name) via DNS, tries connecting to the resolved addresses, and returns a`Stream`on success. Uses the provided allocator temporarily for resolution.`tcpConnectToAddress(address: Address) TcpConnectToAddressError!Stream`: Connects directly to a pre-defined`Address`(IPv4 or IPv6). Returns a`Stream`.`connectUnixSocket(path: []const u8) !Stream`: Connects to a Unix domain socket at the given`path`. Returns a`Stream`. Only available ifhas_unix_socketsis true.
`Address.listen(options: ListenOptions) ListenError!Server`: Creates a listening`Server`bound to the`Address`.`Server.accept() AcceptError!Connection`: Blocks until a client connects, then returns a`Connection`containing the client's`Stream`and`Address`.`Server.deinit()`: Closes the listening socket and cleans up the`Server`struct.
`Stream.read(buffer: []u8) ReadError!usize`: Reads up to`buffer.len`bytes. Returns bytes read. Can return 0 without error (e.g., on non-blocking sockets).`Stream.readAll(buffer: []u8) ReadError!void`: Reads exactly`buffer.len`bytes, unless EOF (`error.EndOfStream`) is reached first.`Stream.readAtLeast(buffer: []u8, len: usize) ReadError!usize`: Reads at least`len`bytes, unless EOF is reached first. Returns bytes read.`Stream.write(buffer: []const u8) WriteError!usize`: Writes up to`buffer.len`bytes. Returns bytes written.`Stream.writeAll(bytes: []const u8) WriteError!void`: Writes exactly`bytes.len`bytes. Loops internally until all bytes are written or an error occurs.`Stream.readv(...)`,`Stream.writev(...)`,`Stream.writevAll(...)`: Scatter/gather I/O using`iovec`arrays.`Stream.close()`: Closes the connection/stream.`Stream.reader() -> std.io.Reader`,`Stream.writer() -> std.io.Writer`: Get standard I/O interfaces.
`Address.getPort() u16`: Gets the port from an IP`Address`(asserts it's IP).`Address.setPort(port: u16) void`: Sets the port on an IP`Address`(asserts it's IP).`Address.format(self, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void`: Formats the address to a stream for printing.`Address.eql(a: Address, b: Address) bool`: Checks if two addresses are equal.`Address.getOsSockLen() posix.socklen_t`: Gets the size needed for OS socket calls.
- Functions return errors combined into specific sets (e.g.,
`TcpConnectToHostError`,`ReadError`,`WriteError`,`ListenError`,`AcceptError`,`GetAddressListError`). - Common errors include:
`error.ConnectionRefused`,`error.NetworkUnreachable`,`error.HostUnreachable`,`error.AccessDenied`,`error.AddressInUse`,`error.OutOfMemory`,`error.Unexpected`(OS error),`error.EndOfStream`,`error.ConnectionResetByPeer`,`error.BlockingOperationWouldBlock`(or`error.WouldBlock`).
const std = @import("std");
const net = std.net;
const mem = std.mem;
const print = std.debug.print;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const host = "example.org";
const port = 80;
print("Connecting to {s}:{d}...\n", .{ host, port });
// Resolve DNS and connect
var stream = try net.tcpConnectToHost(allocator, host, port);
defer stream.close();
print("Connected!\n", .{});
// Send an HTTP GET request
const request = "GET / HTTP/1.1\r\nHost: example.org\r\nConnection: close\r\n\r\n";
try stream.writer().writeAll(request); // Using writer interface
print("Sent request.\n", .{});
// Read the response
var buffer: [1024]u8 = undefined;
while (true) {
const bytes_read = try stream.reader().read(&buffer);
if (bytes_read == 0) break; // EOF or non-blocking read empty
// Process the received data (just print it here)
print("{s}", .{buffer[0..bytes_read]});
}
print("\nConnection closed by peer.\n", .{});
}const std = @import("std");
const net = std.net;
const print = std.debug.print;
pub fn main() !void {
// Parse listen address (listen on all IPv4 interfaces, port 8080)
var listen_address = try net.Address.parseIp4("0.0.0.0", 8080);
// Start listening
var server = try listen_address.listen(.{ .reuse_address = true });
defer server.deinit(); // Ensure server socket is closed
print("Server listening on {}\n", .{server.listen_address});
while (true) {
// Accept a new connection
var connection = try server.accept();
// Note: In a real server, you'd likely spawn a thread or use async I/O here
// to handle multiple clients concurrently. This example handles one at a time.
defer connection.stream.close(); // Close client connection when done
print("Accepted connection from {}\n", .{connection.address});
// Simple echo handling
var buffer: [1024]u8 = undefined;
while (true) {
const bytes_read = try connection.stream.read(&buffer);
if (bytes_read == 0) break; // Should generally be caught by EndOfStream
// Echo back the received data using the writer interface
try connection.stream.writer().writeAll(buffer[0..bytes_read]);
}
print("Connection closed from {}\n", .{connection.address});
}
}This document summarizes the std.posix module in Zig, which provides a low-level API layer mirroring POSIX (and sometimes related, like Linux or Windows) system calls.
- Provides a cross-platform (primarily targeting POSIX-compliant systems like Linux, macOS, BSDs, and also WASI, with partial Windows support) interface to operating system functionalities.
- Lower-level and less abstract than modules like
std.fsorstd.process. Often maps directly to libc functions or syscalls. - Use this when you need fine-grained control, access to specific OS features not exposed by higher-level APIs, or maximum performance by avoiding higher-level abstractions.
- Be aware that portability might be limited for non-standard features, and OS-specific code might still be needed. Check function documentation for platform support.
Functions generally correspond closely to their C counterparts (e.g., `posix.open` maps to `open(2)`). Names and parameters often match the underlying syscall.
Many operations revolve around file descriptors (`posix.fd_t`), which are opaque handles (usually integers) representing open files, sockets, pipes, etc. Constants `posix.STDIN_FILENO`, `posix.STDOUT_FILENO`, `posix.STDERR_FILENO` are predefined.
- Functions typically return
`Error!ReturnType`(e.g.,`OpenError!fd_t`). - Each function usually has a specific
ErrorSet(e.g.,`OpenError`,`BindError`) containing possible OS errors (mapped fromerrno). `error.Unexpected`indicates an OS error code not explicitly mapped by the Zig standard library for that function.- Use
`try`or`catch`for error handling. Typed errors are preferred over checkingerrno.
- Paths are typically passed as
`[]const u8`(slices). Encoding depends on the platform (often UTF-8, but can be opaque bytes on Unix, WTF-8 on Windows). ZSuffix: Functions ending inZ(e.g.,`openZ`,`accessZ`) expect null-terminated C strings (`[*:0]const u8`).WSuffix: Functions ending inW(e.g.,`mkdirW`,`renameW`) are Windows-specific and expect WTF-16 Little Endian encoded paths (`[]const u16`or`[*:0]const u16`). Check function docs for encoding details.atSuffix: Functions ending inat(e.g.,`openat`,`fstatat`) perform path-based operations relative to a directory file descriptor (`dirfd`), avoiding races associated with the process's current working directory. Use`posix.AT.FDCWD`for the CWD.
Operations often take flags defined as constants within `posix` (or nested structs/enums like `posix.O`, `posix.PROT`, `posix.MAP`). These map directly to POSIX constants (e.g., `posix.O.RDONLY`).
`open(path, flags, mode)`/`openZ`: Open/create a file. Returns`fd_t`.`openat(dirfd, path, flags, mode)`/`openatZ`: Open/create relative to`dirfd`.`close(fd)`: Close a file descriptor.
`read(fd, buf)`: Read bytes into buffer. Returns bytes read (`usize`).`write(fd, buf)`: Write bytes from buffer. Returns bytes written (`usize`).`pread(fd, buf, offset)`: Read at specific offset.`pwrite(fd, buf, offset)`: Write at specific offset.`readv(fd, iov)`/`writev(fd, iov)`: Scatter/gather read/write using`posix.iovec`.
`lseek(fd, offset, whence)`: General seek function (use`posix.SEEK.SET`,`posix.SEEK.CUR`,`posix.SEEK.END`for`whence`). Returns new offset.- Convenience wrappers:
`lseek_SET`,`lseek_CUR`,`lseek_END`,`lseek_CUR_get`.
`fstat(fd)`: Get status (metadata,`posix.Stat`) of an open file.`fstatat(dirfd, path, flags)`/`fstatatZ`: Get status of a file by path relative to`dirfd`(flags like`posix.AT.SYMLINK_NOFOLLOW`).`access(path, mode)`/`accessZ`: Check permissions (`posix.R_OK`,`W_OK`,`X_OK`,`F_OK`).`faccessat(dirfd, path, mode, flags)`/`faccessatZ`: Check permissions relative to`dirfd`.
`ftruncate(fd, length)`: Change file size.`fchmod(fd, mode)`: Change permissions (mode bits) of an open file.`fchmodat(dirfd, path, mode, flags)`: Change permissions by path relative to`dirfd`.`fchown(fd, owner, group)`: Change ownership (UID/GID) of an open file.`fsync(fd)`/`fdatasync(fd)`: Flush changes to disk (data+metadata / data only).`sync()`/`syncfs(fd)`: Flush all filesystem buffers / buffers for the filesystem containingfd.
`mkdir(path, mode)`/`mkdirZ`/`mkdirW`: Create directory.`mkdirat(dirfd, path, mode)`/`mkdiratZ`/`mkdiratW`: Create directory relative to`dirfd`.`rmdir(path)`/`rmdirZ`/`rmdirW`: Remove empty directory.`unlink(path)`/`unlinkZ`/`unlinkW`: Remove file link (name).`unlinkat(dirfd, path, flags)`/`unlinkatZ`/`unlinkatW`: Remove file/directory (if`AT.REMOVEDIR`) relative to`dirfd`.`rename(oldpath, newpath)`/`renameZ`/`renameW`: Rename/move file.`renameat(olddirfd, oldpath, newdirfd, newpath)`/`renameatZ`/`renameatW`: Rename/move relative to directory fds.`link(oldpath, newpath)`/`linkZ`: Create hard link.`linkat(olddirfd, oldpath, newdirfd, newpath, flags)`/`linkatZ`: Create hard link relative to directory fds.`symlink(target, linkpath)`/`symlinkZ`: Create symbolic link.`symlinkat(target, newdirfd, linkpath)`/`symlinkatZ`: Create symbolic link relative to`newdirfd`.`readlink(path, buf)`/`readlinkZ`/`readlinkW`: Read value of symbolic link. Returns length of link path.`readlinkat(dirfd, path, buf)`/`readlinkatZ`/`readlinkatW`: Read symlink relative to`dirfd`.
`chdir(path)`/`chdirZ`/`chdirW`: Change current working directory.`fchdir(dirfd)`: Change CWD to directory represented by`dirfd`.`getcwd(buf)`: Get current working directory path. Returns slice ofbuf.
// Example: Read from a file using posix
const std = @import("std");
const posix = std.posix;
pub fn main() !void {
const fd = try posix.open("my_file.txt", .{}, 0);
defer posix.close(fd);
var buf: [1024]u8 = undefined;
const bytes_read = try posix.read(fd, &buf);
std.debug.print("Read {d} bytes: {s}\n", .{bytes_read, buf[0..bytes_read]});
}`fork()`: Create a child process (Unix-like). Returnspid_t(0 in child, child's PID in parent).`execveZ(path, argv, envp)`: Replace current process image with a new one (no PATH search).argvandenvpare null-terminated arrays of C strings.`execvpeZ(file, argv, envp)`: Replace process image, searching PATH forfile.- (Windows uses separate
CreateProcessfunctions, often viastd.process).
`exit(status)`: Terminate process normally (never returns).`abort()`: Terminate process abnormally (often raises`SIGABRT`).`waitpid(pid, flags)`/`wait4(pid, flags, rusage)`: Wait for child process state changes. Returns info like PID and exit status.pidcan be specific PID, -1 (any child), 0 (any child in process group), etc. Flags like`WNOHANG`.
`kill(pid, sig)`: Send a signal (`posix.SIG.*`) to a process or process group.`raise(sig)`: Send a signal to the calling thread/process.`getpid()`: Get current process ID (`pid_t`).`getppid()`: Get parent process ID.`setpgid(pid, pgid)`: Set process group ID.`getuid()`/`geteuid()`/`setuid()`/`seteuid()`: User ID management (`uid_t`).`getgid()`/`getegid()`/`setgid()`/`setegid()`: Group ID management (`gid_t`).
`getrlimit(resource)`/`setrlimit(resource, limits)`: Get/Set resource limits (`posix.RLIMIT.*`, uses`posix.rlimit`struct).`getrusage(who)`: Get resource usage statistics (`posix.rusage`struct,`who`is e.g.`RUSAGE.SELF`).
// Example: Simple fork/exec (Conceptual, needs robust error handling)
const std = @import("std");
const posix = std.posix;
pub fn main() !void {
const pid = try posix.fork();
if (pid == 0) {
// Child process
const argv = [_:null]?[*:0]const u8{ "ls", "-l" };
const envp = [_:null]?[*:0]const u8{null}; // Inherit environment
switch (posix.execvpeZ("ls", &argv, &envp)) {
else => unreachable,
}
} else {
const res = posix.waitpid(pid, 0);
// Extract the actual exit status using WEXITSTATUS if the process exited normally
const status = if (posix.W.IFEXITED(res.status))
posix.W.EXITSTATUS(res.status)
else
res.status;
std.debug.print("Child {d} exited with status {d}\n", .{ res.pid, status });
}
}Note: std.net provides higher-level abstractions over these.
`socket(domain, type, protocol)`: Create a socket (e.g.,`posix.AF.INET`,`posix.SOCK.STREAM`). Returns`fd_t`.`bind(sockfd, addr, addrlen)`: Assign address (`posix.sockaddr`) to socket.`listen(sockfd, backlog)`: Mark socket to accept connections.`accept(sockfd, addr, addrlen, flags)`/`accept4`: Accept incoming connection. Fillsaddrand returns new client`fd_t`.`connect(sockfd, addr, addrlen)`: Initiate connection on a socket.
`send(sockfd, buf, flags)`/`sendto(sockfd, buf, flags, dest_addr, addrlen)`/`sendmsg(sockfd, msghdr, flags)`: Send data.`recv(sockfd, buf, flags)`/`recvfrom(sockfd, buf, flags, src_addr, addrlen)`/`recvmsg(sockfd, msghdr, flags)`: Receive data.
`shutdown(sockfd, how)`: Disable send/receive on socket (`posix.SHUT.RD`,`WR`,`RDWR`).`getsockopt(sockfd, level, optname, optval_buf)`/`setsockopt(sockfd, level, optname, optval_buf)`: Get/Set socket options (e.g.,`posix.SOL.SOCKET`,`posix.SO.REUSEADDR`).`getsockname(sockfd, addr, addrlen)`: Get local socket address.`getpeername(sockfd, addr, addrlen)`: Get remote peer address.
`gethostname(buf)`: Get standard hostname.- (DNS/Address resolution often uses higher-level functions like
getaddrinfoorstd.net.getAddressList).
// Import the standard library namespace
const std = @import("std");
// Import the expect function for writing tests from the standard testing module
const expect = std.testing.expect;
// Import the networking module from the standard library
const net = std.net;
// Import the operating system specific functions module (like socket operations)
const os = std.os;
// Test block to verify the basic functionality of Socket.init
test "create a socket" {
// Attempt to initialize a Socket for localhost (127.0.0.1) on port 3000.
// 'try' will propagate any error returned by Socket.init.
const socket = try Socket.init("127.0.0.1", 3000);
// Ensure that the underlying socket descriptor created within the Socket struct
// is of the correct type, std.posix.socket_t (which is typically an integer file descriptor).
// 'try' is used here because 'expect' can potentially fail (though unlikely in this specific type check).
try expect(@TypeOf(socket.socket) == std.posix.socket_t);
// Note: The socket created here is not closed because it goes out of scope
// when the test finishes. In a real application, you'd need explicit cleanup.
// However, test cleanup is often handled by the OS or test runner implicitly.
}
// Define a struct named 'Socket' to encapsulate socket-related data and operations.
const Socket = struct {
// Stores the parsed network address (IP and port) associated with this socket.
address: std.net.Address,
// Stores the low-level operating system socket descriptor (e.g., a file descriptor).
// This is the handle used for OS-level socket operations.
socket: std.posix.socket_t,
// Public function (method) to initialize a new Socket instance.
// Takes an IP address string (IPv4) and a port number.
// Returns '!Socket', meaning it returns either a Socket instance or an error.
fn init(ip: []const u8, port: u16) !Socket {
// Parse the string IP address and port into a std.net.Address structure.
// This validates the IP format and combines it with the port.
// 'try' propagates errors from parsing (e.g., invalid IP format).
const parsed_address = try std.net.Address.parseIp4(ip, port);
// Create a low-level POSIX socket using the operating system's socket call.
// - std.posix.AF.INET: Specifies the address family (IPv4).
// - std.posix.SOCK.DGRAM: Specifies the socket type (UDP - datagram based, connectionless).
// - 0: Specifies the protocol (0 usually means the default protocol for the type, which is UDP here).
// 'try' propagates errors from the OS (e.g., insufficient permissions, resource limits).
const sock = try std.posix.socket(std.posix.AF.INET, std.posix.SOCK.DGRAM, 0);
// 'errdefer' schedules code to run ONLY if an error occurs *after* this line
// but *before* the function successfully returns. If socket creation succeeds
// but a subsequent operation in this function were to fail (none here, but imagine more steps),
// this ensures the created socket `sock` is closed, preventing resource leaks.
errdefer os.closeSocket(sock);
// If both parsing and socket creation succeed, return a new Socket struct instance,
// populated with the parsed address and the created OS socket descriptor.
return Socket{ .address = parsed_address, .socket = sock };
}
// Method to bind the socket to the address specified during initialization.
// Takes a mutable pointer 'self' to the Socket instance.
// Returns '!void', meaning it returns void on success or an error.
// Binding is necessary on the server/receiving side to listen on a specific IP/port.
fn bind(self: *Socket) !void {
// Call the operating system's bind function.
// - self.socket: The socket descriptor to bind.
// - &self.address.any: A pointer to the low-level OS socket address structure
// (like sockaddr_in for IPv4). std.net.Address provides access via the '.any' field.
// - self.address.getOsSockLen(): The size of the specific OS socket address structure.
// 'try' propagates errors from the OS (e.g., address already in use, invalid address).
try os.bind(self.socket, &self.address.any, self.address.getOsSockLen());
}
// Method to continuously listen for incoming UDP datagrams on the bound socket.
// Takes a mutable pointer 'self' to the Socket instance.
// Returns '!void', meaning it returns void or an error (though the loop is infinite).
// Note: For UDP, "listen" really means "start receiving". There's no connection setup like TCP.
fn listen(self: *Socket) !void {
// Declare a buffer on the stack to hold incoming data. Size 1024 bytes.
// Initialized to 'undefined' as recvfrom will fill it.
var buffer: [1024]u8 = undefined;
// Loop indefinitely to continuously receive data.
while (true) {
// Wait to receive data from the socket using the OS's recvfrom call.
// - self.socket: The socket descriptor to receive from.
// - buffer[0..]: A slice covering the entire buffer where data will be stored.
// - 0: Flags for the receive operation (0 means no special flags).
// - null: Pointer to store the sender's address (ignored in this example).
// - null: Pointer to store the sender's address length (ignored in this example).
// 'try' propagates errors from the OS (e.g., network issues).
// 'received_bytes' will hold the number of bytes actually received.
const received_bytes = try std.os.recvfrom(self.socket, buffer[0..], 0, null, null);
// Print the number of bytes received and the received data (as a string)
// to the standard error output (using std.debug.print).
// We use a slice `buffer[0..received_bytes]` to only print the valid data received,
// not the potentially uninitialized parts of the buffer.
std.debug.print("Received {d} bytes: {s}\n", .{ received_bytes, buffer[0..received_bytes] });
}
}
};`mmap(addr_hint, len, prot, flags, fd, offset)`: Map files or devices into memory (`posix.PROT.*`,`posix.MAP.*`).`munmap(addr, len)`: Unmap memory.`mprotect(addr, len, prot)`: Change memory protection.`mremap(...)`: Remap/resize virtual memory area (Linux-specific).`madvise(addr, len, advice)`: Give advice about memory usage (`posix.MADV.*`).`memfd_create(name, flags)`: Create an anonymous file in RAM (Linux-specific). Returns`fd_t`.
`clock_gettime(clock_id)`/`clock_getres(clock_id)`: Get time/resolution (`posix.timespec`) from specific clocks (e.g.,`posix.CLOCK.MONOTONIC`,`REALTIME`).`nanosleep(req_seconds, req_nanos)`: Suspend execution for an interval.`gettimeofday()`: Get wall-clock time (`posix.timeval`, legacy).`timerfd_create(clockid, flags)`/`timerfd_settime(...)`/`timerfd_gettime(...)`: File descriptor based timers (Linux-specific).
`isatty(fd)`: Check if fd refers to a terminal. Returnsbool.`tcgetattr(fd)`/`tcsetattr(fd, action, termios_p)`: Get/Set terminal attributes (`posix.termios`struct).`tcgetpgrp(fd)`/`tcsetpgrp(fd, pgrp)`: Get/Set terminal foreground process group.`ioctl(fd, request, ...)`: Device-specific control (e.g., getting window size`posix.TIOCGWINSZ`with`posix.winsize`struct).
`sigaction(signum, act, oldact)`: Examine/change signal action (install signal handler using`posix.Sigaction`struct).`sigprocmask(how, set, oldset)`: Examine/change blocked signals (`posix.sigset_t`) for the calling thread.`sigaltstack(ss, old_ss)`: Set/get alternate signal stack.- (See also
`kill`,`raise`in Process Management).
`poll(fds, timeout_ms)`: Wait for events on multiple file descriptors (`posix.pollfd`array).`ppoll(fds, timeout, sigmask)`: Like`poll`, but with atomic signal mask change.`epoll_create1(flags)`/`epoll_ctl(epfd, op, fd, event)`/`epoll_wait(epfd, events, maxevents, timeout)`: Edge-triggered I/O event notification (Linux-specific).`kqueue()`/`kevent(kq, changelist, eventlist, timeout)`: Generic event notification (BSD/macOS).`inotify_init1(flags)`/`inotify_add_watch(infd, path, mask)`/`inotify_rm_watch(infd, wd)`: Filesystem event notification (Linux-specific).`signalfd(...)`: Read signals as events from a file descriptor (Linux-specific).
`uname()`: Get system name and information (`posix.utsname`struct).`getenv(name)`/`getenvZ`: Get environment variable value (returns`?[]const u8`). Note: Not thread-safe in all libc implementations.`sysctl(...)`/`sysctlbynameZ(...)`: Get/Set system information (BSD/macOS).`prctl(...)`: Process control operations (Linux-specific).
`fd_t`, `pid_t`, `gid_t`, `uid_t`, `mode_t`, `off_t`, `size_t`, `ssize_t`, `timespec`, `timeval`, `sockaddr`, `sockaddr_in`, `sockaddr_in6`, `socklen_t`, `iovec`, `Stat`, `rusage`, `rlimit`, `sigset_t`, `Sigaction`, `termios`, `pollfd`, `utsname`, etc.
Found within `posix` namespace or nested structs/enums:
- File Flags:
`posix.O.*`(e.g.,`O.RDONLY`,`O.RDWR`,`O.CREAT`,`O.CLOEXEC`,`O.NONBLOCK`) - Memory Protection:
`posix.PROT.*`(e.g.,`PROT.READ`,`PROT.WRITE`,`PROT.EXEC`) - Memory Map Flags:
`posix.MAP.*`(e.g.,`MAP.SHARED`,`MAP.PRIVATE`,`MAP.FIXED`) - Seek Modes:
`posix.SEEK.*`(`SEEK.SET`,`SEEK.CUR`,`SEEK.END`) - Access Modes:
`posix.F_OK`,`posix.R_OK`,`posix.W_OK`,`posix.X_OK` - Socket Domains:
`posix.AF.*`(e.g.,`AF.INET`,`AF.INET6`,`AF.UNIX`) - Socket Types:
`posix.SOCK.*`(e.g.,`SOCK.STREAM`,`SOCK.DGRAM`,`SOCK.CLOEXEC`) - Socket Options:
`posix.SOL.*`,`posix.SO.*`,`posix.IPPROTO.*`,`posix.TCP.*` - Signal Numbers:
`posix.SIG.*`(e.g.,`SIG.INT`,`SIG.TERM`,`SIG.KILL`,`SIG.CHLD`) atFlags:`posix.AT.*`(e.g.,`AT.FDCWD`,`AT.SYMLINK_NOFOLLOW`,`AT.REMOVEDIR`)- Standard FDs:
`STDIN_FILENO`,`STDOUT_FILENO`,`STDERR_FILENO` - Error Codes (for reference):
`posix.E.*`(e.g.,`E.INVAL`,`E.PERM`,`E.NOENT`,`E.AGAIN`/`E.WOULDBLOCK`) - typically consumed via typed errors.