milestone 19: hygiene sweep - dead ecs surface, single-source constants, tls classification, frontend state hazards, docker smoke network fix
This commit is contained in:
+5
-130
@@ -90,59 +90,6 @@ pub fn findOption(packet: []const u8, opt: OptRecord, code: u16) error{BadOption
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Address families in the EDNS Client Subnet option, from the IANA Address
|
||||
/// Family Numbers registry.
|
||||
pub const ecs_family_ipv4: u16 = 1;
|
||||
pub const ecs_family_ipv6: u16 = 2;
|
||||
|
||||
pub const Ecs = struct {
|
||||
family: u16,
|
||||
source_prefix: u8,
|
||||
scope_prefix: u8,
|
||||
/// The truncated address, `ceil(source_prefix / 8)` bytes, as a slice into
|
||||
/// the option data.
|
||||
address: []const u8,
|
||||
};
|
||||
|
||||
pub const EcsError = error{BadEcs};
|
||||
|
||||
/// RFC 7871 §6: FAMILY, SOURCE PREFIX-LENGTH, SCOPE PREFIX-LENGTH, then only
|
||||
/// as many address bytes as the source prefix covers.
|
||||
pub fn parseEcs(data: []const u8) EcsError!Ecs {
|
||||
if (data.len < 4) return error.BadEcs;
|
||||
const family = std.mem.readInt(u16, data[0..2], .big);
|
||||
const source_prefix = data[2];
|
||||
const scope_prefix = data[3];
|
||||
|
||||
const max_prefix: u16 = switch (family) {
|
||||
ecs_family_ipv4 => 32,
|
||||
ecs_family_ipv6 => 128,
|
||||
// An unknown family has no known address width, so only the encoded
|
||||
// length can be checked.
|
||||
else => 255,
|
||||
};
|
||||
if (source_prefix > max_prefix) return error.BadEcs;
|
||||
if (scope_prefix > max_prefix) return error.BadEcs;
|
||||
|
||||
// RFC 7871 §6 truncates the address to the source prefix and pads the last
|
||||
// octet with zero bits, so a prefix of 0 carries no address bytes at all.
|
||||
const address_len = (@as(usize, source_prefix) + 7) / 8;
|
||||
if (data.len - 4 != address_len) return error.BadEcs;
|
||||
|
||||
const significant_bits: u3 = @intCast(source_prefix % 8);
|
||||
if (significant_bits != 0) {
|
||||
const padding_mask = @as(u8, 0xff) >> significant_bits;
|
||||
if (data[3 + address_len] & padding_mask != 0) return error.BadEcs;
|
||||
}
|
||||
|
||||
return .{
|
||||
.family = family,
|
||||
.source_prefix = source_prefix,
|
||||
.scope_prefix = scope_prefix,
|
||||
.address = data[4..],
|
||||
};
|
||||
}
|
||||
|
||||
/// Writes the OPT record: root owner name, type OPT, the payload size in
|
||||
/// CLASS, the flags in TTL, then the option list verbatim.
|
||||
pub fn encodeOpt(opt: OptRecord, options_bytes: []const u8, w: *Writer) (Writer.Error || error{OptionsTooLong})!void {
|
||||
@@ -386,81 +333,6 @@ test "parseOpt rejects a malformed option list" {
|
||||
try testing.expectError(error.BadOption, parseOpt(partial, rp.record));
|
||||
}
|
||||
|
||||
test "parseEcs reads an IPv4 subnet" {
|
||||
const ecs = try parseEcs("\x00\x01\x18\x00\xc0\x00\x02");
|
||||
try testing.expectEqual(ecs_family_ipv4, ecs.family);
|
||||
try testing.expectEqual(@as(u8, 24), ecs.source_prefix);
|
||||
try testing.expectEqual(@as(u8, 0), ecs.scope_prefix);
|
||||
try testing.expectEqualSlices(u8, "\xc0\x00\x02", ecs.address);
|
||||
}
|
||||
|
||||
test "parseEcs reads an IPv6 subnet" {
|
||||
const ecs = try parseEcs("\x00\x02\x38\x38\x20\x01\x0d\xb8\x00\x00\x00");
|
||||
try testing.expectEqual(ecs_family_ipv6, ecs.family);
|
||||
try testing.expectEqual(@as(u8, 56), ecs.source_prefix);
|
||||
try testing.expectEqual(@as(u8, 56), ecs.scope_prefix);
|
||||
try testing.expectEqual(@as(usize, 7), ecs.address.len);
|
||||
}
|
||||
|
||||
test "parseEcs reads a zero-length prefix" {
|
||||
const ecs = try parseEcs("\x00\x01\x00\x00");
|
||||
try testing.expectEqual(@as(u8, 0), ecs.source_prefix);
|
||||
try testing.expectEqual(@as(usize, 0), ecs.address.len);
|
||||
|
||||
// A prefix of 0 covers no address byte, so any address byte is a length
|
||||
// mismatch.
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x00\x00\x00"));
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x00\x00\xc0\x00\x02\x00"));
|
||||
}
|
||||
|
||||
test "parseEcs rejects nonzero padding bits past the source prefix" {
|
||||
// IPv4 /25: the low seven bits of the fourth address byte must be zero.
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x19\x00\xc0\x00\x02\x01"));
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x19\x00\xc0\x00\x02\xff"));
|
||||
|
||||
const zero_padded = try parseEcs("\x00\x01\x19\x00\xc0\x00\x02\x80");
|
||||
try testing.expectEqual(@as(u8, 25), zero_padded.source_prefix);
|
||||
try testing.expectEqualSlices(u8, "\xc0\x00\x02\x80", zero_padded.address);
|
||||
|
||||
// IPv4 /20: the low four bits of the third address byte must be zero.
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x14\x00\xc0\x00\x0f"));
|
||||
try testing.expectEqualSlices(u8, "\xc0\x00\x00", (try parseEcs("\x00\x01\x14\x00\xc0\x00\x00")).address);
|
||||
|
||||
// IPv6 /57: the low seven bits of the eighth address byte must be zero.
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x02\x39\x00\x20\x01\x0d\xb8\x00\x00\x00\x7f"));
|
||||
try testing.expectEqualSlices(
|
||||
u8,
|
||||
"\x20\x01\x0d\xb8\x00\x00\x00\x80",
|
||||
(try parseEcs("\x00\x02\x39\x00\x20\x01\x0d\xb8\x00\x00\x00\x80")).address,
|
||||
);
|
||||
|
||||
// An unknown family uses the same encoding, so the rule holds there too.
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x12\x34\x03\x00\xff"));
|
||||
try testing.expectEqualSlices(u8, "\xe0", (try parseEcs("\x12\x34\x03\x00\xe0")).address);
|
||||
}
|
||||
|
||||
test "parseEcs rejects malformed options" {
|
||||
// Shorter than the fixed fields.
|
||||
try testing.expectError(error.BadEcs, parseEcs(""));
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x18"));
|
||||
// Prefix wider than the family allows.
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x21" ++ "\x00\x00\x00\x00\x00"));
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x02\x81" ++ "\x00" ** 17));
|
||||
// Scope wider than the family allows.
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x18\x21\xc0\x00\x02"));
|
||||
// Address shorter than the prefix needs.
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x18\x00\xc0\x00"));
|
||||
// Address longer than the prefix needs.
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x00\x01\x18\x00\xc0\x00\x02\x00"));
|
||||
}
|
||||
|
||||
test "parseEcs accepts an unknown family with a consistent length" {
|
||||
const ecs = try parseEcs("\x12\x34\x08\x00\xff");
|
||||
try testing.expectEqual(@as(u16, 0x1234), ecs.family);
|
||||
try testing.expectEqualSlices(u8, "\xff", ecs.address);
|
||||
try testing.expectError(error.BadEcs, parseEcs("\x12\x34\x08\x00\xff\xff"));
|
||||
}
|
||||
|
||||
test "encodeOpt round-trips through record parse and parseOpt" {
|
||||
const original: OptRecord = .{
|
||||
.udp_payload_size = 1232,
|
||||
@@ -485,8 +357,11 @@ test "encodeOpt round-trips through record parse and parseOpt" {
|
||||
try testing.expectEqual(original.do_bit, opt.do_bit);
|
||||
try testing.expectEqualSlices(u8, options_bytes, opt.options.slice(bytes));
|
||||
|
||||
const ecs = try parseEcs((try findOption(bytes, opt, ecs_option_code)).?.data);
|
||||
try testing.expectEqual(@as(u8, 24), ecs.source_prefix);
|
||||
try testing.expectEqualSlices(
|
||||
u8,
|
||||
"\x00\x01\x18\x00\xc0\x00\x02",
|
||||
(try findOption(bytes, opt, ecs_option_code)).?.data,
|
||||
);
|
||||
}
|
||||
|
||||
test "encodeOpt round-trips both DO states" {
|
||||
|
||||
Reference in New Issue
Block a user