| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // IP address manipulations | |
| // | |
| // IPv4 addresses are 4 bytes; IPv6 addresses are 16 bytes. | |
| // An IPv4 address can be converted to an IPv6 address by | |
| // adding a canonical prefix (10 zeros, 2 0xFFs). | |
| // This library accepts either size of byte slice but always | |
| // returns 16-byte addresses. | |
| package ipaddr | |
| import ( | |
| stdnet "net" | |
| ) | |
| // | |
| // Lean on the standard net lib as much as possible. | |
| // | |
| type IP = stdnet.IP | |
| type IPNet = stdnet.IPNet | |
| type ParseError = stdnet.ParseError | |
| const IPv4len = stdnet.IPv4len | |
| const IPv6len = stdnet.IPv6len | |
| var CIDRMask = stdnet.CIDRMask | |
| var IPv4 = stdnet.IPv4 | |
| // 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 | |
| } | |
| // | |
| // NOTE: This correct check was added for go-1.17, but is a | |
| // backwards-incompatible change for Terraform users, who might have | |
| // already written modules with leading zeroes. | |
| // | |
| //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]) | |
| } | |
| // 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) | |
| } | |
| } | |
| return nil | |
| } | |
| // ParseCIDR parses s as a CIDR notation IP address and prefix length, | |
| // like "192.0.2.0/24" or "2001:db8::/32", as defined in | |
| // RFC 4632 and RFC 4291. | |
| // | |
| // It returns the IP address and the network implied by the IP and | |
| // prefix length. | |
| // For example, ParseCIDR("192.0.2.1/24") returns the IP address | |
| // 192.0.2.1 and the network 192.0.2.0/24. | |
| func ParseCIDR(s string) (IP, *IPNet, error) { | |
| i := indexByteString(s, '/') | |
| if i < 0 { | |
| 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) | |
| } | |
| n, i, ok := dtoi(mask) | |
| if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen { | |
| return nil, nil, &ParseError{Type: "CIDR address", Text: s} | |
| } | |
| m := CIDRMask(n, 8*iplen) | |
| return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil | |
| } | |
| // This is copied from go/src/internal/bytealg, which includes versions | |
| // optimized for various platforms. Those optimizations are elided here so we | |
| // don't have to maintain them. | |
| func indexByteString(s string, c byte) int { | |
| for i := 0; i < len(s); i++ { | |
| if s[i] == c { | |
| return i | |
| } | |
| } | |
| return -1 | |
| } |