From e2c60cf76778cc049d41a9149df8a5fbb172d3c1 Mon Sep 17 00:00:00 2001 From: Huang Zhichao Date: Mon, 25 May 2026 12:57:12 +0800 Subject: [PATCH] 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) --- lib/std/Io/net/HostName.zig | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/std/Io/net/HostName.zig b/lib/std/Io/net/HostName.zig index 8c5717d56d13f7dc5bc861cdfbda7cbb81816860..2fc2c4c79a55d9526825db860c9083742b7c6ae9 100644 --- a/lib/std/Io/net/HostName.zig +++ b/lib/std/Io/net/HostName.zig @@ -17,7 +17,17 @@ const Stream = Io.net.Stream; /// Externally managed memory. Already checked to be valid. bytes: []const u8, -pub const max_len = 255; +/// The maximum number of bytes needed to store the text representation of +/// a max length host name, where labels are separated by dots, including +/// the trailing dot and the zero-length root label. +/// +/// The max length of a host name is determined by its packet representation, +/// where each label has a length prefix of 1 octet, the root label has a +/// length of 0, and the maximum total number of octets is 255. +/// +/// See [RFC 1035, Section 3.1](https://datatracker.ietf.org/doc/html/rfc1035#section-3.1) +pub const max_len = 254; +const max_len_without_root = max_len - 1; pub const FromUriError = error{UriMissingHost} || ValidateError; @@ -40,12 +50,12 @@ pub const ValidateError = error{ pub fn validate(bytes: []const u8) ValidateError!void { if (bytes.len == 0) return error.InvalidHostName; - // The accepted maximum length of a hostname, including labels and dots. - if (bytes.len > max_len) return error.NameTooLong; - // Ignore trailing dot (FQDN). const end = if (bytes[bytes.len - 1] == '.') bytes.len - 1 else bytes.len; + // The accepted maximum length of a hostname, including labels and dots. + if (end > max_len_without_root) return error.NameTooLong; + // Hostnames are divided into dot-separated "labels", which: // // - Start with a letter or digit @@ -92,9 +102,10 @@ test validate { const many_a: [63]u8 = @splat('a'); try validate(&many_a ++ ".com"); // Label exactly 63 chars (valid) - const many_a_dot_buf: [127][2]u8 = @splat(.{ 'a', '.' }); + const many_a_dot_buf: [126][2]u8 = @splat(.{ 'a', '.' }); const many_a_dot: []const u8 = @ptrCast(&many_a_dot_buf); - try validate(many_a_dot ++ "a"); // Total length 255 (valid) + try validate(many_a_dot ++ "a"); // Total length 253 (without the trailing dot) + try validate(many_a_dot ++ "a."); // Total length 254 (with the trailing dot) // Invalid hostnames try std.testing.expectError(error.InvalidHostName, validate("")); @@ -109,8 +120,8 @@ test validate { try std.testing.expectError(error.InvalidHostName, validate(".")); try std.testing.expectError(error.InvalidHostName, validate("..")); try std.testing.expectError(error.InvalidHostName, validate(&many_a ++ "a.com")); // Label length 64 (too long) - try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "a.")); // Total length 255 + trailing dot (too long) - try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "ab")); // Total length 256 (too long) + try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "ab")); // Total length 254 (without the trailing dot) + try std.testing.expectError(error.NameTooLong, validate(many_a_dot ++ "ab.")); // Total length 255 (with the trailing dot) } pub fn init(bytes: []const u8) ValidateError!HostName { -- 2.54.0