227 lines
4.8 KiB
Go
227 lines
4.8 KiB
Go
// 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
|
|
}
|