milestone 18: collapse duplicated infrastructure into shared listener core, crud list helper, resource shells, transport race, name and line helpers, ui modules
This commit is contained in:
@@ -134,6 +134,34 @@ pub fn fromText(text: []const u8) FromTextError!Name {
|
||||
return name;
|
||||
}
|
||||
|
||||
pub const NormalizeError = error{BadName};
|
||||
|
||||
/// Lowercases over ASCII into `buf`, strips one trailing dot, and checks the
|
||||
/// result is a name `fromText` accepts. Returns the normalized text, borrowed
|
||||
/// from `buf`.
|
||||
///
|
||||
/// A byte ≥ 0x80 is rejected: query names arrive ASCII-lowercased, so a high
|
||||
/// byte here could never be matched, and configuration that can never match is
|
||||
/// worth reporting rather than storing. The root name is rejected for the same
|
||||
/// reason — a configured entry that matches everything, or nothing, is not what
|
||||
/// either caller means.
|
||||
///
|
||||
/// `filter/rules.zig`, `filter/compiler.zig` and `cache/dns_cache.zig` keep
|
||||
/// their own variants on purpose; each says why beside its copy.
|
||||
pub fn normalizeText(text: []const u8, buf: *[types.max_name_len]u8) NormalizeError![]const u8 {
|
||||
var rest = text;
|
||||
if (rest.len > 0 and rest[rest.len - 1] == '.') rest = rest[0 .. rest.len - 1];
|
||||
if (rest.len == 0 or rest.len > types.max_name_len) return error.BadName;
|
||||
|
||||
for (rest, 0..) |byte, i| {
|
||||
if (byte >= 0x80) return error.BadName;
|
||||
buf[i] = std.ascii.toLower(byte);
|
||||
}
|
||||
const normalized = buf[0..rest.len];
|
||||
_ = fromText(normalized) catch return error.BadName;
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/// Writes presentation form: labels joined by dots, no trailing dot. The root
|
||||
/// name writes as ".".
|
||||
pub fn formatText(name: Name, w: *Writer) Writer.Error!void {
|
||||
@@ -392,3 +420,22 @@ test "labelCount" {
|
||||
try testing.expectEqual(@as(usize, 1), (try fromText("com")).labelCount());
|
||||
try testing.expectEqual(@as(usize, 3), (try fromText("www.example.com")).labelCount());
|
||||
}
|
||||
|
||||
test "normalizeText lowercases and strips one trailing dot" {
|
||||
var buf: [types.max_name_len]u8 = undefined;
|
||||
try testing.expectEqualStrings("example.com", try normalizeText("Example.COM", &buf));
|
||||
try testing.expectEqualStrings("example.com", try normalizeText("example.com.", &buf));
|
||||
try testing.expectEqualStrings("a", try normalizeText("A", &buf));
|
||||
}
|
||||
|
||||
test "normalizeText rejects the root, high bytes and names fromText refuses" {
|
||||
var buf: [types.max_name_len]u8 = undefined;
|
||||
try testing.expectError(error.BadName, normalizeText("", &buf));
|
||||
try testing.expectError(error.BadName, normalizeText(".", &buf));
|
||||
try testing.expectError(error.BadName, normalizeText("caf\xc3\xa9.example.com", &buf));
|
||||
try testing.expectError(error.BadName, normalizeText("a..b", &buf));
|
||||
try testing.expectError(error.BadName, normalizeText("a." ** 200 ++ "com", &buf));
|
||||
|
||||
// A label of 64 bytes is one over the wire limit.
|
||||
try testing.expectError(error.BadName, normalizeText("a" ** 64 ++ ".com", &buf));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user