netutil

package
v0.31.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 9, 2024 License: Unlicense Imports: 13 Imported by: 38

Documentation

Overview

Package netutil contains common utilities for IP, MAC, and other kinds of network addresses.

Index

Examples

Constants

View Source
const (
	// ErrNotAReversedIP is the underlying error returned from validation
	// functions when a domain name is not a full reversed IP address.
	ErrNotAReversedIP errors.Error = "not a full reversed ip address"

	// ErrNotAReversedSubnet is the underlying error returned from validation
	// functions when a domain name is not a valid reversed IP network.
	ErrNotAReversedSubnet errors.Error = "not a reversed ip network"
)
View Source
const (
	IPv4BitLen = net.IPv4len * 8
	IPv6BitLen = net.IPv6len * 8
)

Bit lengths of IP addresses.

View Source
const MaxDomainLabelLen = 63

MaxDomainLabelLen is the maximum allowed length of a domain name label according to RFC 1035.

View Source
const MaxDomainNameLen = 253

MaxDomainNameLen is the maximum allowed length of a full domain name according to RFC 1035.

See also: https://stackoverflow.com/a/32294443/1892060.

View Source
const MaxServiceLabelLen = 16

MaxServiceLabelLen is the maximum allowed length of a service name label according to RFC 6335.

Variables

This section is empty.

Functions

func CloneIPs

func CloneIPs(ips []net.IP) (clone []net.IP)

CloneIPs returns a deep clone of ips.

func CloneURL

func CloneURL(u *url.URL) (clone *url.URL)

CloneURL returns a deep clone of u. The User pointer of clone is the same, since a *url.Userinfo is effectively an immutable value.

func ExtractReversedAddr added in v0.20.4

func ExtractReversedAddr(domain string) (pref netip.Prefix, err error)

ExtractReversedAddr searches for an ARPA subdomain within domain and returns the encoded prefix. It returns ErrNotAReversedSubnet if domain contains no valid ARPA subdomain. The root ARPA domains "in-addr.arpa" and "ip6.arpa" even within a longer domain name are considered valid and turn into the zero prefixes of the corresponding family. domain can be a domain name or an FQDN. The returned error will have the underlying type of *AddrError.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

// check is a helper function for examples that panics on error.
func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	pref, err := netutil.ExtractReversedAddr("_some-srv.10.in-addr.arpa")
	check(err)

	fmt.Println(pref)

	pref, err = netutil.ExtractReversedAddr("_some-srv.1.0.0.0.0.0.0.0.0.0.0.0.0.4.3.2.1.ip6.arpa")
	check(err)

	fmt.Println(pref)

	pref, err = netutil.ExtractReversedAddr("_some-srv.in-addr.arpa")
	check(err)

	fmt.Println(pref)

	pref, err = netutil.ExtractReversedAddr("_some-srv.ip6.arpa")
	check(err)

	fmt.Println(pref)

}
Output:


10.0.0.0/8
1234::1000:0:0:0/68
0.0.0.0/0
::/0

func IPAndPortFromAddr

func IPAndPortFromAddr(addr net.Addr) (ip net.IP, port uint16)

IPAndPortFromAddr returns the IP address and the port from addr. If addr is neither a *net.TCPAddr nor a *net.UDPAddr, it returns nil and 0. The port of the address should fit in uint16.

func IPFromReversedAddr

func IPFromReversedAddr(arpa string) (addr netip.Addr, err error)

IPFromReversedAddr tries to convert a full reversed ARPA address to a normal IP address. arpa can be domain name or an FQDN.

Any error returned will have the underlying type of *AddrError.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

// check is a helper function for examples that panics on error.
func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	ip, err := netutil.IPFromReversedAddr("4.3.2.1.in-addr.arpa")
	check(err)

	fmt.Println(ip)

	a := `4.3.2.1.d.c.b.a.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa`
	ip, err = netutil.IPFromReversedAddr(a)
	check(err)

	fmt.Println(ip)

}
Output:


1.2.3.4
::abcd:1234

func IPNetToPrefix added in v0.11.0

func IPNetToPrefix(subnet *net.IPNet, fam AddrFamily) (p netip.Prefix, err error)

IPNetToPrefix is a helper that converts a *net.IPNet into a netip.Prefix. subnet should not be nil. fam must be either AddrFamilyIPv4 or AddrFamilyIPv6.

See also IPNetToPrefixNoMapped.

Example
package main

import (
	"fmt"
	"net"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	_, n, _ := net.ParseCIDR("1.2.3.0/24")
	pref, err := netutil.IPNetToPrefix(n, netutil.AddrFamilyIPv4)
	fmt.Printf("%q, error: %v\n", pref, err)

	pref, err = netutil.IPNetToPrefix(n, netutil.AddrFamilyIPv6)
	fmt.Printf("%q, error: %v\n", pref, err)

	_, n, _ = net.ParseCIDR("1234::/72")
	pref, err = netutil.IPNetToPrefix(n, netutil.AddrFamilyIPv4)
	fmt.Printf("%q, error: %v\n", pref, err)

	pref, err = netutil.IPNetToPrefix(n, netutil.AddrFamilyIPv6)
	fmt.Printf("%q, error: %v\n", pref, err)

}
Output:

"1.2.3.0/24", error: <nil>
"::ffff:1.2.3.0/24", error: <nil>
"invalid Prefix", error: bad ip for subnet 1234::/72: bad ipv4 net.IP 1234::
"1234::/72", error: <nil>

func IPNetToPrefixNoMapped added in v0.11.0

func IPNetToPrefixNoMapped(subnet *net.IPNet) (p netip.Prefix, err error)

IPNetToPrefixNoMapped is like IPNetToPrefix but it detects the address family automatically by assuming that every IPv6-mapped IPv4 address is actually an IPv4 address. Do not use IPNetToPrefixNoMapped where this assumption isn't safe.

Example
package main

import (
	"fmt"
	"net"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	_, n, _ := net.ParseCIDR("1.2.3.0/24")
	pref, err := netutil.IPNetToPrefixNoMapped(n)
	fmt.Printf("%q, error: %v\n", pref, err)

	n = &net.IPNet{
		IP:   net.IP{1, 2, 3, 0},
		Mask: net.CIDRMask(24, 32),
	}
	pref, err = netutil.IPNetToPrefixNoMapped(n)
	fmt.Printf("%q, error: %v\n", pref, err)

	_, n, _ = net.ParseCIDR("1234::/72")
	pref, err = netutil.IPNetToPrefixNoMapped(n)
	fmt.Printf("%q, error: %v\n", pref, err)

}
Output:

"1.2.3.0/24", error: <nil>
"1.2.3.0/24", error: <nil>
"1234::/72", error: <nil>

func IPToAddr added in v0.11.0

func IPToAddr(ip net.IP, fam AddrFamily) (addr netip.Addr, err error)

IPToAddr converts a net.IP into a netip.Addr of the given family and returns a meaningful error. ip should not be nil. fam must be either AddrFamilyIPv4 or AddrFamilyIPv6.

See also IPToAddrNoMapped.

Example
package main

import (
	"fmt"
	"net"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	ip := net.ParseIP("1.2.3.4")
	addr, err := netutil.IPToAddr(ip, netutil.AddrFamilyIPv4)
	fmt.Printf("%q, error: %v\n", addr, err)

	addr, err = netutil.IPToAddr(ip, netutil.AddrFamilyIPv6)
	fmt.Printf("%q, error: %v\n", addr, err)

	ip = net.ParseIP("1234::5678")
	addr, err = netutil.IPToAddr(ip, netutil.AddrFamilyIPv4)
	fmt.Printf("%q, error: %v\n", addr, err)

	addr, err = netutil.IPToAddr(ip, netutil.AddrFamilyIPv6)
	fmt.Printf("%q, error: %v\n", addr, err)

}
Output:

"1.2.3.4", error: <nil>
"::ffff:1.2.3.4", error: <nil>
"invalid IP", error: bad ipv4 net.IP 1234::5678
"1234::5678", error: <nil>

func IPToAddrNoMapped added in v0.11.0

func IPToAddrNoMapped(ip net.IP) (addr netip.Addr, err error)

IPToAddrNoMapped is like IPToAddr but it detects the address family automatically by assuming that every IPv6-mapped IPv4 address is actually an IPv4 address. Do not use IPToAddrNoMapped where this assumption isn't safe.

Example
package main

import (
	"fmt"
	"net"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	ip := net.ParseIP("1.2.3.4")
	addr, err := netutil.IPToAddrNoMapped(ip)
	fmt.Printf("%q, error: %v\n", addr, err)

	ip = net.IP{1, 2, 3, 4}
	addr, err = netutil.IPToAddrNoMapped(ip)
	fmt.Printf("%q, error: %v\n", addr, err)

	ip = net.ParseIP("1234::5678")
	addr, err = netutil.IPToAddrNoMapped(ip)
	fmt.Printf("%q, error: %v\n", addr, err)

}
Output:

"1.2.3.4", error: <nil>
"1.2.3.4", error: <nil>
"1234::5678", error: <nil>

func IPToReversedAddr

func IPToReversedAddr(ip net.IP) (arpa string, err error)

IPToReversedAddr returns the reversed ARPA address of ip suitable for reverse DNS (PTR) record lookups. This is a modified version of function ReverseAddr from package github.com/miekg/dns package that accepts an IP.

Any error returned will have the underlying type of *AddrError.

Example
package main

import (
	"fmt"
	"net"

	"github.com/AdguardTeam/golibs/netutil"
)

// check is a helper function for examples that panics on error.
func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	arpa, err := netutil.IPToReversedAddr(net.IP{1, 2, 3, 4})
	check(err)

	fmt.Println(arpa)

	ip := net.ParseIP("::abcd:1234")
	arpa, err = netutil.IPToReversedAddr(ip)
	check(err)

	fmt.Println(arpa)

}
Output:


4.3.2.1.in-addr.arpa
4.3.2.1.d.c.b.a.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa

func IPv4Localhost added in v0.11.0

func IPv4Localhost() (ip netip.Addr)

IPv4Localhost returns the IPv4 localhost address "127.0.0.1".

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	fmt.Println(netutil.IPv4Localhost())

}
Output:

127.0.0.1

func IPv4Zero added in v0.9.2

func IPv4Zero() (ip net.IP)

IPv4Zero returns a new unspecified (aka empty or null) IPv4 address, 0.0.0.0. It has the same name as the variable in package net, but the result always has four bytes.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	fmt.Println(netutil.IPv4Zero())

}
Output:


0.0.0.0

func IPv4allrouter added in v0.9.3

func IPv4allrouter() (ip net.IP)

IPv4allrouter returns a new all routers IPv4 address, 224.0.0.2. It has the same name as the variable in package net, but the result always has four bytes.

func IPv4allsys added in v0.9.3

func IPv4allsys() (ip net.IP)

IPv4allsys returns a new all systems (aka all hosts) IPv4 address, 224.0.0.1. It has the same name as the variable in package net, but the result always has four bytes.

func IPv4bcast added in v0.9.3

func IPv4bcast() (ip net.IP)

IPv4bcast returns a new limited broadcast IPv4 address, 255.255.255.255. It has the same name as the variable in package net, but the result always has four bytes.

func IPv6Localhost added in v0.11.0

func IPv6Localhost() (ip netip.Addr)

IPv6Localhost returns the IPv6 localhost address "::1".

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	fmt.Println(netutil.IPv6Localhost())

}
Output:

::1

func IPv6Zero added in v0.9.2

func IPv6Zero() (ip net.IP)

IPv6Zero returns a new unspecified (aka empty or null) IPv6 address, [::]. It has the same name as the variable in package net.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	fmt.Println(netutil.IPv6Zero())

}
Output:


::

func IsImmediateSubdomain added in v0.11.3

func IsImmediateSubdomain(domain, top string) (ok bool)

IsImmediateSubdomain returns true if domain is an immediate subdomain of top. domain and top should be valid domain names, qualified in the same manner, and have the same letter case.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	printResult := func(name string, isImmSub bool) {
		fmt.Printf("%-14s: %5t\n", name, isImmSub)
	}

	printResult("same domain", netutil.IsImmediateSubdomain("sub.example.com", "example.com"))
	printResult("empty", netutil.IsImmediateSubdomain("", ""))
	printResult("same", netutil.IsImmediateSubdomain("example.com", "example.com"))
	printResult("dot only", netutil.IsImmediateSubdomain(".example.com", "example.com"))
	printResult("backwards", netutil.IsImmediateSubdomain("example.com", "sub.example.com"))
	printResult("other domain", netutil.IsImmediateSubdomain("sub.example.com", "example.org"))
	printResult("not immediate", netutil.IsImmediateSubdomain("sub.sub.example.com", "example.com"))
	printResult("similar 1", netutil.IsImmediateSubdomain("sub.myexample.com", "example.org"))
	printResult("similar 2", netutil.IsImmediateSubdomain("sub.example.com", "myexample.org"))

}
Output:

same domain   :  true
empty         : false
same          : false
dot only      : false
backwards     : false
other domain  : false
not immediate : false
similar 1     : false
similar 2     : false

func IsLocallyServed added in v0.10.8

func IsLocallyServed(ip netip.Addr) (ok bool)

IsLocallyServed checks if ip belongs to any network defined by RFC 6303:

10.0.0.0/8
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.2.0/24
192.168.0.0/16
198.51.100.0/24
203.0.113.0/24
255.255.255.255/32

::/128
::1/128
2001:db8::/32
fd00::/8
fe80::/10

It may also be used as a SubnetSetFunc.

func IsSpecialPurpose added in v0.10.8

func IsSpecialPurpose(ip netip.Addr) (ok bool)

IsSpecialPurpose checks if ip belongs to any network defined by IANA Special-Purpose Address Registry:

0.0.0.0/8          "This host on this network".
10.0.0.0/8         Private-Use.
100.64.0.0/10      Shared Address Space.
127.0.0.0/8        Loopback.
169.254.0.0/16     Link Local.
172.16.0.0/12      Private-Use.
192.0.0.0/24       IETF Protocol Assignments.
192.0.0.0/29       DS-Lite.
192.0.2.0/24       Documentation (TEST-NET-1)
192.88.99.0/24     6to4 Relay Anycast.
192.168.0.0/16     Private-Use.
198.18.0.0/15      Benchmarking.
198.51.100.0/24    Documentation (TEST-NET-2).
203.0.113.0/24     Documentation (TEST-NET-3).
240.0.0.0/4        Reserved.
255.255.255.255/32 Limited Broadcast.

::/128            Unspecified Address.
::1/128           Loopback Address.
64:ff9b::/96      IPv4-IPv6 Translation Address.
64:ff9b:1::/48    IPv4-IPv6 Translation Address.
100::/64          Discard-Only Address Block.
2001::/23         IETF Protocol Assignments.
2001::/32         TEREDO.
2001:1::1/128     Port Control Protocol Anycast.
2001:1::2/128     Traversal Using Relays around NAT Anycast.
2001:2::/48       Benchmarking.
2001:3::/32       AMT.
2001:4:112::/48   AS112-v6.
2001:10::/28      ORCHID.
2001:20::/28      ORCHIDv2.
2001:db8::/32     Documentation.
2002::/16         6to4.
2620:4f:8000::/48 Direct Delegation AS112 Service.
fc00::/7          Unique-Local.
fe80::/10         Linked-Scoped Unicast.

See https://www.iana.org/assignments/iana-ipv4-special-registry and https://www.iana.org/assignments/iana-ipv6-special-registry.

It may also be used as a SubnetSetFunc.

func IsSubdomain added in v0.11.3

func IsSubdomain(domain, top string) (ok bool)

IsSubdomain returns true if domain is a subdomain of top. domain and top should be valid domain names, qualified in the same manner, and have the same letter case.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	printResult := func(name string, isImmSub bool) {
		fmt.Printf("%-14s: %5t\n", name, isImmSub)
	}

	printResult("same domain", netutil.IsSubdomain("sub.example.com", "example.com"))
	printResult("not immediate", netutil.IsSubdomain("sub.sub.example.com", "example.com"))
	printResult("empty", netutil.IsSubdomain("", ""))
	printResult("same", netutil.IsSubdomain("example.com", "example.com"))
	printResult("dot only", netutil.IsSubdomain(".example.com", "example.com"))
	printResult("backwards", netutil.IsSubdomain("example.com", "sub.example.com"))
	printResult("other domain", netutil.IsSubdomain("sub.example.com", "example.org"))
	printResult("similar 1", netutil.IsSubdomain("sub.myexample.com", "example.org"))
	printResult("similar 2", netutil.IsSubdomain("sub.example.com", "myexample.org"))

}
Output:

same domain   :  true
not immediate :  true
empty         : false
same          : false
dot only      : false
backwards     : false
other domain  : false
similar 1     : false
similar 2     : false

func IsValidHostInnerRune

func IsValidHostInnerRune(r rune) (ok bool)

IsValidHostInnerRune returns true if r is a valid inner—that is, neither initial nor final—rune for a hostname label.

func IsValidHostOuterRune

func IsValidHostOuterRune(r rune) (ok bool)

IsValidHostOuterRune returns true if r is a valid initial or final rune for a hostname label.

func IsValidHostname added in v0.25.1

func IsValidHostname(name string) (ok bool)

IsValidHostname returns true if name is in accordance to [RFC 952], [RFC 1035], and with [RFC 1123]'s inclusion of digits at the start of the host. It doesn't validate against two or more hyphens to allow punycode and internationalized domains.

It replicates the behavior of ValidateHostname, but allocates less.

TODO(e.burkov): Validate non-ASCII domain names separately.

func IsValidHostnameLabel added in v0.25.1

func IsValidHostnameLabel(label string) (ok bool)

IsValidHostnameLabel returns false if label is not a valid label of a host name. An empty label is considered invalid. label should only contain ASCII characters, use idna.ToASCII for converting non-ASCII labels.

It replicates the behavior of ValidateHostnameLabel, but doesn't allocate.

func IsValidIPString added in v0.25.1

func IsValidIPString(s string) (ok bool)

IsValidIPString returns true if s is a valid IPv4 or IPv6 address string representation as accepted by netip.ParseAddr.

func JoinHostPort

func JoinHostPort(host string, port uint16) (hostport string)

JoinHostPort is a convenient wrapper for net.JoinHostPort with port of type uint16. As opposed to net.JoinHostPort it also trims the host from square brackets if any. This may be useful when passing url.URL.Host field containing an IPv6 address.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	fmt.Println(netutil.JoinHostPort("example.com", 12345))

}
Output:


example.com:12345

func NetAddrToAddrPort added in v0.11.3

func NetAddrToAddrPort(addr net.Addr) (addrPort netip.AddrPort)

NetAddrToAddrPort converts a net.Addr into a netip.AddrPort if it can. Otherwise, it returns an empty netip.AddrPort. addr must not be nil.

Since net.TCPAddr.AddrPort and net.UDPAddr.AddrPort perform a naïve conversion of their net.IP values into netip.Addr ones, that does not take mapped addresses into account, IPv4-mapped IPv6 addresses are assumed to actually be IPv4 addresses and are normalized into them.

Those who want a conversion without this normalization may use:

if ap, ok := addr.(interface{ AddrPort() (a netip.AddrPort) }); ok {
	return ap.AddrPort()
}

See https://github.com/golang/go/issues/53607.

Example
package main

import (
	"fmt"
	"net"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	tcpAddr := &net.TCPAddr{
		// NOTE: This produces a 16-byte form of the IPv4 address.
		IP:   net.IPv4(1, 2, 3, 4),
		Port: 56789,
	}

	addrPort := netutil.NetAddrToAddrPort(tcpAddr)
	fmt.Println(addrPort)
	fmt.Println(addrPort.Addr().Is4())
	fmt.Println(addrPort.Addr().Is4In6())

}
Output:

1.2.3.4:56789
true
false

func ParseIP

func ParseIP(s string) (ip net.IP, err error)

ParseIP is a wrapper around net.ParseIP that returns a useful error.

Any error returned will have the underlying type of *AddrError.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	ip, err := netutil.ParseIP("1.2.3.4")
	fmt.Println(ip, err)

	ip, err = netutil.ParseIP("1234::cdef")
	fmt.Println(ip, err)

	ip, err = netutil.ParseIP("!!!")
	fmt.Println(ip, err)

}
Output:


1.2.3.4 <nil>
1234::cdef <nil>
<nil> bad ip address "!!!"

func ParseIPv4

func ParseIPv4(s string) (ip net.IP, err error)

ParseIPv4 is a wrapper around net.ParseIP that makes sure that the parsed IP is an IPv4 address and returns a useful error.

Any error returned will have the underlying type of either *AddrError.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	ip, err := netutil.ParseIPv4("1.2.3.4")
	fmt.Println(ip, err)

	ip, err = netutil.ParseIPv4("1234::cdef")
	fmt.Println(ip, err)

	ip, err = netutil.ParseIPv4("!!!")
	fmt.Println(ip, err)

}
Output:


1.2.3.4 <nil>
<nil> bad ipv4 address "1234::cdef"
<nil> bad ipv4 address "!!!"

func PreferIPv4 added in v0.20.4

func PreferIPv4(a, b netip.Addr) (res int)

PreferIPv4 compares two addresses, preferring IPv4 addresses over IPv6 ones. It's intended to be used as a compare function in slices.SortFunc. Invalid addresses are sorted near the end.

Example
addrs := []netip.Addr{
	testIPv6Addr.Next(),
	testIPv6Addr,
	{},
	testIPv4Addr.Next(),
	testIPv4Addr,
}
slices.SortFunc(addrs, netutil.PreferIPv4)

fmt.Printf("%q\n", addrs)
Output:


["1.2.3.4" "1.2.3.5" "1234::cdef" "1234::cdf0" "invalid IP"]

func PreferIPv6 added in v0.20.4

func PreferIPv6(a, b netip.Addr) (res int)

PreferIPv6 compares two addresses, preferring IPv6 addresses over IPv4 ones. It's intended to be used as a compare function in slices.SortFunc. Invalid addresses are sorted near the end.

Example
addrs := []netip.Addr{
	testIPv4Addr.Next(),
	testIPv4Addr,
	{},
	testIPv6Addr.Next(),
	testIPv6Addr,
}
slices.SortFunc(addrs, netutil.PreferIPv6)

fmt.Printf("%q\n", addrs)
Output:


["1234::cdef" "1234::cdf0" "1.2.3.4" "1.2.3.5" "invalid IP"]

func PrefixFromReversedAddr added in v0.19.0

func PrefixFromReversedAddr(arpa string) (p netip.Prefix, err error)

PrefixFromReversedAddr tries to convert a reversed ARPA address to an IP address prefix. The root ARPA domains "in-addr.arpa" and "ip6.arpa" are considered valid and turn into the zero prefixes of the corresponding family. arpa can be domain name or an FQDN.

Any error returned will have the underlying type of *AddrError.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

// check is a helper function for examples that panics on error.
func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	pref, err := netutil.PrefixFromReversedAddr("10.in-addr.arpa")
	check(err)

	fmt.Println(pref)

	pref, err = netutil.PrefixFromReversedAddr("1.0.0.0.0.0.0.0.0.0.0.0.0.4.3.2.1.ip6.arpa")
	check(err)

	fmt.Println(pref)

	pref, err = netutil.PrefixFromReversedAddr("in-addr.arpa")
	check(err)

	fmt.Println(pref)

	pref, err = netutil.PrefixFromReversedAddr("ip6.arpa")
	check(err)

	fmt.Println(pref)

}
Output:


10.0.0.0/8
1234::1000:0:0:0/68
0.0.0.0/0
::/0

func SplitHost

func SplitHost(hostport string) (host string, err error)

SplitHost is a wrapper for net.SplitHostPort for cases when the hostport may or may not contain a port.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	host, err := netutil.SplitHost("example.com:12345")
	if err != nil {
		panic(err)
	}

	fmt.Println(host)

	host, err = netutil.SplitHost("example.org")
	if err != nil {
		panic(err)
	}

	fmt.Println(host)

	_, err = netutil.SplitHost("[BAD:!")
	fmt.Println(err)

}
Output:


example.com
example.org
address [BAD:!: missing ']' in address

func SplitHostPort

func SplitHostPort(hostport string) (host string, port uint16, err error)

SplitHostPort is a convenient wrapper for net.SplitHostPort with port of type uint16.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	host, port, err := netutil.SplitHostPort("example.com:12345")
	if err != nil {
		panic(err)
	}

	fmt.Printf("%T(%[1]v)\n", host)
	fmt.Printf("%T(%[1]v)\n", port)

}
Output:


string(example.com)
uint16(12345)

func Subdomains added in v0.9.3

func Subdomains(domain string) (sub []string)

Subdomains returns all subdomains of domain, starting from domain itself. domain must be a valid, non-fully-qualified domain name. If domain is empty, Subdomains returns nil.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	fmt.Printf("%#v\n", netutil.Subdomains("subsub.sub.domain.tld"))

	fmt.Println()

	fmt.Printf("%#v\n", netutil.Subdomains(""))

}
Output:


[]string{"subsub.sub.domain.tld", "sub.domain.tld", "domain.tld", "tld"}

[]string(nil)

func UnembedPrefixes added in v0.19.0

func UnembedPrefixes(embed []Prefix) (ps []netip.Prefix)

UnembedPrefixes returns a slice of netip.Prefix from embed slice.

Example
package main

import (
	"fmt"
	"net/netip"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	addr := netip.MustParseAddr("1.2.3.4")
	stdPref := netip.PrefixFrom(addr, 16)

	p := netutil.Prefix{
		Prefix: stdPref,
	}

	fmt.Println(netutil.UnembedPrefixes([]netutil.Prefix{p}))

}
Output:


[1.2.3.4/16]

func ValidateDomainName

func ValidateDomainName(name string) (err error)

ValidateDomainName validates the domain name in accordance to RFC 1035 and RFC 3696 Section 2. As opposed to ValidateHostname, this function only validates the lengths of the name itself and its labels, except the TLD.

Any error returned will have the underlying type of *AddrError.

func ValidateDomainNameLabel

func ValidateDomainNameLabel(label string) (err error)

ValidateDomainNameLabel returns an error if label is not a valid label of a domain name. An empty label is considered invalid. Essentially it validates the length of the label since the name in DNS is permitted to contain any printable ASCII character, see RFC 3696 Section 2. label should only contain ASCII characters, use idna.ToASCII for converting non-ASCII labels.

Any error returned will have the underlying type of *LabelError.

func ValidateHostname added in v0.12.0

func ValidateHostname(name string) (err error)

ValidateHostname validates the domain name in accordance to RFC 952, RFC 1035, and with RFC 1123's inclusion of digits at the start of the host. It doesn't validate against two or more hyphens to allow punycode and internationalized domains.

Any error returned will have the underlying type of *AddrError.

func ValidateHostnameLabel added in v0.12.0

func ValidateHostnameLabel(label string) (err error)

ValidateHostnameLabel returns an error if label is not a valid label of a host name. An empty label is considered invalid. label should only contain ASCII characters, use idna.ToASCII for converting non-ASCII labels.

Any error returned will have the underlying type of *LabelError.

func ValidateIP added in v0.9.2

func ValidateIP(ip net.IP) (err error)

ValidateIP returns an error if ip is not a valid IPv4 or IPv6 address.

Any error returned will have the underlying type of *AddrError.

func ValidateMAC

func ValidateMAC(mac net.HardwareAddr) (err error)

ValidateMAC returns an error if mac is not a valid EUI-48, EUI-64, or 20-octet InfiniBand link-layer address.

Any error returned will have the underlying type of *AddrError.

func ValidateSRVDomainName added in v0.10.4

func ValidateSRVDomainName(name string) (err error)

ValidateSRVDomainName validates name assuming it belongs to the superset of service domain names in accordance to RFC 2782 and RFC 6763. It doesn't validate against two or more hyphens to allow punycode and internationalized domains.

Any error returned will have the underlying type of *AddrError.

func ValidateServiceNameLabel added in v0.10.4

func ValidateServiceNameLabel(label string) (err error)

ValidateServiceNameLabel returns an error if label is not a valid label of a service domain name. An empty label is considered invalid. label should only contain ASCII characters, use idna.ToASCII for converting non-ASCII labels.

Any error returned will have the underlying type of *LabelError.

func ValidateTLDLabel added in v0.12.0

func ValidateTLDLabel(tld string) (err error)

ValidateTLDLabel validates a top-level domain label in accordance to RFC 3696 Section 2. An empty label is considered invalid. In addition to the ValidateHostnameLabel validation, it also checks that the label contains at least one non-digit character. label should only contain ASCII characters, use idna.ToASCII for converting non-ASCII labels.

Any error returned will have the underlying type of *LabelError.

func ZeroPrefix added in v0.11.0

func ZeroPrefix(fam AddrFamily) (n netip.Prefix)

ZeroPrefix returns an IP subnet with prefix 0 and all bytes of the IP address set to 0. fam must be either AddrFamilyIPv4 or AddrFamilyIPv6.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	fmt.Println(netutil.ZeroPrefix(netutil.AddrFamilyIPv4))
	fmt.Println(netutil.ZeroPrefix(netutil.AddrFamilyIPv6))

	// Invalid value for [netutil.AddrFamily].
	func() {
		defer func() { fmt.Println(recover()) }()

		fmt.Println(netutil.ZeroPrefix(1234))
	}()

}
Output:

0.0.0.0/0
::/0
netutil.ZeroPrefix: bad address family 1234

Types

type AddrError

type AddrError struct {
	// Err is the underlying error, if any.
	Err error

	// Kind is the kind of address or address part.
	Kind AddrKind

	// Addr is the text of the invalid address.
	Addr string
}

AddrError is the underlying type of errors returned from validation functions when a domain name is invalid.

func (*AddrError) Error

func (err *AddrError) Error() (msg string)

Error implements the error interface for *AddrError.

func (*AddrError) Unwrap

func (err *AddrError) Unwrap() (unwrapped error)

Unwrap implements the errors.Wrapper interface for *AddrError. It returns err.Err.

type AddrFamily added in v0.11.0

type AddrFamily uint16

AddrFamily is the type for IANA address family numbers.

const (
	AddrFamilyNone AddrFamily = 0
	AddrFamilyIPv4 AddrFamily = 1
	AddrFamilyIPv6 AddrFamily = 2
)

An incomplete list of IANA address family numbers.

See https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml.

func AddrFamilyFromRRType added in v0.11.0

func AddrFamilyFromRRType(rr uint16) (fam AddrFamily)

AddrFamilyFromRRType returns an AddrFamily appropriate for the DNS resource record type rr. That is, AddrFamilyIPv4 for DNS type A (1), AddrFamilyIPv6 for DNS type AAAA (28), and AddrFamilyNone otherwise.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	// DNS type A.
	fmt.Println(netutil.AddrFamilyFromRRType(1))

	// DNS type AAAA.
	fmt.Println(netutil.AddrFamilyFromRRType(28))

	// Other DNS type.
	fmt.Println(netutil.AddrFamilyFromRRType(1234))

}
Output:

ipv4
ipv6
none

func (AddrFamily) String added in v0.11.0

func (f AddrFamily) String() (s string)

String implements the fmt.Stringer interface for AddrFamily.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	fmt.Println(netutil.AddrFamilyIPv4, netutil.AddrFamilyIPv6)

	// An empty family.
	var fam netutil.AddrFamily
	fmt.Println(fam)

	// An unsupported family.
	fam = netutil.AddrFamily(1234)
	fmt.Println(fam)

}
Output:

ipv4 ipv6
none
!bad_addr_fam_1234

type AddrKind

type AddrKind = string

AddrKind is a convenient alias for string describing the kind of address and used for error reporting.

const (
	AddrKindARPA       AddrKind = "arpa domain name"
	AddrKindCIDR       AddrKind = "cidr address"
	AddrKindHostPort   AddrKind = "hostport address"
	AddrKindIP         AddrKind = "ip address"
	AddrKindIPPort     AddrKind = "ipport address"
	AddrKindIPv4       AddrKind = "ipv4 address"
	AddrKindMAC        AddrKind = "mac address"
	AddrKindName       AddrKind = "hostname"
	AddrKindDomainName AddrKind = "domain name"
	AddrKindSRVName    AddrKind = "service domain name"
)

Kinds of addresses for AddrError.

type HostPort

type HostPort struct {
	Host string
	Port uint16
}

HostPort is a convenient type for addresses that contain a hostname and a port, like "example.com:12345", "1.2.3.4:56789", or "[1234::cdef]:12345".

func CloneHostPorts

func CloneHostPorts(hps []*HostPort) (clone []*HostPort)

CloneHostPorts returns a deep copy of hps.

func ParseHostPort

func ParseHostPort(addr string) (hp *HostPort, err error)

ParseHostPort parses a HostPort from addr. Any error returned will have the underlying type of *AddrError.

func (*HostPort) Clone

func (hp *HostPort) Clone() (clone *HostPort)

Clone returns a clone of hp.

func (HostPort) MarshalText

func (hp HostPort) MarshalText() (b []byte, err error)

MarshalText implements the encoding.TextMarshaler interface for HostPort.

Example
package main

import (
	"encoding/json"
	"os"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	resp := struct {
		Hosts []netutil.HostPort `json:"hosts"`
	}{
		Hosts: []netutil.HostPort{{
			Host: "example.com",
			Port: 12345,
		}, {
			Host: "example.org",
			Port: 23456,
		}},
	}

	err := json.NewEncoder(os.Stdout).Encode(resp)
	if err != nil {
		panic(err)
	}

	respPtrs := struct {
		HostPtrs []*netutil.HostPort `json:"host_ptrs"`
	}{
		HostPtrs: []*netutil.HostPort{{
			Host: "example.com",
			Port: 12345,
		}, {
			Host: "example.org",
			Port: 23456,
		}},
	}

	err = json.NewEncoder(os.Stdout).Encode(respPtrs)
	if err != nil {
		panic(err)
	}

}
Output:


{"hosts":["example.com:12345","example.org:23456"]}
{"host_ptrs":["example.com:12345","example.org:23456"]}

func (HostPort) String

func (hp HostPort) String() (s string)

String implements the fmt.Stringer interface for *HostPort.

Example
package main

import (
	"fmt"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	hp := &netutil.HostPort{
		Host: "example.com",
		Port: 12345,
	}

	fmt.Println(hp)

	hp.Host = "1234::cdef"
	fmt.Println(hp)

	hp.Port = 0
	fmt.Println(hp)

	hp.Host = ""
	fmt.Println(hp)

}
Output:


example.com:12345
[1234::cdef]:12345
[1234::cdef]:0
:0

func (*HostPort) UnmarshalText

func (hp *HostPort) UnmarshalText(b []byte) (err error)

UnmarshalText implements the encoding.TextUnmarshaler interface for *HostPort. Any error returned will have the underlying type of *AddrError.

Example
package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	resp := &struct {
		Hosts []netutil.HostPort `json:"hosts"`
	}{}

	r := strings.NewReader(`{"hosts":["example.com:12345","example.org:23456"]}`)
	err := json.NewDecoder(r).Decode(resp)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", resp.Hosts[0])
	fmt.Printf("%#v\n", resp.Hosts[1])

	respPtrs := &struct {
		HostPtrs []*netutil.HostPort `json:"host_ptrs"`
	}{}

	r = strings.NewReader(`{"host_ptrs":["example.com:12345","example.org:23456"]}`)
	err = json.NewDecoder(r).Decode(respPtrs)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", respPtrs.HostPtrs[0])
	fmt.Printf("%#v\n", respPtrs.HostPtrs[1])
	fmt.Println()

	r = strings.NewReader(`{"host_ptrs":["example.com:99999","example.org:99999"]}`)
	err = json.NewDecoder(r).Decode(respPtrs)

	isOutOfRange := err.Error() == `bad hostport address "example.com:99999": `+
		`parsing port: strconv.ParseUint: parsing "99999": value out of range`
	fmt.Printf("got the expected error: %t", isOutOfRange)

}
Output:


netutil.HostPort{Host:"example.com", Port:0x3039}
netutil.HostPort{Host:"example.org", Port:0x5ba0}
&netutil.HostPort{Host:"example.com", Port:0x3039}
&netutil.HostPort{Host:"example.org", Port:0x5ba0}

got the expected error: true

type LabelError added in v0.12.0

type LabelError struct {
	// Err is the underlying error, if any.
	Err error

	// Kind is the kind of label of the name.
	Kind LabelKind

	// Label is the text of the invalid label.
	Label string
}

LabelError is the underlying type of errors returned from validation functions when a label of a name is invalid.

func (*LabelError) Error added in v0.12.0

func (err *LabelError) Error() (msg string)

Error implements the error interface for *LabelError.

func (*LabelError) Unwrap added in v0.12.0

func (err *LabelError) Unwrap() (unwrapped error)

Unwrap implements the errors.Wrapper interface for *LabelError. It returns Err error.

type LabelKind added in v0.12.0

type LabelKind = string

LabelKind is a convenient alias for string describing the kind of label of a name and used for error reporting.

const (
	LabelKindDomain LabelKind = "domain name label"
	LabelKindHost   LabelKind = "hostname label"
	LabelKindSRV    LabelKind = "service name label"
	LabelKindTLD    LabelKind = "top-level domain name label"
)

Kinds of labels for LabelError.

type LengthError

type LengthError struct {
	// Kind is the kind of address or address part.
	Kind string

	// Allowed are the allowed lengths for this kind of address.  If allowed
	// is empty, Max should be non-zero.
	Allowed []int

	// Max is the maximum length for this part or address kind.  If Max is
	// zero, Allowed should be non-empty.
	Max int

	// Length is the length of the provided address.
	Length int
}

LengthError is the underlying type of errors returned from validation functions when an address or a part of an address has a bad length. Kind is either AddrKind or LabelKind.

func (*LengthError) Error

func (err *LengthError) Error() (msg string)

Error implements the error interface for *LengthError.

type Prefix added in v0.19.0

type Prefix struct {
	netip.Prefix
}

Prefix is a wrapper for netip.Prefix providing more functionality in encoding. Unlike netip.Prefix it decodes IP addresses with unspecified mask bits (i.e. "127.0.0.1") as single-IP CIDR prefixes.

func (*Prefix) UnmarshalText added in v0.19.0

func (p *Prefix) UnmarshalText(b []byte) (err error)

UnmarshalText implements encoding.TextUnmarshaler interface for *Prefix.

Example
package main

import (
	"fmt"
	"net/netip"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	addr := netip.MustParseAddr("1.2.3.4")

	stdPref := netip.PrefixFrom(addr, 16)
	encPref, err := stdPref.MarshalText()
	if err != nil {
		panic(err)
	}

	p := netutil.Prefix{}
	err = p.UnmarshalText(encPref)
	fmt.Println(p, err)

	err = p.UnmarshalText([]byte("1.2.3.4"))
	fmt.Println(p, err)

}
Output:


1.2.3.4/16 <nil>
1.2.3.4/32 <nil>

type RuneError

type RuneError struct {
	// Kind is the kind of address or address part.
	Kind string

	// Rune is the invalid rune.
	Rune rune
}

RuneError is the underlying type of errors returned from validation functions when a rune in the address is invalid. Kind is either AddrKind or LabelKind.

func (*RuneError) Error

func (err *RuneError) Error() (msg string)

Error implements the error interface for *RuneError.

type SliceSubnetSet added in v0.10.8

type SliceSubnetSet []netip.Prefix

SliceSubnetSet is the SubnetSet that checks the address through a slice of netip.Prefix.

func (SliceSubnetSet) Contains added in v0.10.8

func (s SliceSubnetSet) Contains(ip netip.Addr) (ok bool)

Contains implements the SubnetSet interface for SliceSubnetSet.

Example
package main

import (
	"fmt"
	"net/netip"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	nets := []netip.Prefix{
		netip.MustParsePrefix("1.2.3.0/24"),
		netip.MustParsePrefix("ffff:12ab::/16"),
	}

	s := netutil.SliceSubnetSet(nets)

	fmt.Println("contains 1.2.3.4:      ", s.Contains(netip.MustParseAddr("1.2.3.4")))
	fmt.Println("contains 4.3.2.1:      ", s.Contains(netip.MustParseAddr("4.3.2.1")))
	fmt.Println("contains ffff:12ab::10:", s.Contains(netip.MustParseAddr("ffff:12ab::10")))
	fmt.Println("contains 12ab:ffff::10:", s.Contains(netip.MustParseAddr("12ab:ffff::10")))

	fmt.Println()

	s = netutil.SliceSubnetSet{}

	fmt.Println("contains 1.2.3.4:      ", s.Contains(netip.MustParseAddr("1.2.3.4")))
	fmt.Println("contains ffff:12ab::10:", s.Contains(netip.MustParseAddr("ffff:12ab::10")))

	fmt.Println()

	s = netutil.SliceSubnetSet{
		netip.MustParsePrefix("0.0.0.0/0"),
		netip.MustParsePrefix("::/0"),
	}

	fmt.Println("contains 1.2.3.4:      ", s.Contains(netip.MustParseAddr("1.2.3.4")))
	fmt.Println("contains ffff:12ab::10:", s.Contains(netip.MustParseAddr("ffff:12ab::10")))
	fmt.Println("contains zero value:   ", s.Contains(netip.Addr{}))

}
Output:


contains 1.2.3.4:       true
contains 4.3.2.1:       false
contains ffff:12ab::10: true
contains 12ab:ffff::10: false

contains 1.2.3.4:       false
contains ffff:12ab::10: false

contains 1.2.3.4:       true
contains ffff:12ab::10: true
contains zero value:    false

type SubnetSet added in v0.10.8

type SubnetSet interface {
	// Contains returns true if the given ip is contained by any network in the
	// set.
	Contains(ip netip.Addr) (ok bool)
}

SubnetSet represents a set of IP networks used to determine if an IP address belongs to any of them.

type SubnetSetFunc added in v0.10.8

type SubnetSetFunc func(ip netip.Addr) (ok bool)

SubnetSetFunc is the SubnetSet that checks the address with the function determining if the given ip belongs to the set of subnets.

Example
package main

import (
	"fmt"
	"net/netip"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	var (
		ipv4      = netip.MustParseAddr("1.2.3.4")
		ipv6      = netip.MustParseAddr("1234::cdef")
		ipInvalid = netip.Addr{}
	)

	var s netutil.SubnetSet = netutil.SubnetSetFunc(func(ip netip.Addr) (ok bool) {
		return true
	})

	fmt.Printf("all contains %s: %t\n", ipv4, s.Contains(ipv4))
	fmt.Printf("all contains %s: %t\n", ipv6, s.Contains(ipv6))
	fmt.Printf("all contains %s: %t\n", ipInvalid, s.Contains(ipInvalid))

	s = netutil.SubnetSetFunc(func(ip netip.Addr) (ok bool) {
		return false
	})

	fmt.Printf("none contains %s: %t\n", ipv4, s.Contains(ipv4))
	fmt.Printf("none contains %s: %t\n", ipv6, s.Contains(ipv6))
	fmt.Printf("none contains %s: %t\n", ipInvalid, s.Contains(ipInvalid))

	s = netutil.SubnetSetFunc(netip.Addr.IsValid)

	fmt.Printf("valid contains %s: %t\n", ipv4, s.Contains(ipv4))
	fmt.Printf("valid contains %s: %t\n", ipv6, s.Contains(ipv6))
	fmt.Printf("valid contains %s: %t\n", ipInvalid, s.Contains(ipInvalid))

}
Output:


all contains 1.2.3.4: true
all contains 1234::cdef: true
all contains invalid IP: true
none contains 1.2.3.4: false
none contains 1234::cdef: false
none contains invalid IP: false
valid contains 1.2.3.4: true
valid contains 1234::cdef: true
valid contains invalid IP: false

func (SubnetSetFunc) Contains added in v0.10.8

func (f SubnetSetFunc) Contains(ip netip.Addr) (ok bool)

Contains implements the SubnetSet interface for SubnetSetFunc. The ip is not required to be valid so that f is responsible for validation.

Example
package main

import (
	"fmt"
	"net/netip"

	"github.com/AdguardTeam/golibs/netutil"
)

func main() {
	s := netutil.SubnetSetFunc(func(ip netip.Addr) (ok bool) {
		slice := ip.AsSlice()

		return len(slice) > 0 && slice[0] == 0xFF
	})

	fmt.Println("contains 255.0.0.1:", s.Contains(netip.MustParseAddr("255.0.0.1")))
	fmt.Println("contains 254.0.0.1:", s.Contains(netip.MustParseAddr("254.0.0.1")))
	fmt.Println("contains ff00:::1: ", s.Contains(netip.MustParseAddr("ff00::1")))
	fmt.Println("contains ff:::1:   ", s.Contains(netip.MustParseAddr("ff::1")))

}
Output:


contains 255.0.0.1: true
contains 254.0.0.1: false
contains ff00:::1:  true
contains ff:::1:    false

Directories

Path Synopsis
Package httputil contains common constants, functions, and types for working with HTTP.
Package httputil contains common constants, functions, and types for working with HTTP.
Package sysresolv provides cross-platform functionality to discover DNS resolvers currently used by the system.
Package sysresolv provides cross-platform functionality to discover DNS resolvers currently used by the system.
Package urlutil contains types and utilities for dealing with URLs.
Package urlutil contains types and utilities for dealing with URLs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL