Repository navigation
proposal(net): classify IP addresses against the IANA special-purpose registries #7315
Description
I'd like to add IP classification to @std/net/unstable-ip, backed by the IANA special-purpose registries. Fix the parsers first.
The current validators accept these inputs, reproduced on this checkout:
| Validator | Invalid inputs returning true |
|---|---|
isIPv4 |
"0x7f.0.0.1", " 1.2.3.4", "1..2.3", "01.002.3.4" |
isIPv6 |
":1", "2001:db8:::1", "::1 " |
In #6765, 0f-0b requested URL-standard syntax and parsers that return bytes. This would follow through on that proposal.
There's demand beyond validation. Better Auth's shared host classifier serves eleven call sites across seven packages, including SSRF fixes and client metadata fetches. Go netip, Rust std::net, and Python ipaddress already offer address classification. Potential std consumers include client-IP handling in #7237 and unspecified-address handling in http/file_server.ts. The first is the concrete one. Keying a rate limit by client IP has to group IPv6 clients by their allocation, not their address, and that needs masking and canonical formatting on top of the parsers. That is item 5.
Proposed scope, in separate PRs:
- Add
parseIPv4/parseIPv6, returning 4 / 16 bytes asUint8Array, orundefinedfor invalid input. Share parsing with the validators and subnet matchers. Landed in fix(net/unstable): parse IP addresses per the URL standard #7316. - Add
classifyIP(): IPAddressKind | undefined, plusisLoopback,isPrivate,isLinkLocal,isMulticast, andisUnspecified. Kinds: loopback, unspecified, private, link-local, shared, documentation, benchmarking, multicast, broadcast, reserved, global. Open as feat(net/unstable): classify IP addresses by IANA special-purpose registry #7324. - Optional, and worth doing only with a caller behind it:
embeddedIPv4for 6to4, NAT64's well-known prefix, Teredo, and deprecated IPv4-compatible addresses, thenisGloballyReachableonce the handling of embedded addresses and IANA's N/A entries is defined and tested. Both answer an SSRF question, and nothing in std asks it yet. - Use the helpers in std where they simplify existing code, starting with the file server or rate limiter.
- Add
formatIP, turning bytes into canonical text via the URL standard's IPv6 serializer, andmaskIP, turning bytes plus a prefix length into the masked bytes. Export the IPv4-mapped unmapping that item 2 keeps private. Those three plus the parsers are the whole of client-IP keying: parse, mask to the allocation, format the key.
Item 5 is next. Item 3 waits for someone who needs it.
Keep this in net/unstable_ip.ts, under the existing entry point. Use longest-prefix matching against the IPv4 and IPv6 registries, with RFC-defined multicast ranges. Exceptions matter: 192.0.0.9 and .10 are globally reachable within a non-global /24.
Unmap IPv4-mapped IPv6 before classification, so ::ffff:127.0.0.1 classifies as loopback. Keep tunnel extraction separate from classification of the outer address.
Inputs are bare IP literals: no ports, brackets, whitespace trimming, zone IDs, or hostname classification. URL callers normalize through URL and remove IPv6 hostname brackets before passing the address. Global reachability describes address ranges. It does not resolve DNS, prevent rebinding, or check redirects.
For IPv4, I proposed the URL standard's strict dotted-quad syntax: four decimal octets, no leading zeros. That is what #7316 landed, so I am treating it as settled. Say so here if you would rather have the URL parser's permissive grammar accepting hex, octal, and shorthand, since that regrades parseIPv4 and everything above it.
Activity