* added some netx funcs * added netx/dnsx * currently updating docs and adding *x funcs to sprigx
631 lines
11 KiB
Go
631 lines
11 KiB
Go
package netx
|
|
|
|
import (
|
|
`math/bits`
|
|
`net`
|
|
`net/netip`
|
|
`strconv`
|
|
`strings`
|
|
|
|
`go4.org/netipx`
|
|
)
|
|
|
|
/*
|
|
AddrRfc returns an RFC-friendly string from an IP address ([net/netip.Addr]).
|
|
|
|
If addr is an IPv4 address, it will simply be the string representation (e.g. "203.0.113.1").
|
|
|
|
If addr is an IPv6 address, it will be enclosed in brackets (e.g. "[2001:db8::1]").
|
|
|
|
If the version can't be determined, rfcStr will be an empty string.
|
|
*/
|
|
func AddrRfc(addr netip.Addr) (rfcStr string) {
|
|
|
|
if addr.Is4() {
|
|
rfcStr = addr.String()
|
|
} else if addr.Is6() {
|
|
rfcStr = "[" + addr.String() + "]"
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Cidr4ToIPMask takes an IPv4 CIDR/bit size/prefix length and returns the [net.IPMask].
|
|
It's (essentially) the inverse of [net.IPMask.Size].
|
|
|
|
See also:
|
|
|
|
* [Cidr4ToMask]
|
|
* [Cidr4ToStr]
|
|
|
|
Inverse of [IPMask4ToCidr].
|
|
*/
|
|
func Cidr4ToIPMask(cidr uint8) (ipMask net.IPMask, err error) {
|
|
|
|
if cidr > 32 {
|
|
err = ErrBadNetFam
|
|
return
|
|
}
|
|
|
|
ipMask = net.CIDRMask(int(cidr), 32)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Cidr4ToMask takes an IPv4 CIDR/bit size/prefix length and returns the netmask *in bitmask form*.
|
|
|
|
See also:
|
|
|
|
* [Cidr4ToIPMask]
|
|
* [Cidr4ToStr]
|
|
|
|
Inverse of [Mask4ToCidr].
|
|
*/
|
|
func Cidr4ToMask(cidr uint8) (mask uint32, err error) {
|
|
|
|
if cidr > 32 {
|
|
err = ErrBadNetFam
|
|
return
|
|
}
|
|
|
|
// COULD do (1 << 32) - (1 << (32 - ip.Bits())) instead but in EXTREME edge cases that could cause an overflow.
|
|
// We're basically converting the CIDR size ("number of bits"/"number of ones") to an integer mask ("number AS bits")
|
|
mask = uint32(0xffffffff) << uint32(32-cidr)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Cidr4ToStr is a convenience wrapper around [IPMask4ToStr]([Cidr4ToMask](cidr)).
|
|
|
|
See also:
|
|
|
|
* [Cidr4ToIPMask]
|
|
* [Cidr4ToMask]
|
|
|
|
Inverse of [Mask4StrToCidr].
|
|
*/
|
|
func Cidr4ToStr(cidr uint8) (maskStr string, err error) {
|
|
|
|
var ipMask net.IPMask
|
|
|
|
if ipMask, err = Cidr4ToIPMask(cidr); err != nil {
|
|
return
|
|
}
|
|
|
|
if maskStr, err = IPMask4ToStr(ipMask); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
FamilyToVer returns a more "human-friendly" IP version from a system/lower-level IP family
|
|
([AFUnspec], [AFInet], [AFInet6]).
|
|
|
|
ipVer will be int(4) for [AFInet], int(6) for [AFInet6], int(0) for [AFUnspec], or
|
|
int(-1) for an unknown family.
|
|
*/
|
|
func FamilyToVer(family uint16) (ipVer int) {
|
|
|
|
switch family {
|
|
case AFInet:
|
|
ipVer = 4
|
|
case AFInet6:
|
|
ipVer = 6
|
|
case AFUnspec:
|
|
ipVer = 0
|
|
default:
|
|
ipVer = -1
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
GetAddrFamily returns the network family of a [net/netip.Addr].
|
|
|
|
See also [GetIpFamily].
|
|
|
|
Note that this returns [AFInet] or [AFInet6], NOT uint16(4) or uint16(6).
|
|
(See [FamilyToVer] to get the associated higher-level value.)
|
|
|
|
If addr is not a "valid" IP address or the version can't be determined, family will be AFUnspec (usually 0x00/0).
|
|
*/
|
|
func GetAddrFamily(addr netip.Addr) (family uint16) {
|
|
|
|
family = AFUnspec
|
|
|
|
if !addr.IsValid() {
|
|
return
|
|
}
|
|
|
|
if addr.Is4() {
|
|
family = AFInet
|
|
} else if addr.Is6() {
|
|
family = AFInet6
|
|
} else {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
GetIpFamily returns the network family of a [net.IP].
|
|
|
|
Note that this returns [AFInet] or [AFInet6], NOT uint16(4) or uint16(6).
|
|
(See [FamilyToVer] to get the associated higher-level value.)
|
|
|
|
See also [GetAddrFamily].
|
|
|
|
If ip is not a "valid" IP address or the version can't be determined,
|
|
family will be [golang.org/x/sys/unix.AF_UNSPEC] or [golang.org/x/sys/windows.AF_UNSPEC] depending on platform (usually 0x00/0).
|
|
*/
|
|
func GetIpFamily(ip net.IP) (family uint16) {
|
|
|
|
var ok bool
|
|
var addr netip.Addr
|
|
|
|
if addr, ok = netipx.FromStdIP(ip); !ok {
|
|
return
|
|
}
|
|
|
|
family = GetAddrFamily(addr)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IpRfc returns an RFC-friendly string from an IP address ([net.IP]).
|
|
|
|
If ip is an IPv4 address, it will simmply be the string representation (e.g. "203.0.113.1").
|
|
|
|
If ip is an IPv6 address, it will be enclosed in brackets (e.g. "[2001:db8::1]").
|
|
|
|
If the version can't be determined, rfcStr will be an empty string.
|
|
|
|
See also [IpRfcStr] for providing an IP address as a string.
|
|
*/
|
|
func IpRfc(ip net.IP) (rfcStr string) {
|
|
|
|
if ip.To4() != nil {
|
|
rfcStr = ip.To4().String()
|
|
} else if ip.To16() != nil {
|
|
rfcStr = "[" + ip.To16().String() + "]"
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IpRfcStr implements [IpRfc]/[AddrRfc] for string representations of an IP address s.
|
|
|
|
If s is an IPv6 address already in the bracketed RFC format,
|
|
then rfcStr will be equal to s.
|
|
|
|
If s is not a string representation of an IP address, rfcStr will be empty.
|
|
|
|
See [IpStripRfcStr] for the inverse (removing any brackets from s if present).
|
|
*/
|
|
func IpRfcStr(s string) (rfcStr string) {
|
|
|
|
var ip net.IP
|
|
|
|
if !IsIpAddr(s) {
|
|
return
|
|
}
|
|
if IsBracketedIp6(s) {
|
|
rfcStr = s
|
|
return
|
|
}
|
|
ip = net.ParseIP(s)
|
|
if ip == nil {
|
|
return
|
|
}
|
|
rfcStr = IpRfc(ip)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IpStripRfcStr returns IP address string s without any brackets.
|
|
|
|
If s is not a valid IP address, stripStr will be empty.
|
|
*/
|
|
func IpStripRfcStr(s string) (stripStr string) {
|
|
|
|
if !IsIpAddr(s) {
|
|
return
|
|
}
|
|
if !IsBracketedIp6(s) {
|
|
stripStr = s
|
|
return
|
|
}
|
|
stripStr = strings.TrimPrefix(s, "[")
|
|
stripStr = strings.TrimSuffix(stripStr, "]")
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IPMask4ToCidr returns a CIDR prefix size/bit size/bit length from a [net.IPMask].
|
|
|
|
See also:
|
|
|
|
* [IPMask4ToMask]
|
|
* [IPMask4ToStr]
|
|
|
|
Inverse of [Cidr4ToIPMask].
|
|
*/
|
|
func IPMask4ToCidr(ipMask net.IPMask) (cidr uint8, err error) {
|
|
|
|
var ones int
|
|
var total int
|
|
|
|
ones, total = ipMask.Size()
|
|
|
|
if total != 32 {
|
|
err = ErrBadNetFam
|
|
return
|
|
}
|
|
if ones > 32 {
|
|
err = ErrBadNetFam
|
|
return
|
|
}
|
|
|
|
cidr = uint8(ones)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IPMask4ToMask returns the mask *in bitmask form* from a [net.IPMask].
|
|
|
|
See also:
|
|
|
|
* [IPMask4ToCidr]
|
|
* [IPMask4ToStr]
|
|
|
|
Inverse of [Mask4ToIPMask].
|
|
*/
|
|
func IPMask4ToMask(ipMask net.IPMask) (mask uint32, err error) {
|
|
|
|
var cidr uint8
|
|
|
|
if cidr, err = IPMask4ToCidr(ipMask); err != nil {
|
|
return
|
|
}
|
|
|
|
if mask, err = Cidr4ToMask(cidr); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IPMask4ToStr returns a string representation of an IPv4 netmask (e.g. "255.255.255.0" for a /24) from a [net.IPMask].
|
|
|
|
See also:
|
|
|
|
* [IPMask4ToCidr]
|
|
* [IPMask4ToMask]
|
|
|
|
Inverse of [Mask4StrToIPMask].
|
|
*/
|
|
func IPMask4ToStr(ipMask net.IPMask) (maskStr string, err error) {
|
|
|
|
var idx int
|
|
var b []byte
|
|
var quads []string = make([]string, 4)
|
|
|
|
b = []byte(ipMask)
|
|
if len(b) != 4 {
|
|
err = ErrBadNetFam
|
|
return
|
|
}
|
|
|
|
for idx = 0; idx < len(b); idx++ {
|
|
quads[idx] = strconv.Itoa(int(b[idx]))
|
|
}
|
|
|
|
maskStr = strings.Join(quads, ".")
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IpVerStr provides the IP family of IP address/network string s.
|
|
|
|
s may be one of the following formats/syntaxes:
|
|
|
|
* 203.0.113.0
|
|
* 203.0.113.1
|
|
* 203.0.113.0/24
|
|
* 203.0.113.1/24
|
|
* 2001:db8::
|
|
* 2001:db8::1
|
|
* 2001:db8::/32
|
|
* 2001:db8::1/32
|
|
* [2001:db8::]
|
|
* [2001:db8::1]
|
|
|
|
|
|
Unlike [GetAddrFamily]/[GetIpFamily], this returns a more "friendly"
|
|
version - if s is not valid syntax, ipVer will be int(0),
|
|
otherwise ipVer will be int(4) for family IPv4 and int(6) for family IPv6.
|
|
(See [VerToFamily] to get the associated system/lower-level value.)
|
|
*/
|
|
func IpVerStr(s string) (ipVer int) {
|
|
|
|
var err error
|
|
var ipstr string
|
|
var p netip.Prefix
|
|
|
|
ipstr = strings.TrimPrefix(s, "[")
|
|
ipstr = strings.TrimSuffix(ipstr, "]")
|
|
|
|
if p, err = netip.ParsePrefix(ipstr); err != nil {
|
|
return
|
|
}
|
|
if p.Addr().Is6() {
|
|
ipVer = 6
|
|
} else {
|
|
ipVer = 4
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IsBracketedIp6 returns a boolean indicating if s is a valid bracket-enclosed IPv6 in string format
|
|
(e.g. "[2001:db8::1]").
|
|
|
|
It will return false for *non-bracketed* IPv6 addresses (e.g. "2001:db8::1"), IPv4 addresses,
|
|
or if s is not a valid IPv6 address string.
|
|
|
|
[IpRfcStr] or [IpStripRfcStr] can be used to coerce a string to a specific format.
|
|
*/
|
|
func IsBracketedIp6(s string) (isBrktdIp bool) {
|
|
|
|
var ip net.IP
|
|
var ipstr string
|
|
|
|
if IpVerStr(s) != 6 {
|
|
return
|
|
}
|
|
|
|
ipstr = strings.TrimPrefix(s, "[")
|
|
ipstr = strings.TrimSuffix(ipstr, "]")
|
|
|
|
if ip = net.ParseIP(ipstr); ip == nil {
|
|
return
|
|
}
|
|
|
|
isBrktdIp = ipstr == s
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IsIpAddr returns a boolean indicating if s is an IP address (either IPv4 or IPv6) in string format.
|
|
|
|
For IPv6, it will return true for both of these formats:
|
|
|
|
* 2001:db8::1
|
|
* [2001:db8::1]
|
|
|
|
[IsBracketedIp6] can be used to narrow down which form.
|
|
*/
|
|
func IsIpAddr(s string) (isIp bool) {
|
|
|
|
var err error
|
|
var a netip.Addr
|
|
|
|
if a, err = netip.ParseAddr(s); err != nil {
|
|
return
|
|
}
|
|
|
|
isIp = a.IsValid()
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
IsPrefixNet returns true if s is a (valid) IP address or network (either IPv4 or IPv6) in:
|
|
|
|
<addr_or_net>/<prefix_len>
|
|
|
|
format.
|
|
*/
|
|
func IsPrefixNet(s string) (isNet bool) {
|
|
|
|
var err error
|
|
var p netip.Prefix
|
|
|
|
if p, err = netip.ParsePrefix(s); err != nil {
|
|
return
|
|
}
|
|
isNet = p.Masked().IsValid()
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Mask4ToCidr converts an IPv4 netmask *in bitmask form* to a CIDR prefix size/bit size/bit length.
|
|
|
|
See also:
|
|
|
|
* [Mask4ToIPMask]
|
|
* [Mask4ToStr]
|
|
|
|
Inverse of [Cidr4ToMask].
|
|
*/
|
|
func Mask4ToCidr(mask uint32) (cidr uint8, err error) {
|
|
|
|
cidr = 32 - uint8(bits.LeadingZeros32(mask))
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Mask4ToIPMask returns mask *in bitmask form* as a [net.IPMask].
|
|
|
|
See also:
|
|
|
|
* [Mask4ToCidr]
|
|
* [Mask4ToStr]
|
|
|
|
Inverse of [IPMask4ToMask].
|
|
*/
|
|
func Mask4ToIPMask(mask uint32) (ipMask net.IPMask, err error) {
|
|
|
|
var cidr uint8
|
|
|
|
if cidr, err = Mask4ToCidr(mask); err != nil {
|
|
return
|
|
}
|
|
|
|
ipMask = net.CIDRMask(int(cidr), 32)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Mask4ToStr returns a string representation of an IPv4 netmask (e.g. "255.255.255.0" for a /24) from a netmask *in bitmask form*.
|
|
|
|
See also:
|
|
|
|
* [Mask4ToCidr]
|
|
* [Mask4ToIPMask]
|
|
|
|
Inverse of [Mask4StrToMask].
|
|
*/
|
|
func Mask4ToStr(mask uint32) (maskStr string, err error) {
|
|
|
|
var ipMask net.IPMask
|
|
|
|
if ipMask, err = Mask4ToIPMask(mask); err != nil {
|
|
return
|
|
}
|
|
|
|
if maskStr, err = IPMask4ToStr(ipMask); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Mask4StrToCidr parses a "dotted-quad" IPv4 netmask (e.g. "255.255.255.0" for a /24) and returns am IPv4 CIDR/bit size/prefix length.
|
|
|
|
See also:
|
|
|
|
* [Mask4StrToIPMask]
|
|
* [Mask4StrToMask]
|
|
|
|
Inverse of [Cidr4ToMaskStr].
|
|
*/
|
|
func Mask4StrToCidr(maskStr string) (cidr uint8, err error) {
|
|
|
|
var ipMask net.IPMask
|
|
|
|
if ipMask, err = Mask4StrToIPMask(maskStr); err != nil {
|
|
return
|
|
}
|
|
|
|
if cidr, err = IPMask4ToCidr(ipMask); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Mask4StrToIPMask parses a "dotted-quad" IPv4 netmask (e.g. "255.255.255.0" for a /24) and returns a [net.IPMask].
|
|
|
|
See also:
|
|
|
|
* [Mask4StrToCidr]
|
|
* [Mask4StrToMask]
|
|
|
|
Inverse of [IPMask4ToStr].
|
|
*/
|
|
func Mask4StrToIPMask(maskStr string) (mask net.IPMask, err error) {
|
|
|
|
var idx int
|
|
var s string
|
|
var u64 uint64
|
|
var b []byte = make([]byte, 4)
|
|
var sl []string = strings.Split(maskStr, ".")
|
|
|
|
if len(sl) != 4 {
|
|
err = ErrBadMask4Str
|
|
return
|
|
}
|
|
|
|
// A net.IPMask is just a []byte.
|
|
for idx = 0; idx < len(sl); idx++ {
|
|
s = sl[idx]
|
|
if u64, err = strconv.ParseUint(s, 10, 8); err != nil {
|
|
return
|
|
}
|
|
b[idx] = byte(u64)
|
|
}
|
|
|
|
mask = net.IPMask(b)
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
Mask4StrToMask parses a "dotted-quad" IPv4 netmask (e.g. "255.255.255.0" for a /24) and returns a netmask *in bitmask form*.
|
|
|
|
See also:
|
|
|
|
* [Mask4StrToCidr]
|
|
* [Mask4StrToIPMask]
|
|
|
|
Inverse of [Mask4ToStr].
|
|
*/
|
|
func Mask4StrToMask(maskStr string) (mask uint32, err error) {
|
|
|
|
var ipMask net.IPMask
|
|
|
|
if ipMask, err = Mask4StrToIPMask(maskStr); err != nil {
|
|
return
|
|
}
|
|
|
|
if mask, err = IPMask4ToMask(ipMask); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
/*
|
|
VerToFamily takes a "human-readable" IP version ipVer (4 or 6) and returns
|
|
a system-level constant (e.g. [AFUnspec], [AFInet], [AFInet6]).
|
|
|
|
If not a known IP version (i.e. neither 4 nor 6), family will be [AFUnspec].
|
|
|
|
It is the inverse of [FamilyToVer].
|
|
*/
|
|
func VerToFamily(ipVer int) (family uint16) {
|
|
|
|
switch ipVer {
|
|
case 4:
|
|
family = AFInet
|
|
case 6:
|
|
family = AFInet6
|
|
default:
|
|
family = AFUnspec
|
|
}
|
|
|
|
return
|
|
}
|