-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
netutil: add netip.Addr utils, depr old utils
- Loading branch information
Showing
10 changed files
with
374 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,16 @@ | ||
module github.com/AdguardTeam/golibs | ||
|
||
go 1.17 | ||
go 1.18 | ||
|
||
require ( | ||
github.com/stretchr/testify v1.7.1 | ||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 | ||
github.com/stretchr/testify v1.8.0 | ||
golang.org/x/exp v0.0.0-20221019170559-20944726eadf | ||
golang.org/x/net v0.1.0 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
golang.org/x/text v0.3.7 // indirect | ||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect | ||
golang.org/x/text v0.4.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package netutil | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
"net/netip" | ||
) | ||
|
||
// IPv4Localhost returns 127.0.0.1, which returns true for [netip.Addr.Is4]. | ||
// IPv4Unspecified returns the IPv4 unspecified address "0.0.0.0". | ||
func IPv4Localhost() (ip netip.Addr) { return netip.AddrFrom4([4]byte{127, 0, 0, 1}) } | ||
|
||
// IPv6Localhost returns ::1, which returns true for [netip.Addr.Is6]. | ||
func IPv6Localhost() (ip netip.Addr) { return netip.AddrFrom16([16]byte{15: 1}) } | ||
|
||
// 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]. | ||
func ZeroPrefix(fam AddrFamily) (n netip.Prefix) { | ||
switch fam { | ||
case AddrFamilyIPv4: | ||
return netip.PrefixFrom(netip.IPv4Unspecified(), 0) | ||
case AddrFamilyIPv6: | ||
return netip.PrefixFrom(netip.IPv6Unspecified(), 0) | ||
default: | ||
panic(badAddrFam("ZeroPrefix", fam)) | ||
} | ||
} | ||
|
||
// badAddrFam is a helper that returns an informative error for panics caused by | ||
// bad address-family values. | ||
func badAddrFam(fn string, fam AddrFamily) (err error) { | ||
return fmt.Errorf("netutil.%s: bad address family %s", fn, fam) | ||
} | ||
|
||
// IPToAddr converts a [net.IP] into a [netip.Addr] of the given family and | ||
// returns a meaningful error. fam must be either [AddrFamilyIPv4] or | ||
// [AddrFamilyIPv6]. | ||
// | ||
// See also [IPToAddrNoMapped]. | ||
func IPToAddr(ip net.IP, fam AddrFamily) (addr netip.Addr, err error) { | ||
switch fam { | ||
case AddrFamilyIPv4: | ||
// Make sure that we use the IPv4 form of the address to make sure that | ||
// netip.Addr doesn't turn out to be an IPv6 one when it really should | ||
// be an IPv4 one. | ||
ip4 := ip.To4() | ||
if ip4 == nil { | ||
return netip.Addr{}, fmt.Errorf("bad ipv4 net.IP %v", ip) | ||
} | ||
|
||
ip = ip4 | ||
case AddrFamilyIPv6: | ||
// Again, make sure that we use the correct form according to the | ||
// address family. | ||
ip = ip.To16() | ||
default: | ||
panic(badAddrFam("IPToAddr", fam)) | ||
} | ||
|
||
addr, ok := netip.AddrFromSlice(ip) | ||
if !ok { | ||
return netip.Addr{}, fmt.Errorf("bad net.IP value %v", ip) | ||
} | ||
|
||
return addr, nil | ||
} | ||
|
||
// 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. | ||
func IPToAddrNoMapped(ip net.IP) (addr netip.Addr, err error) { | ||
if ip4 := ip.To4(); ip4 != nil { | ||
return IPToAddr(ip4, AddrFamilyIPv4) | ||
} | ||
|
||
return IPToAddr(ip, AddrFamilyIPv6) | ||
} | ||
|
||
// IPNetToPrefix is a helper that converts a [*net.IPNet] into a [netip.Prefix]. | ||
// If subnet is nil, it returns netip.Prefix{}. fam must be either | ||
// [AddrFamilyIPv4] or [AddrFamilyIPv6]. | ||
// | ||
// See also [IPNetToPrefixNoMapped]. | ||
func IPNetToPrefix(subnet *net.IPNet, fam AddrFamily) (p netip.Prefix, err error) { | ||
if subnet == nil { | ||
return netip.Prefix{}, nil | ||
} | ||
|
||
addr, err := IPToAddr(subnet.IP, fam) | ||
if err != nil { | ||
return netip.Prefix{}, fmt.Errorf("bad ip for subnet %v: %w", subnet, err) | ||
} | ||
|
||
ones, _ := subnet.Mask.Size() | ||
p = netip.PrefixFrom(addr, ones) | ||
if !p.IsValid() { | ||
return netip.Prefix{}, fmt.Errorf("bad subnet %v", subnet) | ||
} | ||
|
||
return p, nil | ||
} | ||
|
||
// 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. | ||
func IPNetToPrefixNoMapped(subnet *net.IPNet) (p netip.Prefix, err error) { | ||
if subnet == nil { | ||
return netip.Prefix{}, nil | ||
} | ||
|
||
if ip4 := subnet.IP.To4(); ip4 != nil { | ||
subnet.IP = ip4 | ||
|
||
return IPNetToPrefix(subnet, AddrFamilyIPv4) | ||
} | ||
|
||
return IPNetToPrefix(subnet, AddrFamilyIPv6) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package netutil_test | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
|
||
"github.com/AdguardTeam/golibs/netutil" | ||
) | ||
|
||
func ExampleIPv4Localhost() { | ||
fmt.Println(netutil.IPv4Localhost()) | ||
|
||
// Output: | ||
// 127.0.0.1 | ||
} | ||
|
||
func ExampleIPv6Localhost() { | ||
fmt.Println(netutil.IPv6Localhost()) | ||
|
||
// Output: | ||
// ::1 | ||
} | ||
|
||
func ExampleZeroPrefix() { | ||
fmt.Println(netutil.ZeroPrefix(netutil.AddrFamilyIPv4)) | ||
fmt.Println(netutil.ZeroPrefix(netutil.AddrFamilyIPv6)) | ||
|
||
// Output: | ||
// 0.0.0.0/0 | ||
// ::/0 | ||
} | ||
|
||
func ExampleIPToAddr() { | ||
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 ExampleIPToAddrNoMapped() { | ||
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 ExampleIPNetToPrefix() { | ||
_, 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 ExampleIPNetToPrefixNoMapped() { | ||
_, 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> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package netutil | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// AddrFamily is the type for IANA address family numbers. | ||
type AddrFamily uint16 | ||
|
||
// An incomplete list of IANA address family numbers. | ||
// | ||
// See https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml. | ||
const ( | ||
AddrFamilyNone AddrFamily = 0 | ||
AddrFamilyIPv4 AddrFamily = 1 | ||
AddrFamilyIPv6 AddrFamily = 2 | ||
) | ||
|
||
// type check | ||
var _ fmt.Stringer = AddrFamilyNone | ||
|
||
// String implements the [fmt.Stringer] interface for AddrFamily. | ||
func (f AddrFamily) String() (s string) { | ||
switch f { | ||
case AddrFamilyNone: | ||
return "none" | ||
case AddrFamilyIPv4: | ||
return "ipv4" | ||
case AddrFamilyIPv6: | ||
return "ipv6" | ||
default: | ||
return fmt.Sprintf("!bad_addr_fam_%d", f) | ||
} | ||
} | ||
|
||
// Constants to avoid a dependency on github.com/miekg/dns. | ||
const ( | ||
dnsTypeA uint16 = 1 | ||
dnsTypeAAAA uint16 = 28 | ||
) | ||
|
||
// 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. | ||
func AddrFamilyFromRRType(rr uint16) (fam AddrFamily) { | ||
switch rr { | ||
case dnsTypeA: | ||
return AddrFamilyIPv4 | ||
case dnsTypeAAAA: | ||
return AddrFamilyIPv6 | ||
default: | ||
return AddrFamilyNone | ||
} | ||
} |
Oops, something went wrong.