diff --git a/src/cli/ssh-cache/DiskCache.zig b/src/cli/ssh-cache/DiskCache.zig index bda1d262f..b700c443d 100644 --- a/src/cli/ssh-cache/DiskCache.zig +++ b/src/cli/ssh-cache/DiskCache.zig @@ -411,9 +411,9 @@ pub fn isValidCacheKey(key: []const u8) bool { fn isValidHost(host: []const u8) bool { // First check for valid hostnames because this is assumed to be the more // likely ssh host format. - if (internal_os.hostname.isValid(host)) { + if (std.Io.net.HostName.validate(host)) |_| { return true; - } + } else |_| {} // We also accept valid IP addresses. In practice, IPv4 addresses are also // considered valid hostnames due to their overlapping syntax, so we can diff --git a/src/os/hostname.zig b/src/os/hostname.zig index 45689e0b6..969b4baae 100644 --- a/src/os/hostname.zig +++ b/src/os/hostname.zig @@ -7,91 +7,6 @@ pub const LocalHostnameValidationError = error{ Unexpected, }; -/// Validates a hostname according to [RFC 1123](https://www.rfc-editor.org/rfc/rfc1123) -/// -/// std.net.isValidHostname is (currently) too generous. It considers strings like -/// ".example.com", "exa..mple.com", and "-example.com" to be valid hostnames, which -/// is incorrect. -pub fn isValid(hostname: []const u8) bool { - if (hostname.len == 0) return false; - if (hostname[0] == '.') return false; - - // Ignore trailing dot (FQDN). It doesn't count toward our length. - const end = if (hostname[hostname.len - 1] == '.') end: { - if (hostname.len == 1) return false; - break :end hostname.len - 1; - } else hostname.len; - - if (end > 253) return false; - - // Hostnames are divided into dot-separated "labels", which: - // - // - Start with a letter or digit - // - Can contain letters, digits, or hyphens - // - Must end with a letter or digit - // - Have a minimum of 1 character and a maximum of 63 - var label_start: usize = 0; - var label_len: usize = 0; - for (hostname[0..end], 0..) |c, i| { - switch (c) { - '.' => { - if (label_len == 0 or label_len > 63) return false; - if (!std.ascii.isAlphanumeric(hostname[label_start])) return false; - if (!std.ascii.isAlphanumeric(hostname[i - 1])) return false; - - label_start = i + 1; - label_len = 0; - }, - '-' => { - label_len += 1; - }, - else => { - if (!std.ascii.isAlphanumeric(c)) return false; - label_len += 1; - }, - } - } - - // Validate the final label - if (label_len == 0 or label_len > 63) return false; - if (!std.ascii.isAlphanumeric(hostname[label_start])) return false; - if (!std.ascii.isAlphanumeric(hostname[end - 1])) return false; - - return true; -} - -test isValid { - const testing = std.testing; - - // Valid hostnames - try testing.expect(isValid("example")); - try testing.expect(isValid("example.com")); - try testing.expect(isValid("www.example.com")); - try testing.expect(isValid("sub.domain.example.com")); - try testing.expect(isValid("example.com.")); - try testing.expect(isValid("host-name.example.com.")); - try testing.expect(isValid("123.example.com.")); - try testing.expect(isValid("a-b.com")); - try testing.expect(isValid("a.b.c.d.e.f.g")); - try testing.expect(isValid("127.0.0.1")); // Also a valid hostname - try testing.expect(isValid("a" ** 63 ++ ".com")); // Label exactly 63 chars (valid) - try testing.expect(isValid("a." ** 126 ++ "a")); // Total length 253 (valid) - - // Invalid hostnames - try testing.expect(!isValid("")); - try testing.expect(!isValid(".example.com")); - try testing.expect(!isValid("example.com..")); - try testing.expect(!isValid("host..domain")); - try testing.expect(!isValid("-hostname")); - try testing.expect(!isValid("hostname-")); - try testing.expect(!isValid("a.-.b")); - try testing.expect(!isValid("host_name.com")); - try testing.expect(!isValid(".")); - try testing.expect(!isValid("..")); - try testing.expect(!isValid("a" ** 64 ++ ".com")); // Label length 64 (too long) - try testing.expect(!isValid("a." ** 126 ++ "ab")); // Total length 254 (too long) -} - /// Checks if a hostname is local to the current machine. This matches /// both "localhost" and the current hostname of the machine (as returned /// by `gethostname`).