authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-07-03 21:30:31-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-07-03 21:33:40-07:00
loga6f7722b32bd9e0c1b86a978adcb5db270a23d59
treee43bedbfc8c5204fdda40dfea936f89b9e3b9314
parentf1531406979b7c196ec535008a159b5f411cf78f

HostName: Include trailing . in max length calculation

The 255 max length restriction is based on the representation in a packet, where each label has a byte prefixed denoting the length and the trailing dot is non-optional but specified as a zero-length label (so one byte with value 0 at the end). This means that for the purposes of validation, the trailing dot should be included in the length. This also ensures that max_len is always enough to hold a validated HostName, whereas before any code relying on that assumption was technically invalid as it was possible to pass through `validate` with a 256-length slice.

1 files changed, 5 insertions(+), 6 deletions(-)

lib/std/Io/net/HostName.zig+5-6
......@@ -28,11 +28,11 @@ pub const ValidateError = error{
2828pub fn validate(bytes: []const u8) ValidateError!void {
2929 if (bytes.len == 0) return error.InvalidHostName;
3030
31 // Ignore trailing dot (FQDN). It doesn't count toward our length.
32 const end = if (bytes[bytes.len - 1] == '.') bytes.len - 1 else bytes.len;
33
3431 // The accepted maximum length of a hostname, including labels and dots.
35 if (end > max_len) return error.NameTooLong;
32 if (bytes.len > max_len) return error.NameTooLong;
33
34 // Ignore trailing dot (FQDN).
35 const end = if (bytes[bytes.len - 1] == '.') bytes.len - 1 else bytes.len;
3636
3737 // Hostnames are divided into dot-separated "labels", which:
3838 //
......@@ -83,7 +83,6 @@ test validate {
8383 const many_a_dot_buf: [127][2]u8 = @splat(.{ 'a', '.' });
8484 const many_a_dot: []const u8 = @ptrCast(&many_a_dot_buf);
8585 try validate(many_a_dot ++ "a"); // Total length 255 (valid)
86 try validate(many_a_dot ++ "a."); // Total length 255 + trailing dot (valid)
8786
8887 // Invalid hostnames
8988 try std.testing.expectError(error.InvalidHostName, validate(""));
......@@ -98,8 +97,8 @@ test validate {
9897 try std.testing.expectError(error.InvalidHostName, validate("."));
9998 try std.testing.expectError(error.InvalidHostName, validate(".."));
10099 try std.testing.expectError(error.InvalidHostName, validate(&many_a ++ "a.com")); // Label length 64 (too long)
100 try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "a.")); // Total length 255 + trailing dot (too long)
101101 try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "ab")); // Total length 256 (too long)
102 try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "ab.")); // Total length 256 + trailing dot (too long)
103102}
104103
105104pub fn init(bytes: []const u8) ValidateError!HostName {