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 simmply 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 } /* GetAddrFamily returns the network family of a [net/netip.Addr]. See also [GetIpFamily]. 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]. 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. */ 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 } /* 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 } /* 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 }