| // 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 |
| } |