//! Server configuration. Uses plain struct defaults rather than wish's //! functional `WithX` options — zig has struct defaults, so the option-fn //! dance is unnecessary. Names still mirror wish for parity. const Options = @This(); /// Listen address (host + port). `std.Io.net.IpAddress` gives us parsing and /// validation for free — build it with `.parse("0.0.0.0", 2222)` or an /// `.{ .ip4 = ... }` literal. Default is 0.0.0.0:2222 so examples run without /// root. address: IpAddress = .{ .ip4 = .{ .bytes = .{ 0, 0, 0, 0 }, .port = 2222 } }, /// Path to a host key on disk (any type libssh accepts). If null and /// `host_key_pem` is also null, `serve` fails with `error.HostKeyRequired`. host_key_path: ?[:0]const u8 = null, /// In-memory PEM-encoded host key. Overrides `host_key_path` if both are set. host_key_pem: ?[:0]const u8 = null, /// Banner string sent before authentication. banner: [:0]const u8 = "", /// Server identification string. libssh default is used when null. version: ?[:0]const u8 = null, const std = @import("std"); const IpAddress = std.Io.net.IpAddress; const Writer = std.Io.Writer; /// Render the bind host (address *without* port) as a sentinel-terminated /// string into `buf`, for libssh's `SSH_BIND_OPTIONS_BINDADDR`. Note that /// `IpAddress.format` glues the port on (`a.b.c.d:port` / `[addr]:port`), so /// we format host-only ourselves. pub fn hostZ(self: Options, buf: []u8) ![:0]const u8 { switch (self.address) { .ip4 => |a| { const b = a.bytes; return std.fmt.bufPrintZ(buf, "{d}.{d}.{d}.{d}", .{ b[0], b[1], b[2], b[3] }) catch error.AddressTooLong; }, .ip6 => { // Ip6Address.format emits "[addr]:port"; take the bracketed part. var tmp: [64]u8 = undefined; var w: Writer = .fixed(&tmp); self.address.format(&w) catch return error.AddressTooLong; const s = w.buffered(); const start = (std.mem.indexOfScalar(u8, s, '[') orelse return error.AddressTooLong) + 1; const end = std.mem.indexOfScalar(u8, s, ']') orelse return error.AddressTooLong; return std.fmt.bufPrintZ(buf, "{s}", .{s[start..end]}) catch error.AddressTooLong; }, } } /// Render the bind port as a sentinel-terminated string into `buf`, for /// libssh's `SSH_BIND_OPTIONS_BINDPORT_STR`. pub fn portZ(self: Options, buf: []u8) ![:0]const u8 { return std.fmt.bufPrintZ(buf, "{d}", .{self.address.getPort()}); }