auth/vendor/inet.af/netaddr/netaddr.go

1920 lines
53 KiB
Go

// Copyright 2020 The Inet.Af AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package netaddr contains a IP address type that's in many ways
// better than the Go standard library's net.IP type. Building on that
// IP type, the package also contains IPPrefix, IPPort, IPRange, and
// IPSet types.
//
// Notably, this package's IP type takes less memory, is immutable,
// comparable (supports == and being a map key), and more. See
// https://github.com/inetaf/netaddr for background.
//
// IPv6 Zones
//
// IP and IPPort are the only types in this package that support IPv6
// zones. Other types silently drop any passed-in zones.
package netaddr // import "inet.af/netaddr"
import (
"encoding/binary"
"errors"
"fmt"
"math"
"net"
"sort"
"strconv"
"strings"
"go4.org/intern"
)
// Sizes: (64-bit)
// net.IP: 24 byte slice header + {4, 16} = 28 to 40 bytes
// net.IPAddr: 40 byte slice header + {4, 16} = 44 to 56 bytes + zone length
// netaddr.IP: 24 bytes (zone is per-name singleton, shared across all users)
// IP represents an IPv4 or IPv6 address (with or without a scoped
// addressing zone), similar to Go's net.IP or net.IPAddr.
//
// Unlike net.IP or net.IPAddr, the netaddr.IP is a comparable value
// type (it supports == and can be a map key) and is immutable.
// Its memory representation is 24 bytes on 64-bit machines (the same
// size as a Go slice header) for both IPv4 and IPv6 address.
type IP struct {
// addr are the hi and lo bits of an IPv6 address. If z==z4,
// hi and lo contain the IPv4-mapped IPv6 address.
//
// hi and lo are constructed by interpreting a 16-byte IPv6
// address as a big-endian 128-bit number. The most significant
// bits of that number go into hi, the rest into lo.
//
// For example, 0011:2233:4455:6677:8899:aabb:ccdd:eeff is stored as:
// addr.hi = 0x0011223344556677
// addr.lo = 0x8899aabbccddeeff
//
// We store IPs like this, rather than as [16]byte, because it
// turns most operations on IPs into arithmetic and bit-twiddling
// operations on 64-bit registers, which is much faster than
// bytewise processing.
addr uint128
// z is a combination of the address family and the IPv6 zone.
//
// nil means invalid IP address (for the IP zero value).
// z4 means an IPv4 address.
// z6noz means an IPv6 address without a zone.
//
// Otherwise it's the interned zone name string.
z *intern.Value
}
// z0, z4, and z6noz are sentinel IP.z values.
// See the IP type's field docs.
var (
z0 = (*intern.Value)(nil)
z4 = new(intern.Value)
z6noz = new(intern.Value)
)
// IPv6LinkLocalAllNodes returns the IPv6 link-local all nodes multicast
// address ff02::1.
func IPv6LinkLocalAllNodes() IP { return IPv6Raw([16]byte{0: 0xff, 1: 0x02, 15: 0x01}) }
// IPv6Unspecified returns the IPv6 unspecified address ::.
func IPv6Unspecified() IP { return IP{z: z6noz} }
// IPv4 returns the IP of the IPv4 address a.b.c.d.
func IPv4(a, b, c, d uint8) IP {
return IP{
addr: uint128{0, 0xffff00000000 | uint64(a)<<24 | uint64(b)<<16 | uint64(c)<<8 | uint64(d)},
z: z4,
}
}
// IPv6Raw returns the IPv6 address given by the bytes in addr,
// without an implicit Unmap call to unmap any v6-mapped IPv4
// address.
func IPv6Raw(addr [16]byte) IP {
return IP{
addr: uint128{
binary.BigEndian.Uint64(addr[:8]),
binary.BigEndian.Uint64(addr[8:]),
},
z: z6noz,
}
}
// ipv6Slice is like IPv6Raw, but operates on a 16-byte slice. Assumes
// slice is 16 bytes, caller must enforce this.
func ipv6Slice(addr []byte) IP {
return IP{
addr: uint128{
binary.BigEndian.Uint64(addr[:8]),
binary.BigEndian.Uint64(addr[8:]),
},
z: z6noz,
}
}
// IPFrom16 returns the IP address given by the bytes in addr,
// unmapping any v6-mapped IPv4 address.
//
// It is equivalent to calling IPv6Raw(addr).Unmap().
func IPFrom16(addr [16]byte) IP {
return IPv6Raw(addr).Unmap()
}
// IPFrom4 returns the IPv4 address given by the bytes in addr.
// It is equivalent to calling IPv4(addr[0], addr[1], addr[2], addr[3]).
func IPFrom4(addr [4]byte) IP {
return IPv4(addr[0], addr[1], addr[2], addr[3])
}
// ParseIP parses s as an IP address, returning the result. The string
// s can be in dotted decimal ("192.0.2.1"), IPv6 ("2001:db8::68"),
// or IPv6 with a scoped addressing zone ("fe80::1cc0:3e8c:119f:c2e1%ens18").
func ParseIP(s string) (IP, error) {
for i := 0; i < len(s); i++ {
switch s[i] {
case '.':
return parseIPv4(s)
case ':':
return parseIPv6(s)
case '%':
// Assume that this was trying to be an IPv6 address with
// a zone specifier, but the address is missing.
return IP{}, parseIPError{in: s, msg: "missing IPv6 address"}
}
}
return IP{}, parseIPError{in: s, msg: "unable to parse IP"}
}
// MustParseIP calls ParseIP(s) and panics on error.
// It is intended for use in tests with hard-coded strings.
func MustParseIP(s string) IP {
ip, err := ParseIP(s)
if err != nil {
panic(err)
}
return ip
}
type parseIPError struct {
in string // the string given to ParseIP
msg string // an explanation of the parse failure
at string // optionally, the unparsed portion of in at which the error occurred.
}
func (err parseIPError) Error() string {
if err.at != "" {
return fmt.Sprintf("ParseIP(%q): %s (at %q)", err.in, err.msg, err.at)
}
return fmt.Sprintf("ParseIP(%q): %s", err.in, err.msg)
}
// parseIPv4 parses s as an IPv4 address (in form "192.168.0.1").
func parseIPv4(s string) (ip IP, err error) {
var fields [3]uint8
var val, pos int
for i := 0; i < len(s); i++ {
if s[i] >= '0' && s[i] <= '9' {
val = val*10 + int(s[i]) - '0'
if val > 255 {
return IP{}, parseIPError{in: s, msg: "IPv4 field has value >255"}
}
} else if s[i] == '.' {
// .1.2.3
// 1.2.3.
// 1..2.3
if i == 0 || i == len(s)-1 || s[i-1] == '.' {
return IP{}, parseIPError{in: s, msg: "IPv4 field must have at least one digit", at: s[i:]}
}
// 1.2.3.4.5
if pos == 3 {
return IP{}, parseIPError{in: s, msg: "IPv4 address too long"}
}
fields[pos] = uint8(val)
pos++
val = 0
} else {
return IP{}, parseIPError{in: s, msg: "unexpected character", at: s[i:]}
}
}
if pos < 3 {
return IP{}, parseIPError{in: s, msg: "IPv4 address too short"}
}
return IPv4(fields[0], fields[1], fields[2], uint8(val)), nil
}
// parseIPv6 parses s as an IPv6 address (in form "2001:db8::68").
func parseIPv6(in string) (IP, error) {
s := in
// Split off the zone right from the start. Yes it's a second scan
// of the string, but trying to handle it inline makes a bunch of
// other inner loop conditionals more expensive, and it ends up
// being slower.
zone := ""
i := strings.IndexByte(s, '%')
if i != -1 {
s, zone = s[:i], s[i+1:]
if zone == "" {
// Not allowed to have an empty zone if explicitly specified.
return IP{}, parseIPError{in: in, msg: "zone must be a non-empty string"}
}
}
var ip [16]byte
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 IPv6Unspecified().WithZone(zone), nil
}
}
// Loop, parsing hex numbers followed by colon.
i = 0
for i < 16 {
// Hex number. Similar to parseIPv4, inlining the hex number
// parsing yields a significant performance increase.
off := 0
acc := uint32(0)
for ; off < len(s); off++ {
c := s[off]
if c >= '0' && c <= '9' {
acc = (acc << 4) + uint32(c-'0')
} else if c >= 'a' && c <= 'f' {
acc = (acc << 4) + uint32(c-'a'+10)
} else if c >= 'A' && c <= 'F' {
acc = (acc << 4) + uint32(c-'A'+10)
} else {
break
}
if acc > math.MaxUint16 {
// Overflow, fail.
return IP{}, parseIPError{in: in, msg: "IPv6 field has value >=2^16", at: s}
}
}
if off == 0 {
// No digits found, fail.
return IP{}, parseIPError{in: in, msg: "each colon-separated field must have at least one digit", at: s}
}
// If followed by dot, might be in trailing IPv4.
if off < len(s) && s[off] == '.' {
if ellipsis < 0 && i != 12 {
// Not the right place.
return IP{}, parseIPError{in: in, msg: "embedded IPv4 address must replace the final 2 fields of the address", at: s}
}
if i+4 > 16 {
// Not enough room.
return IP{}, parseIPError{in: in, msg: "too many hex fields to fit an embedded IPv4 at the end of the address", at: s}
}
// TODO: could make this a bit faster by having a helper
// that parses to a [4]byte, and have both parseIPv4 and
// parseIPv6 use it.
ip4, err := parseIPv4(s)
if err != nil {
return IP{}, parseIPError{in: in, msg: err.Error(), at: s}
}
ip[i] = ip4.v4(0)
ip[i+1] = ip4.v4(1)
ip[i+2] = ip4.v4(2)
ip[i+3] = ip4.v4(3)
s = ""
i += 4
break
}
// Save this 16-bit chunk.
ip[i] = byte(acc >> 8)
ip[i+1] = byte(acc)
i += 2
// Stop at end of string.
s = s[off:]
if len(s) == 0 {
break
}
// Otherwise must be followed by colon and more.
if s[0] != ':' {
return IP{}, parseIPError{in: in, msg: "unexpected character, want colon", at: s}
} else if len(s) == 1 {
return IP{}, parseIPError{in: in, msg: "colon must be followed by more characters", at: s}
}
s = s[1:]
// Look for ellipsis.
if s[0] == ':' {
if ellipsis >= 0 { // already have one
return IP{}, parseIPError{in: in, msg: "multiple :: in address", at: s}
}
ellipsis = i
s = s[1:]
if len(s) == 0 { // can be at end
break
}
}
}
// Must have used entire string.
if len(s) != 0 {
return IP{}, parseIPError{in: in, msg: "trailing garbage after address", at: s}
}
// If didn't parse enough, expand ellipsis.
if i < 16 {
if ellipsis < 0 {
return IP{}, parseIPError{in: in, msg: "address string too short"}
}
n := 16 - 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 IP{}, parseIPError{in: in, msg: "the :: must expand to at least one field of zeros"}
}
return IPv6Raw(ip).WithZone(zone), nil
}
// FromStdIP returns an IP from the standard library's IP type.
//
// If std is invalid, ok is false.
//
// FromStdIP implicitly unmaps IPv6-mapped IPv4 addresses. That is, if
// len(std) == 16 and contains an IPv4 address, only the IPv4 part is
// returned, without the IPv6 wrapper. This is the common form returned by
// the standard library's ParseIP: https://play.golang.org/p/qdjylUkKWxl.
// To convert a standard library IP without the implicit unmapping, use
// FromStdIPRaw.
func FromStdIP(std net.IP) (ip IP, ok bool) {
ret, ok := FromStdIPRaw(std)
if ret.Is4in6() {
ret.z = z4
}
return ret, ok
}
// FromStdIPRaw returns an IP from the standard library's IP type.
// If std is invalid, ok is false.
// Unlike FromStdIP, FromStdIPRaw does not do an implicit Unmap if
// len(std) == 16 and contains an IPv6-mapped IPv4 address.
func FromStdIPRaw(std net.IP) (ip IP, ok bool) {
switch len(std) {
case 4:
return IPv4(std[0], std[1], std[2], std[3]), true
case 16:
return ipv6Slice(std), true
}
return IP{}, false
}
// v4 returns the i'th byte of ip. If ip is not an IPv4, v4 returns
// unspecified garbage.
func (ip IP) v4(i uint8) uint8 {
return uint8(ip.addr.lo >> ((3 - i) * 8))
}
// v6 returns the i'th byte of ip. If ip is an IPv4 address, this
// accesses the IPv4-mapped IPv6 address form of the IP.
func (ip IP) v6(i uint8) uint8 {
return uint8(*(ip.addr.halves()[(i/8)%2]) >> ((7 - i%8) * 8))
}
// v6u16 returns the i'th 16-bit word of ip. If ip is an IPv4 address,
// this accesses the IPv4-mapped IPv6 address form of the IP.
func (ip IP) v6u16(i uint8) uint16 {
return uint16(*(ip.addr.halves()[(i/4)%2]) >> ((3 - i%4) * 16))
}
// IsZero reports whether ip is the zero value of the IP type.
// The zero value is not a valid IP address of any type.
//
// Note that "0.0.0.0" and "::" are not the zero value. Use IsUnspecified to
// check for these values instead.
func (ip IP) IsZero() bool {
// Faster than comparing ip == IP{}, but effectively equivalent,
// as there's no way to make an IP with a nil z from this package.
return ip.z == z0
}
// IsValid whether the IP is an initialized value and not the IP
// type's zero value.
//
// Note that both "0.0.0.0" and "::" are valid, non-zero values.
func (ip IP) IsValid() bool { return ip.z != z0 }
// BitLen returns the number of bits in the IP address:
// 32 for IPv4 or 128 for IPv6.
// For the zero value (see IP.IsZero), it returns 0.
// For IP4-mapped IPv6 addresses, it returns 128.
func (ip IP) BitLen() uint8 {
switch ip.z {
case z0:
return 0
case z4:
return 32
}
return 128
}
// Zone returns ip's IPv6 scoped addressing zone, if any.
func (ip IP) Zone() string {
if ip.z == nil {
return ""
}
zone, _ := ip.z.Get().(string)
return zone
}
// Compare returns an integer comparing two IPs.
// The result will be 0 if ip==ip2, -1 if ip < ip2, and +1 if ip > ip2.
// The definition of "less than" is the same as the IP.Less method.
func (ip IP) Compare(ip2 IP) int {
f1, f2 := ip.BitLen(), ip2.BitLen()
if f1 < f2 {
return -1
}
if f1 > f2 {
return 1
}
if hi1, hi2 := ip.addr.hi, ip2.addr.hi; hi1 < hi2 {
return -1
} else if hi1 > hi2 {
return 1
}
if lo1, lo2 := ip.addr.lo, ip2.addr.lo; lo1 < lo2 {
return -1
} else if lo1 > lo2 {
return 1
}
if ip.Is6() {
za, zb := ip.Zone(), ip2.Zone()
if za < zb {
return -1
} else if za > zb {
return 1
}
}
return 0
}
// Less reports whether ip sorts before ip2.
// IP addresses sort first by length, then their address.
// IPv6 addresses with zones sort just after the same address without a zone.
func (ip IP) Less(ip2 IP) bool { return ip.Compare(ip2) == -1 }
func (ip IP) lessOrEq(ip2 IP) bool { return ip.Compare(ip2) <= 0 }
// ipZone returns the standard library net.IP from ip, as well
// as the zone.
// The optional reuse IP provides memory to reuse.
func (ip IP) ipZone(reuse net.IP) (stdIP net.IP, zone string) {
base := reuse[:0]
switch {
case ip.z == z0:
return nil, ""
case ip.Is4():
a4 := ip.As4()
return append(base, a4[:]...), ""
default:
a16 := ip.As16()
return append(base, a16[:]...), ip.Zone()
}
}
// IPAddr returns the net.IPAddr representation of an IP. The returned value is
// always non-nil, but the IPAddr.IP will be nil if ip is the zero value.
// If ip contains a zone identifier, IPAddr.Zone is populated.
func (ip IP) IPAddr() *net.IPAddr {
stdIP, zone := ip.ipZone(nil)
return &net.IPAddr{IP: stdIP, Zone: zone}
}
// Is4 reports whether ip is an IPv4 address.
//
// It returns false for IP4-mapped IPv6 addresses. See IP.Unmap.
func (ip IP) Is4() bool {
return ip.z == z4
}
// Is4in6 reports whether ip is an IPv4-mapped IPv6 address.
func (ip IP) Is4in6() bool {
return ip.Is6() && ip.addr.hi == 0 && ip.addr.lo>>32 == 0xffff
}
// Is6 reports whether ip is an IPv6 address, including IPv4-mapped
// IPv6 addresses.
func (ip IP) Is6() bool {
return ip.z != z0 && ip.z != z4
}
// Unmap returns ip with any IPv4-mapped IPv6 address prefix removed.
//
// That is, if ip is an IPv6 address wrapping an IPv4 adddress, it
// returns the wrapped IPv4 address. Otherwise it returns ip, regardless
// of its type.
func (ip IP) Unmap() IP {
if ip.Is4in6() {
ip.z = z4
}
return ip
}
// WithZone returns an IP that's the same as ip but with the provided
// zone. If zone is empty, the zone is removed. If ip is an IPv4
// address it's returned unchanged.
func (ip IP) WithZone(zone string) IP {
if !ip.Is6() {
return ip
}
if zone == "" {
ip.z = z6noz
return ip
}
ip.z = intern.GetByString(zone)
return ip
}
// noZone unconditionally strips the zone from IP.
// It's similar to WithZone, but small enough to be inlinable.
func (ip IP) withoutZone() IP {
if !ip.Is6() {
return ip
}
ip.z = z6noz
return ip
}
// hasZone reports whether IP has an IPv6 zone.
func (ip IP) hasZone() bool {
return ip.z != z0 && ip.z != z4 && ip.z != z6noz
}
// IsLinkLocalUnicast reports whether ip is a link-local unicast address.
// If ip is the zero value, it will return false.
func (ip IP) IsLinkLocalUnicast() bool {
// Dynamic Configuration of IPv4 Link-Local Addresses
// https://datatracker.ietf.org/doc/html/rfc3927#section-2.1
if ip.Is4() {
return ip.v4(0) == 169 && ip.v4(1) == 254
}
// IP Version 6 Addressing Architecture (2.4 Address Type Identification)
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.4
if ip.Is6() {
return ip.v6u16(0)&0xffc0 == 0xfe80
}
return false // zero value
}
// IsLoopback reports whether ip is a loopback address. If ip is the zero value,
// it will return false.
func (ip IP) IsLoopback() bool {
// Requirements for Internet Hosts -- Communication Layers (3.2.1.3 Addressing)
// https://datatracker.ietf.org/doc/html/rfc1122#section-3.2.1.3
if ip.Is4() {
return ip.v4(0) == 127
}
// IP Version 6 Addressing Architecture (2.4 Address Type Identification)
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.4
if ip.Is6() {
return ip.addr.hi == 0 && ip.addr.lo == 1
}
return false // zero value
}
// IsMulticast reports whether ip is a multicast address. If ip is the zero
// value, it will return false.
func (ip IP) IsMulticast() bool {
// Host Extensions for IP Multicasting (4. HOST GROUP ADDRESSES)
// https://datatracker.ietf.org/doc/html/rfc1112#section-4
if ip.Is4() {
return ip.v4(0)&0xf0 == 0xe0
}
// IP Version 6 Addressing Architecture (2.4 Address Type Identification)
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.4
if ip.Is6() {
return ip.addr.hi>>(64-8) == 0xff // ip.v6(0) == 0xff
}
return false // zero value
}
// IsInterfaceLocalMulticast reports whether ip is an IPv6 interface-local
// multicast address. If ip is the zero value or an IPv4 address, it will return
// false.
func (ip IP) IsInterfaceLocalMulticast() bool {
// IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses)
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1
if ip.Is6() {
return ip.v6u16(0)&0xff0f == 0xff01
}
return false // zero value
}
// IsLinkLocalMulticast reports whether ip is a link-local multicast address.
// If ip is the zero value, it will return false.
func (ip IP) IsLinkLocalMulticast() bool {
// IPv4 Multicast Guidelines (4. Local Network Control Block (224.0.0/24))
// https://datatracker.ietf.org/doc/html/rfc5771#section-4
if ip.Is4() {
return ip.v4(0) == 224 && ip.v4(1) == 0 && ip.v4(2) == 0
}
// IPv6 Addressing Architecture (2.7.1. Pre-Defined Multicast Addresses)
// https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1
if ip.Is6() {
return ip.v6u16(0)&0xff0f == 0xff02
}
return false // zero value
}
// IsGlobalUnicast reports whether ip is a global unicast address.
//
// It returns true for IPv6 addresses which fall outside of the current
// IANA-allocated 2000::/3 global unicast space, with the exception of the
// link-local address space. It also returns true even if ip is in the IPv4
// private address space or IPv6 unique local address space. If ip is the zero
// value, it will return false.
//
// For reference, see RFC 1122, RFC 4291, and RFC 4632.
func (ip IP) IsGlobalUnicast() bool {
if ip.z == z0 {
// Invalid or zero-value.
return false
}
// Match the stdlib's IsGlobalUnicast logic. Notably private IPv4 addresses
// and ULA IPv6 addresses are still considered "global unicast".
if ip.Is4() && (ip == IPv4(0, 0, 0, 0) || ip == IPv4(255, 255, 255, 255)) {
return false
}
return ip != IPv6Unspecified() &&
!ip.IsLoopback() &&
!ip.IsMulticast() &&
!ip.IsLinkLocalUnicast()
}
// IsPrivate reports whether ip is a private address, according to RFC 1918
// (IPv4 addresses) and RFC 4193 (IPv6 addresses). That is, it reports whether
// ip is in 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, or fc00::/7. This is the
// same as the standard library's net.IP.IsPrivate.
func (ip IP) IsPrivate() bool {
// Match the stdlib's IsPrivate logic.
if ip.Is4() {
// RFC 1918 allocates 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 as
// private IPv4 address subnets.
return ip.v4(0) == 10 ||
(ip.v4(0) == 172 && ip.v4(1)&0xf0 == 16) ||
(ip.v4(0) == 192 && ip.v4(1) == 168)
}
if ip.Is6() {
// RFC 4193 allocates fc00::/7 as the unique local unicast IPv6 address
// subnet.
return ip.v6(0)&0xfe == 0xfc
}
return false // zero value
}
// IsUnspecified reports whether ip is an unspecified address, either the IPv4
// address "0.0.0.0" or the IPv6 address "::".
//
// Note that the IP zero value is not an unspecified address. Use IsZero to
// check for the zero value instead.
func (ip IP) IsUnspecified() bool {
return ip == IPv4(0, 0, 0, 0) || ip == IPv6Unspecified()
}
// Prefix applies a CIDR mask of leading bits to IP, producing an IPPrefix
// of the specified length. If IP is the zero value, a zero-value IPPrefix and
// a nil error are returned. If bits is larger than 32 for an IPv4 address or
// 128 for an IPv6 address, an error is returned.
func (ip IP) Prefix(bits uint8) (IPPrefix, error) {
effectiveBits := bits
switch ip.z {
case z0:
return IPPrefix{}, nil
case z4:
if bits > 32 {
return IPPrefix{}, fmt.Errorf("prefix length %d too large for IPv4", bits)
}
effectiveBits += 96
default:
if bits > 128 {
return IPPrefix{}, fmt.Errorf("prefix length %d too large for IPv6", bits)
}
}
ip.addr = ip.addr.and(mask6[effectiveBits])
return IPPrefixFrom(ip, bits), nil
}
// Netmask applies a bit mask to IP, producing an IPPrefix. If IP is the
// zero value, a zero-value IPPrefix and a nil error are returned. If the
// netmask length is not 4 for IPv4 or 16 for IPv6, an error is
// returned. If the netmask is non-contiguous, an error is returned.
func (ip IP) Netmask(mask []byte) (IPPrefix, error) {
l := len(mask)
switch ip.z {
case z0:
return IPPrefix{}, nil
case z4:
if l != net.IPv4len {
return IPPrefix{}, fmt.Errorf("netmask length %d incorrect for IPv4", l)
}
default:
if l != net.IPv6len {
return IPPrefix{}, fmt.Errorf("netmask length %d incorrect for IPv6", l)
}
}
ones, bits := net.IPMask(mask).Size()
if ones == 0 && bits == 0 {
return IPPrefix{}, errors.New("netmask is non-contiguous")
}
return ip.Prefix(uint8(ones))
}
// As16 returns the IP address in its 16 byte representation.
// IPv4 addresses are returned in their v6-mapped form.
// IPv6 addresses with zones are returned without their zone (use the
// Zone method to get it).
// The ip zero value returns all zeroes.
func (ip IP) As16() [16]byte {
var ret [16]byte
binary.BigEndian.PutUint64(ret[:8], ip.addr.hi)
binary.BigEndian.PutUint64(ret[8:], ip.addr.lo)
return ret
}
// As4 returns an IPv4 or IPv4-in-IPv6 address in its 4 byte representation.
// If ip is the IP zero value or an IPv6 address, As4 panics.
// Note that 0.0.0.0 is not the zero value.
func (ip IP) As4() [4]byte {
if ip.z == z4 || ip.Is4in6() {
var ret [4]byte
binary.BigEndian.PutUint32(ret[:], uint32(ip.addr.lo))
return ret
}
if ip.z == z0 {
panic("As4 called on IP zero value")
}
panic("As4 called on IPv6 address")
}
// Next returns the IP following ip.
// If there is none, it returns the IP zero value.
func (ip IP) Next() IP {
ip.addr = ip.addr.addOne()
if ip.Is4() {
if uint32(ip.addr.lo) == 0 {
// Overflowed.
return IP{}
}
} else {
if ip.addr.isZero() {
// Overflowed
return IP{}
}
}
return ip
}
// Prior returns the IP before ip.
// If there is none, it returns the IP zero value.
func (ip IP) Prior() IP {
if ip.Is4() {
if uint32(ip.addr.lo) == 0 {
return IP{}
}
} else if ip.addr.isZero() {
return IP{}
}
ip.addr = ip.addr.subOne()
return ip
}
// String returns the string form of the IP address ip.
// It returns one of 4 forms:
//
// - "invalid IP", if ip is the zero value
// - IPv4 dotted decimal ("192.0.2.1")
// - IPv6 ("2001:db8::1")
// - IPv6 with zone ("fe80:db8::1%eth0")
//
// Note that unlike the Go standard library's IP.String method,
// IP4-mapped IPv6 addresses do not format as dotted decimals.
func (ip IP) String() string {
switch ip.z {
case z0:
return "zero IP"
case z4:
return ip.string4()
default:
return ip.string6()
}
}
// AppendTo appends a text encoding of ip,
// as generated by MarshalText,
// to b and returns the extended buffer.
func (ip IP) AppendTo(b []byte) []byte {
switch ip.z {
case z0:
return b
case z4:
return ip.appendTo4(b)
default:
return ip.appendTo6(b)
}
}
// digits is a string of the hex digits from 0 to f. It's used in
// appendDecimal and appendHex to format IP addresses.
const digits = "0123456789abcdef"
// appendDecimal appends the decimal string representation of x to b.
func appendDecimal(b []byte, x uint8) []byte {
// Using this function rather than strconv.AppendUint makes IPv4
// string building 2x faster.
if x >= 100 {
b = append(b, digits[x/100])
}
if x >= 10 {
b = append(b, digits[x/10%10])
}
return append(b, digits[x%10])
}
// appendHex appends the hex string representation of x to b.
func appendHex(b []byte, x uint16) []byte {
// Using this function rather than strconv.AppendUint makes IPv6
// string building 2x faster.
if x >= 0x1000 {
b = append(b, digits[x>>12])
}
if x >= 0x100 {
b = append(b, digits[x>>8&0xf])
}
if x >= 0x10 {
b = append(b, digits[x>>4&0xf])
}
return append(b, digits[x&0xf])
}
// appendHexPad appends the fully padded hex string representation of x to b.
func appendHexPad(b []byte, x uint16) []byte {
return append(b, digits[x>>12], digits[x>>8&0xf], digits[x>>4&0xf], digits[x&0xf])
}
func (ip IP) string4() string {
const max = len("255.255.255.255")
ret := make([]byte, 0, max)
ret = ip.appendTo4(ret)
return string(ret)
}
func (ip IP) appendTo4(ret []byte) []byte {
ret = appendDecimal(ret, ip.v4(0))
ret = append(ret, '.')
ret = appendDecimal(ret, ip.v4(1))
ret = append(ret, '.')
ret = appendDecimal(ret, ip.v4(2))
ret = append(ret, '.')
ret = appendDecimal(ret, ip.v4(3))
return ret
}
// string6 formats ip in IPv6 textual representation. It follows the
// guidelines in section 4 of RFC 5952
// (https://tools.ietf.org/html/rfc5952#section-4): no unnecessary
// zeros, use :: to elide the longest run of zeros, and don't use ::
// to compact a single zero field.
func (ip IP) string6() string {
// Use a zone with a "plausibly long" name, so that most zone-ful
// IP addresses won't require additional allocation.
//
// The compiler does a cool optimization here, where ret ends up
// stack-allocated and so the only allocation this function does
// is to construct the returned string. As such, it's okay to be a
// bit greedy here, size-wise.
const max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0")
ret := make([]byte, 0, max)
ret = ip.appendTo6(ret)
return string(ret)
}
func (ip IP) appendTo6(ret []byte) []byte {
zeroStart, zeroEnd := uint8(255), uint8(255)
for i := uint8(0); i < 8; i++ {
j := i
for j < 8 && ip.v6u16(j) == 0 {
j++
}
if l := j - i; l >= 2 && l > zeroEnd-zeroStart {
zeroStart, zeroEnd = i, j
}
}
for i := uint8(0); i < 8; i++ {
if i == zeroStart {
ret = append(ret, ':', ':')
i = zeroEnd
if i >= 8 {
break
}
} else if i > 0 {
ret = append(ret, ':')
}
ret = appendHex(ret, ip.v6u16(i))
}
if ip.z != z6noz {
ret = append(ret, '%')
ret = append(ret, ip.Zone()...)
}
return ret
}
// StringExpanded is like String but IPv6 addresses are expanded with leading
// zeroes and no "::" compression. For example, "2001:db8::1" becomes
// "2001:0db8:0000:0000:0000:0000:0000:0001".
func (ip IP) StringExpanded() string {
switch ip.z {
case z0, z4:
return ip.String()
}
const size = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
ret := make([]byte, 0, size)
for i := uint8(0); i < 8; i++ {
if i > 0 {
ret = append(ret, ':')
}
ret = appendHexPad(ret, ip.v6u16(i))
}
if ip.z != z6noz {
// The addition of a zone will cause a second allocation, but when there
// is no zone the ret slice will be stack allocated.
ret = append(ret, '%')
ret = append(ret, ip.Zone()...)
}
return string(ret)
}
// MarshalText implements the encoding.TextMarshaler interface,
// The encoding is the same as returned by String, with one exception:
// If ip is the zero value, the encoding is the empty string.
func (ip IP) MarshalText() ([]byte, error) {
switch ip.z {
case z0:
return []byte(""), nil
case z4:
max := len("255.255.255.255")
b := make([]byte, 0, max)
return ip.appendTo4(b), nil
default:
max := len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0")
b := make([]byte, 0, max)
return ip.appendTo6(b), nil
}
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The IP address is expected in a form accepted by ParseIP.
// It returns an error if *ip is not the IP zero value.
func (ip *IP) UnmarshalText(text []byte) error {
if ip.z != z0 {
return errors.New("refusing to Unmarshal into non-zero IP")
}
if len(text) == 0 {
return nil
}
var err error
*ip, err = ParseIP(string(text))
return err
}
// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (ip IP) MarshalBinary() ([]byte, error) {
switch ip.z {
case z0:
return nil, nil
case z4:
b := ip.As4()
return b[:], nil
default:
b16 := ip.As16()
b := b16[:]
if z := ip.Zone(); z != "" {
b = append(b, []byte(z)...)
}
return b, nil
}
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
func (ip *IP) UnmarshalBinary(b []byte) error {
if ip.z != z0 {
return errors.New("refusing to Unmarshal into non-zero IP")
}
n := len(b)
switch {
case n == 0:
return nil
case n == 4:
*ip = IPv4(b[0], b[1], b[2], b[3])
return nil
case n == 16:
*ip = ipv6Slice(b)
return nil
case n > 16:
*ip = ipv6Slice(b[:16]).WithZone(string(b[16:]))
return nil
}
return fmt.Errorf("unexpected ip size: %v", len(b))
}
// IPPort is an IP and a port number.
type IPPort struct {
ip IP
port uint16
}
// IPPortFrom returns an IPPort with IP ip and port port.
// It does not allocate.
func IPPortFrom(ip IP, port uint16) IPPort { return IPPort{ip: ip, port: port} }
// WithIP returns an IPPort with IP ip and port p.Port().
func (p IPPort) WithIP(ip IP) IPPort { return IPPort{ip: ip, port: p.port} }
// WithIP returns an IPPort with IP p.IP() and port port.
func (p IPPort) WithPort(port uint16) IPPort { return IPPort{ip: p.ip, port: port} }
// IP returns p's IP.
func (p IPPort) IP() IP { return p.ip }
// Port returns p's port.
func (p IPPort) Port() uint16 { return p.port }
// splitIPPort splits s into an IP address string and a port
// string. It splits strings shaped like "foo:bar" or "[foo]:bar",
// without further validating the substrings. v6 indicates whether the
// ip string should parse as an IPv6 address or an IPv4 address, in
// order for s to be a valid ip:port string.
func splitIPPort(s string) (ip, port string, v6 bool, err error) {
i := strings.LastIndexByte(s, ':')
if i == -1 {
return "", "", false, errors.New("not an ip:port")
}
ip, port = s[:i], s[i+1:]
if len(ip) == 0 {
return "", "", false, errors.New("no IP")
}
if len(port) == 0 {
return "", "", false, errors.New("no port")
}
if ip[0] == '[' {
if len(ip) < 2 || ip[len(ip)-1] != ']' {
return "", "", false, errors.New("missing ]")
}
ip = ip[1 : len(ip)-1]
v6 = true
}
return ip, port, v6, nil
}
// ParseIPPort parses s as an IPPort.
//
// It doesn't do any name resolution, and ports must be numeric.
func ParseIPPort(s string) (IPPort, error) {
var ipp IPPort
ip, port, v6, err := splitIPPort(s)
if err != nil {
return ipp, err
}
port16, err := strconv.ParseUint(port, 10, 16)
if err != nil {
return ipp, fmt.Errorf("invalid port %q parsing %q", port, s)
}
ipp.port = uint16(port16)
ipp.ip, err = ParseIP(ip)
if err != nil {
return IPPort{}, err
}
if v6 && ipp.ip.Is4() {
return IPPort{}, fmt.Errorf("invalid ip:port %q, square brackets can only be used with IPv6 addresses", s)
} else if !v6 && ipp.ip.Is6() {
return IPPort{}, fmt.Errorf("invalid ip:port %q, IPv6 addresses must be surrounded by square brackets", s)
}
return ipp, nil
}
// MustParseIPPort calls ParseIPPort(s) and panics on error.
// It is intended for use in tests with hard-coded strings.
func MustParseIPPort(s string) IPPort {
ip, err := ParseIPPort(s)
if err != nil {
panic(err)
}
return ip
}
// IsZero reports whether p is its zero value.
func (p IPPort) IsZero() bool { return p == IPPort{} }
// IsValid reports whether p.IP() is valid.
// All ports are valid, including zero.
func (p IPPort) IsValid() bool { return p.ip.IsValid() }
// Valid reports whether p.IP() is valid.
// All ports are valid, including zero.
//
// Deprecated: use the correctly named and identical IsValid method instead.
func (p IPPort) Valid() bool { return p.IsValid() }
func (p IPPort) String() string {
switch p.ip.z {
case z0:
return "invalid IPPort"
case z4:
a := p.ip.As4()
return fmt.Sprintf("%d.%d.%d.%d:%d", a[0], a[1], a[2], a[3], p.port)
default:
// TODO: this could be more efficient allocation-wise:
return net.JoinHostPort(p.ip.String(), strconv.Itoa(int(p.port)))
}
}
// AppendTo appends a text encoding of p,
// as generated by MarshalText,
// to b and returns the extended buffer.
func (p IPPort) AppendTo(b []byte) []byte {
switch p.ip.z {
case z0:
return b
case z4:
b = p.ip.appendTo4(b)
default:
b = append(b, '[')
b = p.ip.appendTo6(b)
b = append(b, ']')
}
b = append(b, ':')
b = strconv.AppendInt(b, int64(p.port), 10)
return b
}
// MarshalText implements the encoding.TextMarshaler interface. The
// encoding is the same as returned by String, with one exception: if
// p.IP() is the zero value, the encoding is the empty string.
func (p IPPort) MarshalText() ([]byte, error) {
var max int
switch p.ip.z {
case z0:
case z4:
max = len("255.255.255.255:65535")
default:
max = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535")
}
b := make([]byte, 0, max)
b = p.AppendTo(b)
return b, nil
}
// UnmarshalText implements the encoding.TextUnmarshaler
// interface. The IPPort is expected in a form accepted by
// ParseIPPort. It returns an error if *p is not the IPPort zero
// value.
func (p *IPPort) UnmarshalText(text []byte) error {
if p.ip.z != z0 || p.port != 0 {
return errors.New("refusing to Unmarshal into non-zero IPPort")
}
if len(text) == 0 {
return nil
}
var err error
*p, err = ParseIPPort(string(text))
return err
}
// FromStdAddr maps the components of a standard library TCPAddr or
// UDPAddr into an IPPort.
func FromStdAddr(stdIP net.IP, port int, zone string) (_ IPPort, ok bool) {
ip, ok := FromStdIP(stdIP)
if !ok || port < 0 || port > math.MaxUint16 {
return
}
ip = ip.Unmap()
if zone != "" {
if ip.Is4() {
ok = false
return
}
ip = ip.WithZone(zone)
}
return IPPort{ip: ip, port: uint16(port)}, true
}
// UDPAddr returns a standard library net.UDPAddr from p.
// The returned value is always non-nil. If p.IP() is the zero
// value, then UDPAddr.IP is nil.
//
// UDPAddr necessarily does two allocations. If you have an existing
// UDPAddr already allocated, see UDPAddrAt.
func (p IPPort) UDPAddr() *net.UDPAddr {
ret := &net.UDPAddr{
Port: int(p.port),
}
ret.IP, ret.Zone = p.ip.ipZone(nil)
return ret
}
// UDPAddrAt is like UDPAddr, but reuses the provided UDPAddr, which
// must be non-nil. If at.IP has a capacity of 16, UDPAddrAt is
// allocation-free. It returns at to facilitate using this method as a
// wrapper.
func (p IPPort) UDPAddrAt(at *net.UDPAddr) *net.UDPAddr {
at.Port = int(p.port)
at.IP, at.Zone = p.ip.ipZone(at.IP)
return at
}
// TCPAddr returns a standard library net.TCPAddr from p.
// The returned value is always non-nil. If p.IP() is the zero
// value, then TCPAddr.IP is nil.
func (p IPPort) TCPAddr() *net.TCPAddr {
ip, zone := p.ip.ipZone(nil)
return &net.TCPAddr{
IP: ip,
Port: int(p.port),
Zone: zone,
}
}
// IPPrefix is an IP address prefix (CIDR) representing an IP network.
//
// The first Bits() of IP() are specified. The remaining bits match any address.
// The range of Bits() is [0,32] for IPv4 or [0,128] for IPv6.
type IPPrefix struct {
ip IP
bits uint8
}
// IPPrefixFrom returns an IPPrefix with IP ip and provided bits prefix length.
// It does not allocate.
func IPPrefixFrom(ip IP, bits uint8) IPPrefix {
return IPPrefix{
ip: ip.withoutZone(),
bits: bits,
}
}
// IP returns p's IP.
func (p IPPrefix) IP() IP { return p.ip }
// Bits returns p's prefix length.
func (p IPPrefix) Bits() uint8 { return p.bits }
// IsValid reports whether whether p.Bits() has a valid range for p.IP().
// If p.IP() is zero, Valid returns false.
func (p IPPrefix) IsValid() bool { return !p.ip.IsZero() && p.bits <= p.ip.BitLen() }
// Valid reports whether whether p.Bits() has a valid range for p.IP().
// If p.IP() is zero, Valid returns false.
//
// Deprecated: use the correctly named and identical IsValid method instead.
func (p IPPrefix) Valid() bool { return p.IsValid() }
// IsZero reports whether p is its zero value.
func (p IPPrefix) IsZero() bool { return p == IPPrefix{} }
// IsSingleIP reports whether p contains exactly one IP.
func (p IPPrefix) IsSingleIP() bool { return p.bits != 0 && p.bits == p.ip.BitLen() }
// FromStdIPNet returns an IPPrefix from the standard library's IPNet type.
// If std is invalid, ok is false.
func FromStdIPNet(std *net.IPNet) (prefix IPPrefix, ok bool) {
ip, ok := FromStdIP(std.IP)
if !ok {
return IPPrefix{}, false
}
if l := len(std.Mask); l != net.IPv4len && l != net.IPv6len {
// Invalid mask.
return IPPrefix{}, false
}
ones, bits := std.Mask.Size()
if ones == 0 && bits == 0 {
// IPPrefix does not support non-contiguous masks.
return IPPrefix{}, false
}
return IPPrefix{
ip: ip,
bits: uint8(ones),
}, true
}
// ParseIPPrefix parses s as an IP address prefix.
// The string can be in the form "192.168.1.0/24" or "2001::db8::/32",
// the CIDR notation defined in RFC 4632 and RFC 4291.
//
// Note that masked address bits are not zeroed. Use Masked for that.
func ParseIPPrefix(s string) (IPPrefix, error) {
i := strings.LastIndexByte(s, '/')
if i < 0 {
return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): no '/'", s)
}
ip, err := ParseIP(s[:i])
if err != nil {
return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): %v", s, err)
}
s = s[i+1:]
bits, err := strconv.Atoi(s)
if err != nil {
return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): bad prefix: %v", s, err)
}
maxBits := 32
if ip.Is6() {
maxBits = 128
}
if bits < 0 || bits > maxBits {
return IPPrefix{}, fmt.Errorf("netaddr.ParseIPPrefix(%q): prefix length out of range", s)
}
return IPPrefixFrom(ip, uint8(bits)), nil
}
// MustParseIPPrefix calls ParseIPPrefix(s) and panics on error.
// It is intended for use in tests with hard-coded strings.
func MustParseIPPrefix(s string) IPPrefix {
ip, err := ParseIPPrefix(s)
if err != nil {
panic(err)
}
return ip
}
// Masked returns p in its canonical form, with bits of p.IP() not in p.Bits() masked off.
// If p is zero or otherwise invalid, Masked returns the zero value.
func (p IPPrefix) Masked() IPPrefix {
if m, err := p.ip.Prefix(p.bits); err == nil {
return m
}
return IPPrefix{}
}
// Range returns the inclusive range of IPs that p covers.
//
// If p is zero or otherwise invalid, Range returns the zero value.
func (p IPPrefix) Range() IPRange {
p = p.Masked()
if p.IsZero() {
return IPRange{}
}
return IPRangeFrom(p.ip, p.lastIP())
}
// IPNet returns the net.IPNet representation of an IPPrefix.
// The returned value is always non-nil.
// Any zone identifier is dropped in the conversion.
func (p IPPrefix) IPNet() *net.IPNet {
if !p.IsValid() {
return &net.IPNet{}
}
stdIP, _ := p.ip.ipZone(nil)
return &net.IPNet{
IP: stdIP,
Mask: net.CIDRMask(int(p.bits), int(p.ip.BitLen())),
}
}
// Contains reports whether the network p includes ip.
//
// An IPv4 address will not match an IPv6 prefix.
// A v6-mapped IPv6 address will not match an IPv4 prefix.
// A zero-value IP will not match any prefix.
// If ip has an IPv6 zone, Contains returns false,
// because IPPrefixes strip zones.
func (p IPPrefix) Contains(ip IP) bool {
if !p.IsValid() || ip.hasZone() {
return false
}
if f1, f2 := p.ip.BitLen(), ip.BitLen(); f1 == 0 || f2 == 0 || f1 != f2 {
return false
}
if ip.Is4() {
// xor the IP addresses together; mismatched bits are now ones.
// Shift away the number of bits we don't care about.
// Shifts in Go are more efficient if the compiler can prove
// that the shift amount is smaller than the width of the shifted type (64 here).
// We know that p.bits is in the range 0..32 because p is Valid;
// the compiler doesn't know that, so mask with 63 to help it.
// Now truncate to 32 bits, because this is IPv4.
// If all the bits we care about are equal, the result will be zero.
return uint32((ip.addr.lo^p.ip.addr.lo)>>((32-p.bits)&63)) == 0
} else {
// xor the IP addresses together.
// Mask away the bits we don't care about.
// If all the bits we care about are equal, the result will be zero.
return ip.addr.xor(p.ip.addr).and(mask6[p.bits]).isZero()
}
}
// Overlaps reports whether p and o overlap at all.
//
// If p and o are of different address families or either have a zero
// IP, it reports false. Like the Contains method, a prefix with a
// v6-mapped IPv4 IP is still treated as an IPv6 mask.
//
// If either has a Bits of zero, it returns true.
func (p IPPrefix) Overlaps(o IPPrefix) bool {
if !p.IsValid() || !o.IsValid() {
return false
}
if p == o {
return true
}
if p.ip.Is4() != o.ip.Is4() {
return false
}
var minBits uint8
if p.bits < o.bits {
minBits = p.bits
} else {
minBits = o.bits
}
if minBits == 0 {
return true
}
// One of these Prefix calls might look redundant, but we don't require
// that p and o values are normalized (via IPPrefix.Masked) first,
// so the Prefix call on the one that's already minBits serves to zero
// out any remaining bits in IP.
var err error
if p, err = p.ip.Prefix(minBits); err != nil {
return false
}
if o, err = o.ip.Prefix(minBits); err != nil {
return false
}
return p.ip == o.ip
}
// AppendTo appends a text encoding of p,
// as generated by MarshalText,
// to b and returns the extended buffer.
func (p IPPrefix) AppendTo(b []byte) []byte {
if p.IsZero() {
return b
}
if !p.IsValid() {
return append(b, "invalid IPPrefix"...)
}
// p.IP is non-zero, because p is valid.
if p.ip.z == z4 {
b = p.ip.appendTo4(b)
} else {
b = p.ip.appendTo6(b)
}
b = append(b, '/')
b = appendDecimal(b, p.bits)
return b
}
// MarshalText implements the encoding.TextMarshaler interface,
// The encoding is the same as returned by String, with one exception:
// If p is the zero value, the encoding is the empty string.
func (p IPPrefix) MarshalText() ([]byte, error) {
var max int
switch p.ip.z {
case z0:
case z4:
max = len("255.255.255.255/32")
default:
max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0/128")
}
b := make([]byte, 0, max)
b = p.AppendTo(b)
return b, nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The IP address is expected in a form accepted by ParseIPPrefix.
// It returns an error if *p is not the IPPrefix zero value.
func (p *IPPrefix) UnmarshalText(text []byte) error {
if *p != (IPPrefix{}) {
return errors.New("refusing to Unmarshal into non-zero IPPrefix")
}
if len(text) == 0 {
return nil
}
var err error
*p, err = ParseIPPrefix(string(text))
return err
}
// String returns the CIDR notation of p: "<ip>/<bits>".
func (p IPPrefix) String() string {
if p.IsZero() {
return "zero IPPrefix"
}
if !p.IsValid() {
return "invalid IPPrefix"
}
return fmt.Sprintf("%s/%d", p.ip, p.bits)
}
// lastIP returns the last IP in the prefix.
func (p IPPrefix) lastIP() IP {
if !p.IsValid() {
return IP{}
}
a16 := p.ip.As16()
var off uint8
var bits uint8 = 128
if p.ip.Is4() {
off = 12
bits = 32
}
for b := p.bits; b < bits; b++ {
byteNum, bitInByte := b/8, 7-(b%8)
a16[off+byteNum] |= 1 << uint(bitInByte)
}
if p.ip.Is4() {
return IPFrom16(a16)
} else {
return IPv6Raw(a16) // doesn't unmap
}
}
// IPRange represents an inclusive range of IP addresses
// from the same address family.
//
// The From() and To() IPs are inclusive bounds, both included in the
// range.
//
// To be valid, the From() and To() values must be non-zero, have matching
// address families (IPv4 vs IPv6), and From() must be less than or equal to To().
// IPv6 zones are stripped out and ignored.
// An invalid range may be ignored.
type IPRange struct {
// from is the initial IP address in the range.
from IP
// to is the final IP address in the range.
to IP
}
// IPRangeFrom returns an IPRange from from to to.
// It does not allocate.
func IPRangeFrom(from, to IP) IPRange {
return IPRange{
from: from.withoutZone(),
to: to.withoutZone(),
}
}
// From returns the lower bound of r.
func (r IPRange) From() IP { return r.from }
// To returns the upper bound of r.
func (r IPRange) To() IP { return r.to }
// ParseIPRange parses a range out of two IPs separated by a hyphen.
//
// It returns an error if the range is not valid.
func ParseIPRange(s string) (IPRange, error) {
var r IPRange
h := strings.IndexByte(s, '-')
if h == -1 {
return r, fmt.Errorf("no hyphen in range %q", s)
}
from, to := s[:h], s[h+1:]
var err error
r.from, err = ParseIP(from)
if err != nil {
return r, fmt.Errorf("invalid From IP %q in range %q", from, s)
}
r.from = r.from.withoutZone()
r.to, err = ParseIP(to)
if err != nil {
return r, fmt.Errorf("invalid To IP %q in range %q", to, s)
}
r.to = r.to.withoutZone()
if !r.IsValid() {
return r, fmt.Errorf("range %v to %v not valid", r.from, r.to)
}
return r, nil
}
// MustParseIPRange calls ParseIPRange(s) and panics on error.
// It is intended for use in tests with hard-coded strings.
func MustParseIPRange(s string) IPRange {
r, err := ParseIPRange(s)
if err != nil {
panic(err)
}
return r
}
// String returns a string representation of the range.
//
// For a valid range, the form is "From-To" with a single hyphen
// separating the IPs, the same format recognized by
// ParseIPRange.
func (r IPRange) String() string {
if r.IsValid() {
return fmt.Sprintf("%s-%s", r.from, r.to)
}
if r.from.IsZero() || r.to.IsZero() {
return "zero IPRange"
}
return "invalid IPRange"
}
// AppendTo appends a text encoding of r,
// as generated by MarshalText,
// to b and returns the extended buffer.
func (r IPRange) AppendTo(b []byte) []byte {
if r.IsZero() {
return b
}
b = r.from.AppendTo(b)
b = append(b, '-')
b = r.to.AppendTo(b)
return b
}
// MarshalText implements the encoding.TextMarshaler interface,
// The encoding is the same as returned by String, with one exception:
// If ip is the zero value, the encoding is the empty string.
func (r IPRange) MarshalText() ([]byte, error) {
if r.IsZero() {
return []byte(""), nil
}
var max int
if r.from.z == z4 {
max = len("255.255.255.255-255.255.255.255")
} else {
max = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
}
b := make([]byte, 0, max)
return r.AppendTo(b), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
// The IP range is expected in a form accepted by ParseIPRange.
// It returns an error if *r is not the IPRange zero value.
func (r *IPRange) UnmarshalText(text []byte) error {
if *r != (IPRange{}) {
return errors.New("refusing to Unmarshal into non-zero IPRange")
}
if len(text) == 0 {
return nil
}
var err error
*r, err = ParseIPRange(string(text))
return err
}
// IsZero reports whether r is the zero value of the IPRange type.
func (r IPRange) IsZero() bool {
return r == IPRange{}
}
// IsValid reports whether r.From() and r.To() are both non-zero and
// obey the documented requirements: address families match, and From
// is less than or equal to To.
func (r IPRange) IsValid() bool {
return !r.from.IsZero() &&
r.from.z == r.to.z &&
!r.to.Less(r.from)
}
// Valid reports whether r.From() and r.To() are both non-zero and
// obey the documented requirements: address families match, and From
// is less than or equal to To.
//
// Deprecated: use the correctly named and identical IsValid method instead.
func (r IPRange) Valid() bool { return r.IsValid() }
// Contains reports whether the range r includes addr.
//
// An invalid range always reports false.
//
// If ip has an IPv6 zone, Contains returns false,
// because IPPrefixes strip zones.
func (r IPRange) Contains(addr IP) bool {
return r.IsValid() && !addr.hasZone() && r.contains(addr)
}
// contains is like Contains, but without the validity check.
// addr must not have a zone.
func (r IPRange) contains(addr IP) bool {
return r.from.Compare(addr) <= 0 && r.to.Compare(addr) >= 0
}
// less reports whether r is "before" other. It is before if r.From()
// is before other.From(). If they're equal, then the larger range
// (higher To()) comes first.
func (r IPRange) less(other IPRange) bool {
if cmp := r.from.Compare(other.from); cmp != 0 {
return cmp < 0
}
return other.to.Less(r.to)
}
// entirelyBefore returns whether r lies entirely before other in IP
// space.
func (r IPRange) entirelyBefore(other IPRange) bool {
return r.to.Less(other.from)
}
// entirelyWithin returns whether r is entirely contained within
// other.
func (r IPRange) coveredBy(other IPRange) bool {
return other.from.lessOrEq(r.from) && r.to.lessOrEq(other.to)
}
// inMiddleOf returns whether r is inside other, but not touching the
// edges of other.
func (r IPRange) inMiddleOf(other IPRange) bool {
return other.from.Less(r.from) && r.to.Less(other.to)
}
// overlapsStartOf returns whether r entirely overlaps the start of
// other, but not all of other.
func (r IPRange) overlapsStartOf(other IPRange) bool {
return r.from.lessOrEq(other.from) && r.to.Less(other.to)
}
// overlapsEndOf returns whether r entirely overlaps the end of
// other, but not all of other.
func (r IPRange) overlapsEndOf(other IPRange) bool {
return other.from.Less(r.from) && other.to.lessOrEq(r.to)
}
// mergeIPRanges returns the minimum and sorted set of IP ranges that
// cover r.
func mergeIPRanges(rr []IPRange) (out []IPRange, valid bool) {
// Always return a copy of r, to avoid aliasing slice memory in
// the caller.
switch len(rr) {
case 0:
return nil, true
case 1:
return []IPRange{rr[0]}, true
}
sort.Slice(rr, func(i, j int) bool { return rr[i].less(rr[j]) })
out = make([]IPRange, 1, len(rr))
out[0] = rr[0]
for _, r := range rr[1:] {
prev := &out[len(out)-1]
switch {
case !r.IsValid():
// Invalid ranges make no sense to merge, refuse to
// perform.
return nil, false
case prev.to.Next() == r.from:
// prev and r touch, merge them.
//
// prev r
// f------tf-----t
prev.to = r.to
case prev.to.Less(r.from):
// No overlap and not adjacent (per previous case), no
// merging possible.
//
// prev r
// f------t f-----t
out = append(out, r)
case prev.to.Less(r.to):
// Partial overlap, update prev
//
// prev
// f------t
// f-----t
// r
prev.to = r.to
default:
// r entirely contained in prev, nothing to do.
//
// prev
// f--------t
// f-----t
// r
}
}
return out, true
}
// Overlaps reports whether p and o overlap at all.
//
// If p and o are of different address families or either are invalid,
// it reports false.
func (r IPRange) Overlaps(o IPRange) bool {
return r.IsValid() &&
o.IsValid() &&
r.from.Compare(o.to) <= 0 &&
o.from.Compare(r.to) <= 0
}
// prefixMaker returns a address-family-corrected IPPrefix from a and bits,
// where the input bits is always in the IPv6-mapped form for IPv4 addresses.
type prefixMaker func(a uint128, bits uint8) IPPrefix
// Prefixes returns the set of IPPrefix entries that covers r.
//
// If either of r's bounds are invalid, in the wrong order, or if
// they're of different address families, then Prefixes returns nil.
//
// Prefixes necessarily allocates. See AppendPrefixes for a version that uses
// memory you provide.
func (r IPRange) Prefixes() []IPPrefix {
return r.AppendPrefixes(nil)
}
// AppendPrefixes is an append version of IPRange.Prefixes. It appends
// the IPPrefix entries that cover r to dst.
func (r IPRange) AppendPrefixes(dst []IPPrefix) []IPPrefix {
if !r.IsValid() {
return nil
}
return appendRangePrefixes(dst, r.prefixFrom128AndBits, r.from.addr, r.to.addr)
}
func (r IPRange) prefixFrom128AndBits(a uint128, bits uint8) IPPrefix {
ip := IP{addr: a, z: r.from.z}
if r.from.Is4() {
bits -= 12 * 8
}
return IPPrefix{ip, bits}
}
// aZeroBSet is whether, after the common bits, a is all zero bits and
// b is all set (one) bits.
func comparePrefixes(a, b uint128) (common uint8, aZeroBSet bool) {
common = a.commonPrefixLen(b)
// See whether a and b, after their common shared bits, end
// in all zero bits or all one bits, respectively.
if common == 128 {
return common, true
}
m := mask6[common]
return common, (a.xor(a.and(m)).isZero() &&
b.or(m) == uint128{^uint64(0), ^uint64(0)})
}
// Prefix returns r as an IPPrefix, if it can be presented exactly as such.
// If r is not valid or is not exactly equal to one prefix, ok is false.
func (r IPRange) Prefix() (p IPPrefix, ok bool) {
if !r.IsValid() {
return
}
if common, ok := comparePrefixes(r.from.addr, r.to.addr); ok {
return r.prefixFrom128AndBits(r.from.addr, common), true
}
return
}
func appendRangePrefixes(dst []IPPrefix, makePrefix prefixMaker, a, b uint128) []IPPrefix {
common, ok := comparePrefixes(a, b)
if ok {
// a to b represents a whole range, like 10.50.0.0/16.
// (a being 10.50.0.0 and b being 10.50.255.255)
return append(dst, makePrefix(a, common))
}
// Otherwise recursively do both halves.
dst = appendRangePrefixes(dst, makePrefix, a, a.bitsSetFrom(common+1))
dst = appendRangePrefixes(dst, makePrefix, b.bitsClearedFrom(common+1), b)
return dst
}