authorgravatar for hahv@msn.comHuang Zhichao <hahv@msn.com> 2026-05-25 12:57:12+08:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-08-27 23:11:38+02:00
loge2c60cf76778cc049d41a9149df8a5fbb172d3c1
tree1c7989c88d1e011732b3387d292e19727b12a6b9
parente11cd2316bf752d42ab03807f40112e85cd46ab0

std.Io.net.HostName: correct `max_len`

`max_len` should be 254 since `HostName` stores the text representation of the domain. www .example .com www .example .com . // with the trailing dot (max 254) \x03www\x07example\x03com\x00 // in the dns packet (max 255)

1 files changed, 19 insertions(+), 8 deletions(-)

lib/std/Io/net/HostName.zig+19-8
......@@ -17,7 +17,17 @@ const Stream = Io.net.Stream;
1717/// Externally managed memory. Already checked to be valid.
1818bytes: []const u8,
1919
20pub const max_len = 255;
20/// The maximum number of bytes needed to store the text representation of
21/// a max length host name, where labels are separated by dots, including
22/// the trailing dot and the zero-length root label.
23///
24/// The max length of a host name is determined by its packet representation,
25/// where each label has a length prefix of 1 octet, the root label has a
26/// length of 0, and the maximum total number of octets is 255.
27///
28/// See [RFC 1035, Section 3.1](https://datatracker.ietf.org/doc/html/rfc1035#section-3.1)
29pub const max_len = 254;
30const max_len_without_root = max_len - 1;
2131
2232pub const FromUriError = error{UriMissingHost} || ValidateError;
2333
......@@ -40,12 +50,12 @@ pub const ValidateError = error{
4050pub fn validate(bytes: []const u8) ValidateError!void {
4151 if (bytes.len == 0) return error.InvalidHostName;
4252
43 // The accepted maximum length of a hostname, including labels and dots.
44 if (bytes.len > max_len) return error.NameTooLong;
45
4653 // Ignore trailing dot (FQDN).
4754 const end = if (bytes[bytes.len - 1] == '.') bytes.len - 1 else bytes.len;
4855
56 // The accepted maximum length of a hostname, including labels and dots.
57 if (end > max_len_without_root) return error.NameTooLong;
58
4959 // Hostnames are divided into dot-separated "labels", which:
5060 //
5161 // - Start with a letter or digit
......@@ -92,9 +102,10 @@ test validate {
92102 const many_a: [63]u8 = @splat('a');
93103 try validate(&many_a ++ ".com"); // Label exactly 63 chars (valid)
94104
95 const many_a_dot_buf: [127][2]u8 = @splat(.{ 'a', '.' });
105 const many_a_dot_buf: [126][2]u8 = @splat(.{ 'a', '.' });
96106 const many_a_dot: []const u8 = @ptrCast(&many_a_dot_buf);
97 try validate(many_a_dot ++ "a"); // Total length 255 (valid)
107 try validate(many_a_dot ++ "a"); // Total length 253 (without the trailing dot)
108 try validate(many_a_dot ++ "a."); // Total length 254 (with the trailing dot)
98109
99110 // Invalid hostnames
100111 try std.testing.expectError(error.InvalidHostName, validate(""));
......@@ -109,8 +120,8 @@ test validate {
109120 try std.testing.expectError(error.InvalidHostName, validate("."));
110121 try std.testing.expectError(error.InvalidHostName, validate(".."));
111122 try std.testing.expectError(error.InvalidHostName, validate(&many_a ++ "a.com")); // Label length 64 (too long)
112 try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "a.")); // Total length 255 + trailing dot (too long)
113 try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "ab")); // Total length 256 (too long)
123 try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "ab")); // Total length 254 (without the trailing dot)
124 try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "ab.")); // Total length 255 (with the trailing dot)
114125}
115126
116127pub fn init(bytes: []const u8) ValidateError!HostName {