chore: import improved bogons handling code (#723)
This diff imports improved bogons handling code from websteps
winter 2022 edition's repository.
See https://github.com/ooni/probe/issues/2095
See a65f3e8579/internal/netxlite/bogon.go
This commit is contained in:
parent
1776ea1288
commit
0efd4ff130
|
@ -14,40 +14,113 @@ import (
|
||||||
"github.com/ooni/probe-cli/v3/internal/runtimex"
|
"github.com/ooni/probe-cli/v3/internal/runtimex"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsBogon returns whether if an IP address is bogon. Passing to this
|
// IsBogon returns whether an IP address is bogon. Passing to this
|
||||||
// function a non-IP address causes it to return true.
|
// function a non-IP address causes it to return true.
|
||||||
func IsBogon(address string) bool {
|
func IsBogon(address string) bool {
|
||||||
ip := net.ParseIP(address)
|
ip := net.ParseIP(address)
|
||||||
return ip == nil || isPrivate(ip)
|
return ip == nil || isPrivate(address, ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
var privateIPBlocks []*net.IPNet
|
// IsLoopback returns whether an IP address is loopback. Passing to this
|
||||||
|
// function a non-IP address causes it to return true.
|
||||||
|
func IsLoopback(address string) bool {
|
||||||
|
ip := net.ParseIP(address)
|
||||||
|
return ip == nil || ip.IsLoopback()
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
var (
|
||||||
for _, cidr := range []string{
|
bogons4 []*net.IPNet
|
||||||
"0.0.0.0/8", // "This" network (however, Linux...)
|
bogons6 []*net.IPNet
|
||||||
"10.0.0.0/8", // RFC1918
|
)
|
||||||
"100.64.0.0/10", // Carrier grade NAT
|
|
||||||
"127.0.0.0/8", // IPv4 loopback
|
func expandbogons(cidrs []string) (out []*net.IPNet) {
|
||||||
"169.254.0.0/16", // RFC3927 link-local
|
for _, cidr := range cidrs {
|
||||||
"172.16.0.0/12", // RFC1918
|
|
||||||
"192.168.0.0/16", // RFC1918
|
|
||||||
"224.0.0.0/4", // Multicast
|
|
||||||
"::1/128", // IPv6 loopback
|
|
||||||
"fe80::/10", // IPv6 link-local
|
|
||||||
"fc00::/7", // IPv6 unique local addr
|
|
||||||
} {
|
|
||||||
_, block, err := net.ParseCIDR(cidr)
|
_, block, err := net.ParseCIDR(cidr)
|
||||||
runtimex.PanicOnError(err, "net.ParseCIDR failed")
|
runtimex.PanicOnError(err, "net.ParseCIDR failed")
|
||||||
privateIPBlocks = append(privateIPBlocks, block)
|
out = append(out, block)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isPrivate(ip net.IP) bool {
|
func init() {
|
||||||
|
bogons4 = append(bogons4, expandbogons([]string{
|
||||||
|
//
|
||||||
|
// List extracted from https://ipinfo.io/bogon
|
||||||
|
//
|
||||||
|
"0.0.0.0/8", // "This" network
|
||||||
|
"10.0.0.0/8", // Private-use networks
|
||||||
|
"100.64.0.0/10", // Carrier-grade NAT
|
||||||
|
"127.0.0.0/8", // Loopback
|
||||||
|
"127.0.53.53/32", // Name collision occurrence
|
||||||
|
"169.254.0.0/16", // Link local
|
||||||
|
"172.16.0.0/12", // Private-use networks
|
||||||
|
"192.0.0.0/24", // IETF protocol assignments
|
||||||
|
"192.0.2.0/24", // TEST-NET-1
|
||||||
|
"192.168.0.0/16", // Private-use networks
|
||||||
|
"198.18.0.0/15", // Network interconnect device benchmark testing
|
||||||
|
"198.51.100.0/24", // TEST-NET-2
|
||||||
|
"203.0.113.0/24", // TEST-NET-3
|
||||||
|
"224.0.0.0/4", // Multicast
|
||||||
|
"240.0.0.0/4", // Reserved for future use
|
||||||
|
"255.255.255.255/32", // Limited broadcast
|
||||||
|
})...)
|
||||||
|
bogons6 = append(bogons6, expandbogons([]string{
|
||||||
|
//
|
||||||
|
// List extracted from https://ipinfo.io/bogon
|
||||||
|
//
|
||||||
|
"::/128", // Node-scope unicast unspecified address
|
||||||
|
"::1/128", // Node-scope unicast loopback address
|
||||||
|
"::ffff:0:0/96", // IPv4-mapped addresses
|
||||||
|
"::/96", // IPv4-compatible addresses
|
||||||
|
"100::/64", // Remotely triggered black hole addresses
|
||||||
|
"2001:10::/28", // Overlay routable cryptographic hash identifiers (ORCHID)
|
||||||
|
"2001:db8::/32", // Documentation prefix
|
||||||
|
"fc00::/7", // Unique local addresses (ULA)
|
||||||
|
"fe80::/10", // Link-local unicast
|
||||||
|
"fec0::/10", // Site-local unicast (deprecated)
|
||||||
|
"ff00::/8", // Multicast (Note: ff0e:/16 is global scope and may appear on the global internet.)
|
||||||
|
"2002::/24", // 6to4 bogon (0.0.0.0/8)
|
||||||
|
"2002:a00::/24", // 6to4 bogon (10.0.0.0/8)
|
||||||
|
"2002:7f00::/24", // 6to4 bogon (127.0.0.0/8)
|
||||||
|
"2002:a9fe::/32", // 6to4 bogon (169.254.0.0/16)
|
||||||
|
"2002:ac10::/28", // 6to4 bogon (172.16.0.0/12)
|
||||||
|
"2002:c000::/40", // 6to4 bogon (192.0.0.0/24)
|
||||||
|
"2002:c000:200::/40", // 6to4 bogon (192.0.2.0/24)
|
||||||
|
"2002:c0a8::/32", // 6to4 bogon (192.168.0.0/16)
|
||||||
|
"2002:c612::/31", // 6to4 bogon (198.18.0.0/15)
|
||||||
|
"2002:c633:6400::/40", // 6to4 bogon (198.51.100.0/24)
|
||||||
|
"2002:cb00:7100::/40", // 6to4 bogon (203.0.113.0/24)
|
||||||
|
"2002:e000::/20", // 6to4 bogon (224.0.0.0/4)
|
||||||
|
"2002:f000::/20", // 6to4 bogon (240.0.0.0/4)
|
||||||
|
"2002:ffff:ffff::/48", // 6to4 bogon (255.255.255.255/32)
|
||||||
|
"2001::/40", // Teredo bogon (0.0.0.0/8)
|
||||||
|
"2001:0:a00::/40", // Teredo bogon (10.0.0.0/8)
|
||||||
|
"2001:0:7f00::/40", // Teredo bogon (127.0.0.0/8)
|
||||||
|
"2001:0:a9fe::/48", // Teredo bogon (169.254.0.0/16)
|
||||||
|
"2001:0:ac10::/44", // Teredo bogon (172.16.0.0/12)
|
||||||
|
"2001:0:c000::/56", // Teredo bogon (192.0.0.0/24)
|
||||||
|
"2001:0:c000:200::/56", // Teredo bogon (192.0.2.0/24)
|
||||||
|
"2001:0:c0a8::/48", // Teredo bogon (192.168.0.0/16)
|
||||||
|
"2001:0:c612::/47", // Teredo bogon (198.18.0.0/15)
|
||||||
|
"2001:0:c633:6400::/56", // Teredo bogon (198.51.100.0/24)
|
||||||
|
"2001:0:cb00:7100::/56", // Teredo bogon (203.0.113.0/24)
|
||||||
|
"2001:0:e000::/36", // Teredo bogon (224.0.0.0/4)
|
||||||
|
"2001:0:f000::/36", // Teredo bogon (240.0.0.0/4)
|
||||||
|
"2001:0:ffff:ffff::/64", // Teredo bogon (255.255.255.255/32)
|
||||||
|
})...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPrivate(address string, ip net.IP) bool {
|
||||||
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for _, block := range privateIPBlocks {
|
var bogons []*net.IPNet
|
||||||
|
if isIPv6(address) {
|
||||||
|
bogons = bogons6
|
||||||
|
} else {
|
||||||
|
bogons = bogons4
|
||||||
|
}
|
||||||
|
for _, block := range bogons {
|
||||||
if block.Contains(ip) {
|
if block.Contains(ip) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,40 @@ func TestIsBogon(t *testing.T) {
|
||||||
if IsBogon("1.1.1.1") != false {
|
if IsBogon("1.1.1.1") != false {
|
||||||
t.Fatal("unexpected result")
|
t.Fatal("unexpected result")
|
||||||
}
|
}
|
||||||
|
if IsBogon("8.8.4.4") != false {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
|
if IsBogon("2001:4860:4860::8844") != false {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
if IsBogon("10.0.1.1") != true {
|
if IsBogon("10.0.1.1") != true {
|
||||||
t.Fatal("unexpected result")
|
t.Fatal("unexpected result")
|
||||||
}
|
}
|
||||||
|
if IsBogon("::1") != true {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsLoopback(t *testing.T) {
|
||||||
|
if IsLoopback("antani") != true {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
|
if IsLoopback("127.0.0.1") != true {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
|
if IsLoopback("1.1.1.1") != false {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
|
if IsLoopback("8.8.4.4") != false {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
|
if IsLoopback("2001:4860:4860::8844") != false {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
|
if IsLoopback("10.0.1.1") != false {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
|
if IsLoopback("::1") != true {
|
||||||
|
t.Fatal("unexpected result")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,8 @@ type quicDialerQUICGo struct {
|
||||||
|
|
||||||
var _ model.QUICDialer = &quicDialerQUICGo{}
|
var _ model.QUICDialer = &quicDialerQUICGo{}
|
||||||
|
|
||||||
// errInvalidIP indicates that a string is not a valid IP.
|
// ErrInvalidIP indicates that a string is not a valid IP.
|
||||||
var errInvalidIP = errors.New("netxlite: invalid IP")
|
var ErrInvalidIP = errors.New("netxlite: invalid IP")
|
||||||
|
|
||||||
// DialContext implements QUICDialer.DialContext. This function will
|
// DialContext implements QUICDialer.DialContext. This function will
|
||||||
// apply the following TLS defaults:
|
// apply the following TLS defaults:
|
||||||
|
@ -112,7 +112,7 @@ func (d *quicDialerQUICGo) DialContext(ctx context.Context, network string,
|
||||||
}
|
}
|
||||||
ip := net.ParseIP(onlyhost)
|
ip := net.ParseIP(onlyhost)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return nil, errInvalidIP
|
return nil, ErrInvalidIP
|
||||||
}
|
}
|
||||||
pconn, err := d.QUICListener.Listen(&net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
pconn, err := d.QUICListener.Listen(&net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -93,7 +93,7 @@ func TestQUICDialerQUICGo(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
qconn, err := systemdialer.DialContext(
|
qconn, err := systemdialer.DialContext(
|
||||||
ctx, "udp", "a.b.c.d:0", tlsConfig, &quic.Config{})
|
ctx, "udp", "a.b.c.d:0", tlsConfig, &quic.Config{})
|
||||||
if !errors.Is(err, errInvalidIP) {
|
if !errors.Is(err, ErrInvalidIP) {
|
||||||
t.Fatal("not the error we expected", err)
|
t.Fatal("not the error we expected", err)
|
||||||
}
|
}
|
||||||
if qconn != nil {
|
if qconn != nil {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ooni/probe-cli/v3/internal/model"
|
"github.com/ooni/probe-cli/v3/internal/model"
|
||||||
|
@ -208,6 +209,23 @@ func (r *resolverShortCircuitIPAddr) LookupHost(ctx context.Context, hostname st
|
||||||
return r.Resolver.LookupHost(ctx, hostname)
|
return r.Resolver.LookupHost(ctx, hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsIPv6 returns true if the given candidate is a valid IP address
|
||||||
|
// representation and such representation is IPv6.
|
||||||
|
func IsIPv6(candidate string) (bool, error) {
|
||||||
|
if net.ParseIP(candidate) == nil {
|
||||||
|
return false, ErrInvalidIP
|
||||||
|
}
|
||||||
|
return isIPv6(candidate), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isIPv6 returns true if the given IP address is IPv6.
|
||||||
|
func isIPv6(candidate string) bool {
|
||||||
|
// This check for identifying IPv6 is discussed
|
||||||
|
// at https://stackoverflow.com/questions/22751035
|
||||||
|
// and seems good-enough for our purposes.
|
||||||
|
return strings.Contains(candidate, ":")
|
||||||
|
}
|
||||||
|
|
||||||
// ErrNoResolver is the type of error returned by "without resolver"
|
// ErrNoResolver is the type of error returned by "without resolver"
|
||||||
// dialer when asked to dial for and endpoint containing a domain name,
|
// dialer when asked to dial for and endpoint containing a domain name,
|
||||||
// since they can only dial for endpoints containing IP addresses.
|
// since they can only dial for endpoints containing IP addresses.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user