@@ -1462,18 +1462,87 @@ test parseHosts {
14621462 try std .testing .expectFmt ("127.0.0.2:1234" , "{f}" , .{addrs .items [0 ].addr });
14631463}
14641464
1465+ /// Validates a hostname according to [RFC 1123](https://www.rfc-editor.org/rfc/rfc1123)
14651466pub fn isValidHostName (hostname : []const u8 ) bool {
1466- if (hostname .len >= 254 ) return false ;
1467- if (! std .unicode .utf8ValidateSlice (hostname )) return false ;
1468- for (hostname ) | byte | {
1469- if (! std .ascii .isAscii (byte ) or byte == '.' or byte == '-' or std .ascii .isAlphanumeric (byte )) {
1470- continue ;
1467+ if (hostname .len == 0 ) return false ;
1468+ if (hostname [0 ] == '.' ) return false ;
1469+
1470+ // Ignore trailing dot (FQDN). It doesn't count toward our length.
1471+ const end = if (hostname [hostname .len - 1 ] == '.' ) end : {
1472+ if (hostname .len == 1 ) return false ;
1473+ break :end hostname .len - 1 ;
1474+ } else hostname .len ;
1475+
1476+ // The accepted maximum length of a hostname is 253 characters for the
1477+ // fully qualified domain name (FQDN), including labels and dots.
1478+ if (end > 253 ) return false ;
1479+
1480+ // Hostnames are divided into dot-separated "labels", which:
1481+ //
1482+ // - Start with a letter or digit
1483+ // - Can contain letters, digits, or hyphens
1484+ // - Must end with a letter or digit
1485+ // - Have a minimum of 1 character and a maximum of 63
1486+ var label_start : usize = 0 ;
1487+ var label_len : usize = 0 ;
1488+ for (hostname [0.. end ], 0.. ) | c , i | {
1489+ switch (c ) {
1490+ '.' = > {
1491+ if (label_len == 0 or label_len > 63 ) return false ;
1492+ if (! std .ascii .isAlphanumeric (hostname [label_start ])) return false ;
1493+ if (! std .ascii .isAlphanumeric (hostname [i - 1 ])) return false ;
1494+
1495+ label_start = i + 1 ;
1496+ label_len = 0 ;
1497+ },
1498+ '-' = > {
1499+ label_len += 1 ;
1500+ },
1501+ else = > {
1502+ if (! std .ascii .isAlphanumeric (c )) return false ;
1503+ label_len += 1 ;
1504+ },
14711505 }
1472- return false ;
14731506 }
1507+
1508+ // Validate the final label
1509+ if (label_len == 0 or label_len > 63 ) return false ;
1510+ if (! std .ascii .isAlphanumeric (hostname [label_start ])) return false ;
1511+ if (! std .ascii .isAlphanumeric (hostname [end - 1 ])) return false ;
1512+
14741513 return true ;
14751514}
14761515
1516+ test isValidHostName {
1517+ // Valid hostnames
1518+ try std .testing .expect (isValidHostName ("example" ));
1519+ try std .testing .expect (isValidHostName ("example.com" ));
1520+ try std .testing .expect (isValidHostName ("www.example.com" ));
1521+ try std .testing .expect (isValidHostName ("sub.domain.example.com" ));
1522+ try std .testing .expect (isValidHostName ("example.com." ));
1523+ try std .testing .expect (isValidHostName ("host-name.example.com." ));
1524+ try std .testing .expect (isValidHostName ("123.example.com." ));
1525+ try std .testing .expect (isValidHostName ("a-b.com" ));
1526+ try std .testing .expect (isValidHostName ("a.b.c.d.e.f.g" ));
1527+ try std .testing .expect (isValidHostName ("127.0.0.1" )); // Also a valid hostname
1528+ try std .testing .expect (isValidHostName ("a" ** 63 ++ ".com" )); // Label exactly 63 chars (valid)
1529+ try std .testing .expect (isValidHostName ("a." ** 126 ++ "a" )); // Total length 253 (valid)
1530+
1531+ // Invalid hostnames
1532+ try std .testing .expect (! isValidHostName ("" ));
1533+ try std .testing .expect (! isValidHostName (".example.com" ));
1534+ try std .testing .expect (! isValidHostName ("example.com.." ));
1535+ try std .testing .expect (! isValidHostName ("host..domain" ));
1536+ try std .testing .expect (! isValidHostName ("-hostname" ));
1537+ try std .testing .expect (! isValidHostName ("hostname-" ));
1538+ try std .testing .expect (! isValidHostName ("a.-.b" ));
1539+ try std .testing .expect (! isValidHostName ("host_name.com" ));
1540+ try std .testing .expect (! isValidHostName ("." ));
1541+ try std .testing .expect (! isValidHostName (".." ));
1542+ try std .testing .expect (! isValidHostName ("a" ** 64 ++ ".com" )); // Label length 64 (too long)
1543+ try std .testing .expect (! isValidHostName ("a." ** 126 ++ "ab" )); // Total length 254 (too long)
1544+ }
1545+
14771546fn linuxLookupNameFromDnsSearch (
14781547 gpa : Allocator ,
14791548 addrs : * ArrayList (LookupAddr ),
0 commit comments