import (
"internal/bytealg"
"internal/itoa"
+ "net/netip"
)
// IP address lengths (bytes).
return out
}
-// ubtoa encodes the string form of the integer v to dst[start:] and
-// returns the number of bytes written to dst. The caller must ensure
-// that dst has sufficient length.
-func ubtoa(dst []byte, start int, v byte) int {
- if v < 10 {
- dst[start] = v + '0'
- return 1
- } else if v < 100 {
- dst[start+1] = v%10 + '0'
- dst[start] = v/10 + '0'
- return 2
- }
-
- dst[start+2] = v%10 + '0'
- dst[start+1] = (v/10)%10 + '0'
- dst[start] = v/100 + '0'
- return 3
-}
-
// String returns the string form of the IP address ip.
// It returns one of 4 forms:
// - "<nil>", if ip has length 0
// - IPv6 conforming to RFC 5952 ("2001:db8::1"), if ip is a valid IPv6 address
// - the hexadecimal form of ip, without punctuation, if no other cases apply
func (ip IP) String() string {
- p := ip
-
if len(ip) == 0 {
return "<nil>"
}
- // If IPv4, use dotted notation.
- if p4 := p.To4(); len(p4) == IPv4len {
- const maxIPv4StringLen = len("255.255.255.255")
- b := make([]byte, maxIPv4StringLen)
-
- n := ubtoa(b, 0, p4[0])
- b[n] = '.'
- n++
-
- n += ubtoa(b, n, p4[1])
- b[n] = '.'
- n++
-
- n += ubtoa(b, n, p4[2])
- b[n] = '.'
- n++
-
- n += ubtoa(b, n, p4[3])
- return string(b[:n])
- }
- if len(p) != IPv6len {
+ if len(ip) != IPv4len && len(ip) != IPv6len {
return "?" + hexString(ip)
}
-
- // Find longest run of zeros.
- e0 := -1
- e1 := -1
- for i := 0; i < IPv6len; i += 2 {
- j := i
- for j < IPv6len && p[j] == 0 && p[j+1] == 0 {
- j += 2
- }
- if j > i && j-i > e1-e0 {
- e0 = i
- e1 = j
- i = j
- }
- }
- // The symbol "::" MUST NOT be used to shorten just one 16 bit 0 field.
- if e1-e0 <= 2 {
- e0 = -1
- e1 = -1
- }
-
- const maxLen = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
- b := make([]byte, 0, maxLen)
-
- // Print with possible :: in place of run of zeros
- for i := 0; i < IPv6len; i += 2 {
- if i == e0 {
- b = append(b, ':', ':')
- i = e1
- if i >= IPv6len {
- break
- }
- } else if i > 0 {
- b = append(b, ':')
- }
- b = appendHex(b, (uint32(p[i])<<8)|uint32(p[i+1]))
+ // If IPv4, use dotted notation.
+ if p4 := ip.To4(); len(p4) == IPv4len {
+ return netip.AddrFrom4([4]byte(p4)).String()
}
- return string(b)
+ return netip.AddrFrom16([16]byte(ip)).String()
}
func hexString(b []byte) string {
return nn.String() + "/" + itoa.Uitoa(uint(l))
}
-// Parse IPv4 address (d.d.d.d).
-func parseIPv4(s string) IP {
- var p [IPv4len]byte
- for i := 0; i < IPv4len; i++ {
- if len(s) == 0 {
- // Missing octets.
- return nil
- }
- if i > 0 {
- if s[0] != '.' {
- return nil
- }
- s = s[1:]
- }
- n, c, ok := dtoi(s)
- if !ok || n > 0xFF {
- return nil
- }
- if c > 1 && s[0] == '0' {
- // Reject non-zero components with leading zeroes.
- return nil
- }
- s = s[c:]
- p[i] = byte(n)
- }
- if len(s) != 0 {
- return nil
- }
- return IPv4(p[0], p[1], p[2], p[3])
-}
-
-// parseIPv6Zone parses s as a literal IPv6 address and its associated zone
-// identifier which is described in RFC 4007.
-func parseIPv6Zone(s string) (IP, string) {
- s, zone := splitHostZone(s)
- return parseIPv6(s), zone
-}
-
-// parseIPv6 parses s as a literal IPv6 address described in RFC 4291
-// and RFC 5952.
-func parseIPv6(s string) (ip IP) {
- ip = make(IP, IPv6len)
- ellipsis := -1 // position of ellipsis in ip
-
- // Might have leading ellipsis
- if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
- ellipsis = 0
- s = s[2:]
- // Might be only ellipsis
- if len(s) == 0 {
- return ip
- }
- }
-
- // Loop, parsing hex numbers followed by colon.
- i := 0
- for i < IPv6len {
- // Hex number.
- n, c, ok := xtoi(s)
- if !ok || n > 0xFFFF {
- return nil
- }
-
- // If followed by dot, might be in trailing IPv4.
- if c < len(s) && s[c] == '.' {
- if ellipsis < 0 && i != IPv6len-IPv4len {
- // Not the right place.
- return nil
- }
- if i+IPv4len > IPv6len {
- // Not enough room.
- return nil
- }
- ip4 := parseIPv4(s)
- if ip4 == nil {
- return nil
- }
- ip[i] = ip4[12]
- ip[i+1] = ip4[13]
- ip[i+2] = ip4[14]
- ip[i+3] = ip4[15]
- s = ""
- i += IPv4len
- break
- }
-
- // Save this 16-bit chunk.
- ip[i] = byte(n >> 8)
- ip[i+1] = byte(n)
- i += 2
-
- // Stop at end of string.
- s = s[c:]
- if len(s) == 0 {
- break
- }
-
- // Otherwise must be followed by colon and more.
- if s[0] != ':' || len(s) == 1 {
- return nil
- }
- s = s[1:]
-
- // Look for ellipsis.
- if s[0] == ':' {
- if ellipsis >= 0 { // already have one
- return nil
- }
- ellipsis = i
- s = s[1:]
- if len(s) == 0 { // can be at end
- break
- }
- }
- }
-
- // Must have used entire string.
- if len(s) != 0 {
- return nil
- }
-
- // If didn't parse enough, expand ellipsis.
- if i < IPv6len {
- if ellipsis < 0 {
- return nil
- }
- n := IPv6len - i
- for j := i - 1; j >= ellipsis; j-- {
- ip[j+n] = ip[j]
- }
- for j := ellipsis + n - 1; j >= ellipsis; j-- {
- ip[j] = 0
- }
- } else if ellipsis >= 0 {
- // Ellipsis must represent at least one 0 group.
- return nil
- }
- return ip
-}
-
// ParseIP parses s as an IP address, returning the result.
// The string s can be in IPv4 dotted decimal ("192.0.2.1"), IPv6
// ("2001:db8::68"), or IPv4-mapped IPv6 ("::ffff:192.0.2.1") form.
// If s is not a valid textual representation of an IP address,
// ParseIP returns nil.
func ParseIP(s string) IP {
- for i := 0; i < len(s); i++ {
- switch s[i] {
- case '.':
- return parseIPv4(s)
- case ':':
- return parseIPv6(s)
- }
+ if addr, valid := parseIP(s); valid {
+ return IP(addr[:])
}
return nil
}
-// parseIPZone parses s as an IP address, return it and its associated zone
-// identifier (IPv6 only).
-func parseIPZone(s string) (IP, string) {
- for i := 0; i < len(s); i++ {
- switch s[i] {
- case '.':
- return parseIPv4(s), ""
- case ':':
- return parseIPv6Zone(s)
- }
+func parseIP(s string) ([16]byte, bool) {
+ ip, err := netip.ParseAddr(s)
+ if err != nil || ip.Zone() != "" {
+ return [16]byte{}, false
}
- return nil, ""
+ return ip.As16(), true
}
// ParseCIDR parses s as a CIDR notation IP address and prefix length,
return nil, nil, &ParseError{Type: "CIDR address", Text: s}
}
addr, mask := s[:i], s[i+1:]
- iplen := IPv4len
- ip := parseIPv4(addr)
- if ip == nil {
- iplen = IPv6len
- ip = parseIPv6(addr)
+
+ ipAddr, err := netip.ParseAddr(addr)
+ if err != nil || ipAddr.Zone() != "" {
+ return nil, nil, &ParseError{Type: "CIDR address", Text: s}
}
+
n, i, ok := dtoi(mask)
- if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
+ if !ok || i != len(mask) || n < 0 || n > ipAddr.BitLen() {
return nil, nil, &ParseError{Type: "CIDR address", Text: s}
}
- m := CIDRMask(n, 8*iplen)
- return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil
+ m := CIDRMask(n, ipAddr.BitLen())
+ addr16 := ipAddr.As16()
+ return IP(addr16[:]), &IPNet{IP: IP(addr16[:]).Mask(m), Mask: m}, nil
}
func copyIP(x IP) IP {
// It returns a slice of that host's addresses.
func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
// Make sure that no matter what we do later, host=="" is rejected.
- // parseIP, for example, does accept empty strings.
if host == "" {
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
}
- if ip, _ := parseIPZone(host); ip != nil {
+ if _, err := netip.ParseAddr(host); err == nil {
return []string{host}, nil
}
return r.lookupHost(ctx, host)
// It returns a slice of that host's IPv4 and IPv6 addresses.
func (r *Resolver) lookupIPAddr(ctx context.Context, network, host string) ([]IPAddr, error) {
// Make sure that no matter what we do later, host=="" is rejected.
- // parseIPZone, for example, does accept empty strings.
if host == "" {
return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
}
- if ip, zone := parseIPZone(host); ip != nil {
- return []IPAddr{{IP: ip, Zone: zone}}, nil
+ if ip, err := netip.ParseAddr(host); err == nil {
+ return []IPAddr{{IP: IP(ip.AsSlice()).To16(), Zone: ip.Zone()}}, nil
}
trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
if trace != nil && trace.DNSStart != nil {