milestone 11: systemd and docker packaging, operator and architecture docs, config and api reference, docs drift guards
This commit is contained in:
+59
-4
@@ -458,8 +458,8 @@ fn serve(r: cli.Runner, args: cli.RunArgs) !u8 {
|
||||
.reload_fn = reloadManager,
|
||||
};
|
||||
|
||||
const v6_bind = parseBind(r, cfg.dns.bind_ipv6, cfg.dns.port, "dns.bind_ipv6") catch |err| return err;
|
||||
const v4_bind = parseBind(r, cfg.dns.bind_ipv4, cfg.dns.port, "dns.bind_ipv4") catch |err| return err;
|
||||
const v6_bind = parseBind(r, cfg.dns.bind_ipv6, cfg.dns.port, "dns.bind_ipv6", .ip6) catch |err| return err;
|
||||
const v4_bind = parseBind(r, cfg.dns.bind_ipv4, cfg.dns.port, "dns.bind_ipv4", .ip4) catch |err| return err;
|
||||
|
||||
// IPv6 first, and the order is load-bearing — see `Listeners`.
|
||||
var udp6: ?udp_server.UdpServer = udp_server.UdpServer.bind(gpa, io, v6_bind, &h, .{}) catch |err| bound: {
|
||||
@@ -826,11 +826,66 @@ const Listeners = struct {
|
||||
tcp4: ?net.IpAddress,
|
||||
};
|
||||
|
||||
fn parseBind(r: cli.Runner, text: []const u8, port: u16, field: []const u8) !net.IpAddress {
|
||||
return net.IpAddress.parse(text, port) catch {
|
||||
const BindFamily = enum { ip4, ip6 };
|
||||
|
||||
/// `config/validate.checkBind` enforces the same family rule on import, check
|
||||
/// and settings PUT — but not on a config.db written before the rule existed,
|
||||
/// and `serve` loads that DB without re-validating. Boot is the last seam: a
|
||||
/// cross-family literal here would bind the wrong family's socket and make the
|
||||
/// real one fail with AddressInUse, silently losing a family.
|
||||
fn parseBind(
|
||||
r: cli.Runner,
|
||||
text: []const u8,
|
||||
port: u16,
|
||||
field: []const u8,
|
||||
family: BindFamily,
|
||||
) !net.IpAddress {
|
||||
const addr = net.IpAddress.parse(text, port) catch {
|
||||
r.err.print("{s}: '{s}' is not an IP address\n", .{ field, text }) catch {};
|
||||
return error.BadBindAddress;
|
||||
};
|
||||
const matches = switch (addr) {
|
||||
.ip4 => family == .ip4,
|
||||
.ip6 => family == .ip6,
|
||||
};
|
||||
if (!matches) {
|
||||
const digit: u8 = if (family == .ip4) '4' else '6';
|
||||
r.err.print(
|
||||
"{s}: '{s}' is not an IPv{c} address; re-import the configuration or correct it with a settings PUT\n",
|
||||
.{ field, text, digit },
|
||||
) catch {};
|
||||
return error.BadBindAddress;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
test "parseBind refuses a bind address of the wrong family" {
|
||||
var out_buf: [8]u8 = undefined;
|
||||
var err_buf: [256]u8 = undefined;
|
||||
var out: Writer = .fixed(&out_buf);
|
||||
var err_writer: Writer = .fixed(&err_buf);
|
||||
const r: cli.Runner = .{
|
||||
.io = std.testing.io,
|
||||
.gpa = std.testing.allocator,
|
||||
.out = &out,
|
||||
.err = &err_writer,
|
||||
};
|
||||
|
||||
_ = try parseBind(r, "0.0.0.0", 53, "dns.bind_ipv4", .ip4);
|
||||
_ = try parseBind(r, "::", 53, "dns.bind_ipv6", .ip6);
|
||||
|
||||
try std.testing.expectError(
|
||||
error.BadBindAddress,
|
||||
parseBind(r, "0.0.0.0", 53, "dns.bind_ipv6", .ip6),
|
||||
);
|
||||
try std.testing.expectError(
|
||||
error.BadBindAddress,
|
||||
parseBind(r, "::", 53, "dns.bind_ipv4", .ip4),
|
||||
);
|
||||
|
||||
const printed = err_writer.buffered();
|
||||
try std.testing.expect(std.mem.containsAtLeast(u8, printed, 1, "is not an IPv6 address"));
|
||||
try std.testing.expect(std.mem.containsAtLeast(u8, printed, 1, "is not an IPv4 address"));
|
||||
}
|
||||
|
||||
fn reportBind(r: cli.Runner, which: []const u8, addr: net.IpAddress, err: anyerror) anyerror {
|
||||
|
||||
Reference in New Issue
Block a user