| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| package netip |
|
|
| import ( |
| "cmp" |
| "errors" |
| "internal/bytealg" |
| "internal/byteorder" |
| "math" |
| "strconv" |
| "unique" |
| ) |
|
|
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| type Addr struct { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| addr uint128 |
|
|
| |
| z unique.Handle[addrDetail] |
| } |
|
|
| |
| type addrDetail struct { |
| isV6 bool |
| zoneV6 string |
| } |
|
|
| |
| |
| var ( |
| z0 unique.Handle[addrDetail] |
| z4 = unique.Make(addrDetail{}) |
| z6noz = unique.Make(addrDetail{isV6: true}) |
| ) |
|
|
| |
| |
| func IPv6LinkLocalAllNodes() Addr { return AddrFrom16([16]byte{0: 0xff, 1: 0x02, 15: 0x01}) } |
|
|
| |
| |
| func IPv6LinkLocalAllRouters() Addr { return AddrFrom16([16]byte{0: 0xff, 1: 0x02, 15: 0x02}) } |
|
|
| |
| func IPv6Loopback() Addr { return AddrFrom16([16]byte{15: 0x01}) } |
|
|
| |
| func IPv6Unspecified() Addr { return Addr{z: z6noz} } |
|
|
| |
| func IPv4Unspecified() Addr { return AddrFrom4([4]byte{}) } |
|
|
| |
| func AddrFrom4(addr [4]byte) Addr { |
| return Addr{ |
| addr: uint128{0, 0xffff00000000 | uint64(addr[0])<<24 | uint64(addr[1])<<16 | uint64(addr[2])<<8 | uint64(addr[3])}, |
| z: z4, |
| } |
| } |
|
|
| |
| |
| |
| func AddrFrom16(addr [16]byte) Addr { |
| return Addr{ |
| addr: uint128{ |
| byteorder.BEUint64(addr[:8]), |
| byteorder.BEUint64(addr[8:]), |
| }, |
| z: z6noz, |
| } |
| } |
|
|
| |
| |
| |
| func ParseAddr(s string) (Addr, error) { |
| for i := 0; i < len(s); i++ { |
| switch s[i] { |
| case '.': |
| return parseIPv4(s) |
| case ':': |
| return parseIPv6(s) |
| case '%': |
| |
| |
| return Addr{}, parseAddrError{in: s, msg: "missing IPv6 address"} |
| } |
| } |
| return Addr{}, parseAddrError{in: s, msg: "unable to parse IP"} |
| } |
|
|
| |
| |
| func MustParseAddr(s string) Addr { |
| ip, err := ParseAddr(s) |
| if err != nil { |
| panic(err) |
| } |
| return ip |
| } |
|
|
| type parseAddrError struct { |
| in string |
| msg string |
| at string |
| } |
|
|
| func (err parseAddrError) Error() string { |
| q := strconv.Quote |
| if err.at != "" { |
| return "ParseAddr(" + q(err.in) + "): " + err.msg + " (at " + q(err.at) + ")" |
| } |
| return "ParseAddr(" + q(err.in) + "): " + err.msg |
| } |
|
|
| func parseIPv4Fields(in string, off, end int, fields []uint8) error { |
| var val, pos int |
| var digLen int |
| s := in[off:end] |
| for i := 0; i < len(s); i++ { |
| if s[i] >= '0' && s[i] <= '9' { |
| if digLen == 1 && val == 0 { |
| return parseAddrError{in: in, msg: "IPv4 field has octet with leading zero"} |
| } |
| val = val*10 + int(s[i]) - '0' |
| digLen++ |
| if val > 255 { |
| return parseAddrError{in: in, msg: "IPv4 field has value >255"} |
| } |
| } else if s[i] == '.' { |
| |
| |
| |
| if i == 0 || i == len(s)-1 || s[i-1] == '.' { |
| return parseAddrError{in: in, msg: "IPv4 field must have at least one digit", at: s[i:]} |
| } |
| |
| if pos == 3 { |
| return parseAddrError{in: in, msg: "IPv4 address too long"} |
| } |
| fields[pos] = uint8(val) |
| pos++ |
| val = 0 |
| digLen = 0 |
| } else { |
| return parseAddrError{in: in, msg: "unexpected character", at: s[i:]} |
| } |
| } |
| if pos < 3 { |
| return parseAddrError{in: in, msg: "IPv4 address too short"} |
| } |
| fields[3] = uint8(val) |
| return nil |
| } |
|
|
| |
| func parseIPv4(s string) (ip Addr, err error) { |
| var fields [4]uint8 |
| err = parseIPv4Fields(s, 0, len(s), fields[:]) |
| if err != nil { |
| return Addr{}, err |
| } |
| return AddrFrom4(fields), nil |
| } |
|
|
| |
| func parseIPv6(in string) (Addr, error) { |
| s := in |
|
|
| |
| |
| |
| |
| zone := "" |
| i := bytealg.IndexByteString(s, '%') |
| if i != -1 { |
| s, zone = s[:i], s[i+1:] |
| if zone == "" { |
| |
| return Addr{}, parseAddrError{in: in, msg: "zone must be a non-empty string"} |
| } |
| } |
|
|
| var ip [16]byte |
| ellipsis := -1 |
|
|
| |
| if len(s) >= 2 && s[0] == ':' && s[1] == ':' { |
| ellipsis = 0 |
| s = s[2:] |
| |
| if len(s) == 0 { |
| return IPv6Unspecified().WithZone(zone), nil |
| } |
| } |
|
|
| |
| i = 0 |
| for i < 16 { |
| |
| |
| 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 off > 3 { |
| |
| return Addr{}, parseAddrError{in: in, msg: "each group must have 4 or less digits", at: s} |
| } |
| if acc > math.MaxUint16 { |
| |
| return Addr{}, parseAddrError{in: in, msg: "IPv6 field has value >=2^16", at: s} |
| } |
| } |
| if off == 0 { |
| |
| return Addr{}, parseAddrError{in: in, msg: "each colon-separated field must have at least one digit", at: s} |
| } |
|
|
| |
| if off < len(s) && s[off] == '.' { |
| if ellipsis < 0 && i != 12 { |
| |
| return Addr{}, parseAddrError{in: in, msg: "embedded IPv4 address must replace the final 2 fields of the address", at: s} |
| } |
| if i+4 > 16 { |
| |
| return Addr{}, parseAddrError{in: in, msg: "too many hex fields to fit an embedded IPv4 at the end of the address", at: s} |
| } |
|
|
| end := len(in) |
| if len(zone) > 0 { |
| end -= len(zone) + 1 |
| } |
| err := parseIPv4Fields(in, end-len(s), end, ip[i:i+4]) |
| if err != nil { |
| return Addr{}, err |
| } |
| s = "" |
| i += 4 |
| break |
| } |
|
|
| |
| ip[i] = byte(acc >> 8) |
| ip[i+1] = byte(acc) |
| i += 2 |
|
|
| |
| s = s[off:] |
| if len(s) == 0 { |
| break |
| } |
|
|
| |
| if s[0] != ':' { |
| return Addr{}, parseAddrError{in: in, msg: "unexpected character, want colon", at: s} |
| } else if len(s) == 1 { |
| return Addr{}, parseAddrError{in: in, msg: "colon must be followed by more characters", at: s} |
| } |
| s = s[1:] |
|
|
| |
| if s[0] == ':' { |
| if ellipsis >= 0 { |
| return Addr{}, parseAddrError{in: in, msg: "multiple :: in address", at: s} |
| } |
| ellipsis = i |
| s = s[1:] |
| if len(s) == 0 { |
| break |
| } |
| } |
| } |
|
|
| |
| if len(s) != 0 { |
| return Addr{}, parseAddrError{in: in, msg: "trailing garbage after address", at: s} |
| } |
|
|
| |
| if i < 16 { |
| if ellipsis < 0 { |
| return Addr{}, parseAddrError{in: in, msg: "address string too short"} |
| } |
| n := 16 - i |
| for j := i - 1; j >= ellipsis; j-- { |
| ip[j+n] = ip[j] |
| } |
| clear(ip[ellipsis : ellipsis+n]) |
| } else if ellipsis >= 0 { |
| |
| return Addr{}, parseAddrError{in: in, msg: "the :: must expand to at least one field of zeros"} |
| } |
| return AddrFrom16(ip).WithZone(zone), nil |
| } |
|
|
| |
| |
| |
| func AddrFromSlice(slice []byte) (ip Addr, ok bool) { |
| switch len(slice) { |
| case 4: |
| return AddrFrom4([4]byte(slice)), true |
| case 16: |
| return AddrFrom16([16]byte(slice)), true |
| } |
| return Addr{}, false |
| } |
|
|
| |
| |
| func (ip Addr) v4(i uint8) uint8 { |
| return uint8(ip.addr.lo >> ((3 - i) * 8)) |
| } |
|
|
| |
| |
| func (ip Addr) v6(i uint8) uint8 { |
| return uint8(*(ip.addr.halves()[(i/8)%2]) >> ((7 - i%8) * 8)) |
| } |
|
|
| |
| |
| func (ip Addr) v6u16(i uint8) uint16 { |
| return uint16(*(ip.addr.halves()[(i/4)%2]) >> ((3 - i%4) * 16)) |
| } |
|
|
| |
| |
| |
| |
| |
| func (ip Addr) isZero() bool { |
| |
| |
| return ip.z == z0 |
| } |
|
|
| |
| |
| |
| func (ip Addr) IsValid() bool { return ip.z != z0 } |
|
|
| |
| |
| |
| |
| |
| func (ip Addr) BitLen() int { |
| switch ip.z { |
| case z0: |
| return 0 |
| case z4: |
| return 32 |
| } |
| return 128 |
| } |
|
|
| |
| func (ip Addr) Zone() string { |
| if ip.z == z0 { |
| return "" |
| } |
| return ip.z.Value().zoneV6 |
| } |
|
|
| |
| |
| |
| func (ip Addr) Compare(ip2 Addr) int { |
| f1, f2 := ip.BitLen(), ip2.BitLen() |
| if f1 < f2 { |
| return -1 |
| } |
| if f1 > f2 { |
| return 1 |
| } |
| hi1, hi2 := ip.addr.hi, ip2.addr.hi |
| if hi1 < hi2 { |
| return -1 |
| } |
| if hi1 > hi2 { |
| return 1 |
| } |
| lo1, lo2 := ip.addr.lo, ip2.addr.lo |
| if lo1 < lo2 { |
| return -1 |
| } |
| if lo1 > lo2 { |
| return 1 |
| } |
| if ip.Is6() { |
| za, zb := ip.Zone(), ip2.Zone() |
| if za < zb { |
| return -1 |
| } |
| if za > zb { |
| return 1 |
| } |
| } |
| return 0 |
| } |
|
|
| |
| |
| |
| func (ip Addr) Less(ip2 Addr) bool { return ip.Compare(ip2) == -1 } |
|
|
| |
| |
| |
| func (ip Addr) Is4() bool { |
| return ip.z == z4 |
| } |
|
|
| |
| |
| |
| func (ip Addr) Is4In6() bool { |
| return ip.Is6() && ip.addr.hi == 0 && ip.addr.lo>>32 == 0xffff |
| } |
|
|
| |
| |
| func (ip Addr) Is6() bool { |
| return ip.z != z0 && ip.z != z4 |
| } |
|
|
| |
| |
| |
| |
| func (ip Addr) Unmap() Addr { |
| if ip.Is4In6() { |
| ip.z = z4 |
| } |
| return ip |
| } |
|
|
| |
| |
| |
| func (ip Addr) WithZone(zone string) Addr { |
| if !ip.Is6() { |
| return ip |
| } |
| if zone == "" { |
| ip.z = z6noz |
| return ip |
| } |
| ip.z = unique.Make(addrDetail{isV6: true, zoneV6: zone}) |
| return ip |
| } |
|
|
| |
| |
| func (ip Addr) withoutZone() Addr { |
| if !ip.Is6() { |
| return ip |
| } |
| ip.z = z6noz |
| return ip |
| } |
|
|
| |
| func (ip Addr) hasZone() bool { |
| return ip.z != z0 && ip.z != z4 && ip.z != z6noz |
| } |
|
|
| |
| func (ip Addr) IsLinkLocalUnicast() bool { |
| if ip.Is4In6() { |
| ip = ip.Unmap() |
| } |
|
|
| |
| |
| if ip.Is4() { |
| return ip.v4(0) == 169 && ip.v4(1) == 254 |
| } |
| |
| |
| if ip.Is6() { |
| return ip.v6u16(0)&0xffc0 == 0xfe80 |
| } |
| return false |
| } |
|
|
| |
| func (ip Addr) IsLoopback() bool { |
| if ip.Is4In6() { |
| ip = ip.Unmap() |
| } |
|
|
| |
| |
| if ip.Is4() { |
| return ip.v4(0) == 127 |
| } |
| |
| |
| if ip.Is6() { |
| return ip.addr.hi == 0 && ip.addr.lo == 1 |
| } |
| return false |
| } |
|
|
| |
| func (ip Addr) IsMulticast() bool { |
| if ip.Is4In6() { |
| ip = ip.Unmap() |
| } |
|
|
| |
| |
| if ip.Is4() { |
| return ip.v4(0)&0xf0 == 0xe0 |
| } |
| |
| |
| if ip.Is6() { |
| return ip.addr.hi>>(64-8) == 0xff |
| } |
| return false |
| } |
|
|
| |
| |
| func (ip Addr) IsInterfaceLocalMulticast() bool { |
| |
| |
| if ip.Is6() && !ip.Is4In6() { |
| return ip.v6u16(0)&0xff0f == 0xff01 |
| } |
| return false |
| } |
|
|
| |
| func (ip Addr) IsLinkLocalMulticast() bool { |
| if ip.Is4In6() { |
| ip = ip.Unmap() |
| } |
|
|
| |
| |
| if ip.Is4() { |
| return ip.v4(0) == 224 && ip.v4(1) == 0 && ip.v4(2) == 0 |
| } |
| |
| |
| if ip.Is6() { |
| return ip.v6u16(0)&0xff0f == 0xff02 |
| } |
| return false |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func (ip Addr) IsGlobalUnicast() bool { |
| if ip.z == z0 { |
| |
| return false |
| } |
|
|
| if ip.Is4In6() { |
| ip = ip.Unmap() |
| } |
|
|
| |
| |
| if ip.Is4() && (ip == IPv4Unspecified() || ip == AddrFrom4([4]byte{255, 255, 255, 255})) { |
| return false |
| } |
|
|
| return ip != IPv6Unspecified() && |
| !ip.IsLoopback() && |
| !ip.IsMulticast() && |
| !ip.IsLinkLocalUnicast() |
| } |
|
|
| |
| |
| |
| |
| func (ip Addr) IsPrivate() bool { |
| if ip.Is4In6() { |
| ip = ip.Unmap() |
| } |
|
|
| |
| if ip.Is4() { |
| |
| |
| 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() { |
| |
| |
| return ip.v6(0)&0xfe == 0xfc |
| } |
|
|
| return false |
| } |
|
|
| |
| |
| |
| |
| func (ip Addr) IsUnspecified() bool { |
| return ip == IPv4Unspecified() || ip == IPv6Unspecified() |
| } |
|
|
| |
| |
| |
| |
| |
| func (ip Addr) Prefix(b int) (Prefix, error) { |
| if b < 0 { |
| return Prefix{}, errors.New("negative Prefix bits") |
| } |
| effectiveBits := b |
| switch ip.z { |
| case z0: |
| return Prefix{}, nil |
| case z4: |
| if b > 32 { |
| return Prefix{}, errors.New("prefix length " + strconv.Itoa(b) + " too large for IPv4") |
| } |
| effectiveBits += 96 |
| default: |
| if b > 128 { |
| return Prefix{}, errors.New("prefix length " + strconv.Itoa(b) + " too large for IPv6") |
| } |
| } |
| ip.addr = ip.addr.and(mask6(effectiveBits)) |
| return PrefixFrom(ip, b), nil |
| } |
|
|
| |
| |
| |
| |
| |
| func (ip Addr) As16() (a16 [16]byte) { |
| byteorder.BEPutUint64(a16[:8], ip.addr.hi) |
| byteorder.BEPutUint64(a16[8:], ip.addr.lo) |
| return a16 |
| } |
|
|
| |
| |
| |
| func (ip Addr) As4() (a4 [4]byte) { |
| if ip.z == z4 || ip.Is4In6() { |
| byteorder.BEPutUint32(a4[:], uint32(ip.addr.lo)) |
| return a4 |
| } |
| if ip.z == z0 { |
| panic("As4 called on IP zero value") |
| } |
| panic("As4 called on IPv6 address") |
| } |
|
|
| |
| func (ip Addr) AsSlice() []byte { |
| switch ip.z { |
| case z0: |
| return nil |
| case z4: |
| var ret [4]byte |
| byteorder.BEPutUint32(ret[:], uint32(ip.addr.lo)) |
| return ret[:] |
| default: |
| var ret [16]byte |
| byteorder.BEPutUint64(ret[:8], ip.addr.hi) |
| byteorder.BEPutUint64(ret[8:], ip.addr.lo) |
| return ret[:] |
| } |
| } |
|
|
| |
| |
| func (ip Addr) Next() Addr { |
| ip.addr = ip.addr.addOne() |
| if ip.Is4() { |
| if uint32(ip.addr.lo) == 0 { |
| |
| return Addr{} |
| } |
| } else { |
| if ip.addr.isZero() { |
| |
| return Addr{} |
| } |
| } |
| return ip |
| } |
|
|
| |
| |
| func (ip Addr) Prev() Addr { |
| if ip.Is4() { |
| if uint32(ip.addr.lo) == 0 { |
| return Addr{} |
| } |
| } else if ip.addr.isZero() { |
| return Addr{} |
| } |
| ip.addr = ip.addr.subOne() |
| return ip |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| func (ip Addr) String() string { |
| switch ip.z { |
| case z0: |
| return "invalid IP" |
| case z4: |
| return ip.string4() |
| default: |
| if ip.Is4In6() { |
| return ip.string4In6() |
| } |
| return ip.string6() |
| } |
| } |
|
|
| |
| |
| |
| func (ip Addr) AppendTo(b []byte) []byte { |
| switch ip.z { |
| case z0: |
| return b |
| case z4: |
| return ip.appendTo4(b) |
| default: |
| if ip.Is4In6() { |
| return ip.appendTo4In6(b) |
| } |
| return ip.appendTo6(b) |
| } |
| } |
|
|
| |
| |
| const digits = "0123456789abcdef" |
|
|
| |
| func appendDecimal(b []byte, x uint8) []byte { |
| |
| |
|
|
| 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]) |
| } |
|
|
| |
| func appendHex(b []byte, x uint16) []byte { |
| |
| |
|
|
| 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]) |
| } |
|
|
| |
| 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 Addr) string4() string { |
| const max = len("255.255.255.255") |
| ret := make([]byte, 0, max) |
| ret = ip.appendTo4(ret) |
| return string(ret) |
| } |
|
|
| func (ip Addr) 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 |
| } |
|
|
| func (ip Addr) string4In6() string { |
| const max = len("::ffff:255.255.255.255%enp5s0") |
| ret := make([]byte, 0, max) |
| ret = ip.appendTo4In6(ret) |
| return string(ret) |
| } |
|
|
| func (ip Addr) appendTo4In6(ret []byte) []byte { |
| ret = append(ret, "::ffff:"...) |
| ret = ip.Unmap().appendTo4(ret) |
| if ip.z != z6noz { |
| ret = append(ret, '%') |
| ret = append(ret, ip.Zone()...) |
| } |
| return ret |
| } |
|
|
| |
| |
| |
| |
| |
| func (ip Addr) string6() string { |
| |
| |
| |
| |
| |
| |
| |
| 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 Addr) 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 |
| } |
|
|
| |
| |
| |
| func (ip Addr) 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 { |
| |
| |
| ret = append(ret, '%') |
| ret = append(ret, ip.Zone()...) |
| } |
| return string(ret) |
| } |
|
|
| |
| |
| func (ip Addr) AppendText(b []byte) ([]byte, error) { |
| return ip.AppendTo(b), nil |
| } |
|
|
| |
| |
| |
| func (ip Addr) MarshalText() ([]byte, error) { |
| buf := []byte{} |
| switch ip.z { |
| case z0: |
| case z4: |
| const maxCap = len("255.255.255.255") |
| buf = make([]byte, 0, maxCap) |
| default: |
| if ip.Is4In6() { |
| const maxCap = len("::ffff:255.255.255.255%enp5s0") |
| buf = make([]byte, 0, maxCap) |
| break |
| } |
| const maxCap = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0") |
| buf = make([]byte, 0, maxCap) |
| } |
| return ip.AppendText(buf) |
| } |
|
|
| |
| |
| |
| |
| |
| func (ip *Addr) UnmarshalText(text []byte) error { |
| if len(text) == 0 { |
| *ip = Addr{} |
| return nil |
| } |
| var err error |
| *ip, err = ParseAddr(string(text)) |
| return err |
| } |
|
|
| |
| func (ip Addr) AppendBinary(b []byte) ([]byte, error) { |
| switch ip.z { |
| case z0: |
| case z4: |
| b = byteorder.BEAppendUint32(b, uint32(ip.addr.lo)) |
| default: |
| b = byteorder.BEAppendUint64(b, ip.addr.hi) |
| b = byteorder.BEAppendUint64(b, ip.addr.lo) |
| b = append(b, ip.Zone()...) |
| } |
| return b, nil |
| } |
|
|
| func (ip Addr) marshalBinarySize() int { |
| switch ip.z { |
| case z0: |
| return 0 |
| case z4: |
| return 4 |
| default: |
| return 16 + len(ip.Zone()) |
| } |
| } |
|
|
| |
| |
| |
| |
| func (ip Addr) MarshalBinary() ([]byte, error) { |
| return ip.AppendBinary(make([]byte, 0, ip.marshalBinarySize())) |
| } |
|
|
| |
| |
| func (ip *Addr) UnmarshalBinary(b []byte) error { |
| n := len(b) |
| switch { |
| case n == 0: |
| *ip = Addr{} |
| return nil |
| case n == 4: |
| *ip = AddrFrom4([4]byte(b)) |
| return nil |
| case n == 16: |
| *ip = AddrFrom16([16]byte(b)) |
| return nil |
| case n > 16: |
| *ip = AddrFrom16([16]byte(b[:16])).WithZone(string(b[16:])) |
| return nil |
| } |
| return errors.New("unexpected slice size") |
| } |
|
|
| |
| type AddrPort struct { |
| ip Addr |
| port uint16 |
| } |
|
|
| |
| |
| func AddrPortFrom(ip Addr, port uint16) AddrPort { return AddrPort{ip: ip, port: port} } |
|
|
| |
| func (p AddrPort) Addr() Addr { return p.ip } |
|
|
| |
| func (p AddrPort) Port() uint16 { return p.port } |
|
|
| |
| |
| |
| |
| |
| func splitAddrPort(s string) (ip, port string, v6 bool, err error) { |
| i := bytealg.LastIndexByteString(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 |
| } |
|
|
| |
| |
| |
| |
| func ParseAddrPort(s string) (AddrPort, error) { |
| var ipp AddrPort |
| ip, port, v6, err := splitAddrPort(s) |
| if err != nil { |
| return ipp, err |
| } |
| port16, err := strconv.ParseUint(port, 10, 16) |
| if err != nil { |
| return ipp, errors.New("invalid port " + strconv.Quote(port) + " parsing " + strconv.Quote(s)) |
| } |
| ipp.port = uint16(port16) |
| ipp.ip, err = ParseAddr(ip) |
| if err != nil { |
| return AddrPort{}, err |
| } |
| if v6 && ipp.ip.Is4() { |
| return AddrPort{}, errors.New("invalid ip:port " + strconv.Quote(s) + ", square brackets can only be used with IPv6 addresses") |
| } else if !v6 && ipp.ip.Is6() { |
| return AddrPort{}, errors.New("invalid ip:port " + strconv.Quote(s) + ", IPv6 addresses must be surrounded by square brackets") |
| } |
| return ipp, nil |
| } |
|
|
| |
| |
| func MustParseAddrPort(s string) AddrPort { |
| ip, err := ParseAddrPort(s) |
| if err != nil { |
| panic(err) |
| } |
| return ip |
| } |
|
|
| |
| |
| func (p AddrPort) IsValid() bool { return p.ip.IsValid() } |
|
|
| |
| |
| |
| func (p AddrPort) Compare(p2 AddrPort) int { |
| if c := p.Addr().Compare(p2.Addr()); c != 0 { |
| return c |
| } |
| return cmp.Compare(p.Port(), p2.Port()) |
| } |
|
|
| func (p AddrPort) String() string { |
| var b []byte |
| switch p.ip.z { |
| case z0: |
| return "invalid AddrPort" |
| case z4: |
| const max = len("255.255.255.255:65535") |
| b = make([]byte, 0, max) |
| b = p.ip.appendTo4(b) |
| default: |
| if p.ip.Is4In6() { |
| const max = len("[::ffff:255.255.255.255%enp5s0]:65535") |
| b = make([]byte, 0, max) |
| b = append(b, '[') |
| b = p.ip.appendTo4In6(b) |
| } else { |
| const max = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535") |
| b = make([]byte, 0, max) |
| b = append(b, '[') |
| b = p.ip.appendTo6(b) |
| } |
| b = append(b, ']') |
| } |
| b = append(b, ':') |
| b = strconv.AppendUint(b, uint64(p.port), 10) |
| return string(b) |
| } |
|
|
| |
| |
| |
| func (p AddrPort) AppendTo(b []byte) []byte { |
| switch p.ip.z { |
| case z0: |
| return b |
| case z4: |
| b = p.ip.appendTo4(b) |
| default: |
| b = append(b, '[') |
| if p.ip.Is4In6() { |
| b = p.ip.appendTo4In6(b) |
| } else { |
| b = p.ip.appendTo6(b) |
| } |
| b = append(b, ']') |
| } |
| b = append(b, ':') |
| b = strconv.AppendUint(b, uint64(p.port), 10) |
| return b |
| } |
|
|
| |
| |
| func (p AddrPort) AppendText(b []byte) ([]byte, error) { |
| return p.AppendTo(b), nil |
| } |
|
|
| |
| |
| |
| func (p AddrPort) MarshalText() ([]byte, error) { |
| buf := []byte{} |
| switch p.ip.z { |
| case z0: |
| case z4: |
| const maxCap = len("255.255.255.255:65535") |
| buf = make([]byte, 0, maxCap) |
| default: |
| const maxCap = len("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0]:65535") |
| buf = make([]byte, 0, maxCap) |
| } |
| return p.AppendText(buf) |
| } |
|
|
| |
| |
| |
| func (p *AddrPort) UnmarshalText(text []byte) error { |
| if len(text) == 0 { |
| *p = AddrPort{} |
| return nil |
| } |
| var err error |
| *p, err = ParseAddrPort(string(text)) |
| return err |
| } |
|
|
| |
| |
| |
| func (p AddrPort) AppendBinary(b []byte) ([]byte, error) { |
| b, err := p.Addr().AppendBinary(b) |
| if err != nil { |
| return nil, err |
| } |
| return byteorder.LEAppendUint16(b, p.Port()), nil |
| } |
|
|
| |
| |
| |
| func (p AddrPort) MarshalBinary() ([]byte, error) { |
| return p.AppendBinary(make([]byte, 0, p.Addr().marshalBinarySize()+2)) |
| } |
|
|
| |
| |
| func (p *AddrPort) UnmarshalBinary(b []byte) error { |
| if len(b) < 2 { |
| return errors.New("unexpected slice size") |
| } |
| var addr Addr |
| err := addr.UnmarshalBinary(b[:len(b)-2]) |
| if err != nil { |
| return err |
| } |
| *p = AddrPortFrom(addr, byteorder.LEUint16(b[len(b)-2:])) |
| return nil |
| } |
|
|
| |
| |
| |
| |
| type Prefix struct { |
| ip Addr |
|
|
| |
| |
| bitsPlusOne uint8 |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| func PrefixFrom(ip Addr, bits int) Prefix { |
| var bitsPlusOne uint8 |
| if !ip.isZero() && bits >= 0 && bits <= ip.BitLen() { |
| bitsPlusOne = uint8(bits) + 1 |
| } |
| return Prefix{ |
| ip: ip.withoutZone(), |
| bitsPlusOne: bitsPlusOne, |
| } |
| } |
|
|
| |
| func (p Prefix) Addr() Addr { return p.ip } |
|
|
| |
| |
| |
| func (p Prefix) Bits() int { return int(p.bitsPlusOne) - 1 } |
|
|
| |
| |
| |
| func (p Prefix) IsValid() bool { return p.bitsPlusOne > 0 } |
|
|
| func (p Prefix) isZero() bool { return p == Prefix{} } |
|
|
| |
| func (p Prefix) IsSingleIP() bool { return p.IsValid() && p.Bits() == p.ip.BitLen() } |
|
|
| |
| |
| |
| |
| |
| func (p Prefix) Compare(p2 Prefix) int { |
| |
| |
| |
| if c := p.Masked().Addr().Compare(p2.Masked().Addr()); c != 0 { |
| return c |
| } |
|
|
| if c := cmp.Compare(p.Bits(), p2.Bits()); c != 0 { |
| return c |
| } |
|
|
| return p.Addr().Compare(p2.Addr()) |
| } |
|
|
| type parsePrefixError struct { |
| in string |
| msg string |
| } |
|
|
| func (err parsePrefixError) Error() string { |
| return "netip.ParsePrefix(" + strconv.Quote(err.in) + "): " + err.msg |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| func ParsePrefix(s string) (Prefix, error) { |
| i := bytealg.LastIndexByteString(s, '/') |
| if i < 0 { |
| return Prefix{}, parsePrefixError{in: s, msg: "no '/'"} |
| } |
| ip, err := ParseAddr(s[:i]) |
| if err != nil { |
| return Prefix{}, parsePrefixError{in: s, msg: err.Error()} |
| } |
| |
| if ip.Is6() && ip.z != z6noz { |
| return Prefix{}, parsePrefixError{in: s, msg: "IPv6 zones cannot be present in a prefix"} |
| } |
|
|
| bitsStr := s[i+1:] |
|
|
| |
| if len(bitsStr) > 1 && (bitsStr[0] < '1' || bitsStr[0] > '9') { |
| return Prefix{}, parsePrefixError{in: s, msg: "bad bits after slash: " + strconv.Quote(bitsStr)} |
| } |
|
|
| bits, err := strconv.Atoi(bitsStr) |
| if err != nil { |
| return Prefix{}, parsePrefixError{in: s, msg: "bad bits after slash: " + strconv.Quote(bitsStr)} |
| } |
| maxBits := 32 |
| if ip.Is6() { |
| maxBits = 128 |
| } |
| if bits < 0 || bits > maxBits { |
| return Prefix{}, parsePrefixError{in: s, msg: "prefix length out of range"} |
| } |
| return PrefixFrom(ip, bits), nil |
| } |
|
|
| |
| |
| func MustParsePrefix(s string) Prefix { |
| ip, err := ParsePrefix(s) |
| if err != nil { |
| panic(err) |
| } |
| return ip |
| } |
|
|
| |
| |
| |
| |
| func (p Prefix) Masked() Prefix { |
| m, _ := p.ip.Prefix(p.Bits()) |
| return m |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| func (p Prefix) Contains(ip Addr) 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() { |
| |
| |
| |
| |
| |
| |
| |
| |
| return uint32((ip.addr.lo^p.ip.addr.lo)>>((32-p.Bits())&63)) == 0 |
| } else { |
| |
| |
| |
| return ip.addr.xor(p.ip.addr).and(mask6(p.Bits())).isZero() |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| func (p Prefix) Overlaps(o Prefix) bool { |
| if !p.IsValid() || !o.IsValid() { |
| return false |
| } |
| if p == o { |
| return true |
| } |
| if p.ip.Is4() != o.ip.Is4() { |
| return false |
| } |
| var minBits int |
| if pb, ob := p.Bits(), o.Bits(); pb < ob { |
| minBits = pb |
| } else { |
| minBits = ob |
| } |
| if minBits == 0 { |
| return true |
| } |
| |
| |
| |
| |
| 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 |
| } |
|
|
| |
| |
| |
| func (p Prefix) AppendTo(b []byte) []byte { |
| if p.isZero() { |
| return b |
| } |
| if !p.IsValid() { |
| return append(b, "invalid Prefix"...) |
| } |
|
|
| |
| if p.ip.z == z4 { |
| b = p.ip.appendTo4(b) |
| } else { |
| if p.ip.Is4In6() { |
| b = append(b, "::ffff:"...) |
| b = p.ip.Unmap().appendTo4(b) |
| } else { |
| b = p.ip.appendTo6(b) |
| } |
| } |
|
|
| b = append(b, '/') |
| b = appendDecimal(b, uint8(p.Bits())) |
| return b |
| } |
|
|
| |
| |
| func (p Prefix) AppendText(b []byte) ([]byte, error) { |
| return p.AppendTo(b), nil |
| } |
|
|
| |
| |
| |
| func (p Prefix) MarshalText() ([]byte, error) { |
| buf := []byte{} |
| switch p.ip.z { |
| case z0: |
| case z4: |
| const maxCap = len("255.255.255.255/32") |
| buf = make([]byte, 0, maxCap) |
| default: |
| const maxCap = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%enp5s0/128") |
| buf = make([]byte, 0, maxCap) |
| } |
| return p.AppendText(buf) |
| } |
|
|
| |
| |
| |
| func (p *Prefix) UnmarshalText(text []byte) error { |
| if len(text) == 0 { |
| *p = Prefix{} |
| return nil |
| } |
| var err error |
| *p, err = ParsePrefix(string(text)) |
| return err |
| } |
|
|
| |
| |
| |
| func (p Prefix) AppendBinary(b []byte) ([]byte, error) { |
| b, err := p.Addr().withoutZone().AppendBinary(b) |
| if err != nil { |
| return nil, err |
| } |
| return append(b, uint8(p.Bits())), nil |
| } |
|
|
| |
| |
| |
| func (p Prefix) MarshalBinary() ([]byte, error) { |
| |
| return p.AppendBinary(make([]byte, 0, p.Addr().withoutZone().marshalBinarySize()+1)) |
| } |
|
|
| |
| |
| func (p *Prefix) UnmarshalBinary(b []byte) error { |
| if len(b) < 1 { |
| return errors.New("unexpected slice size") |
| } |
| var addr Addr |
| err := addr.UnmarshalBinary(b[:len(b)-1]) |
| if err != nil { |
| return err |
| } |
| *p = PrefixFrom(addr, int(b[len(b)-1])) |
| return nil |
| } |
|
|
| |
| func (p Prefix) String() string { |
| if !p.IsValid() { |
| return "invalid Prefix" |
| } |
| return p.ip.String() + "/" + strconv.Itoa(p.Bits()) |
| } |
|
|