121 lines
2.9 KiB
Go
121 lines
2.9 KiB
Go
// Package netutil holds small address helpers shared by the DNS server, the
|
|
// rate limiter and the HTTP layer.
|
|
package netutil
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"strings"
|
|
)
|
|
|
|
// PrefixSet answers "is this address in one of these networks?".
|
|
//
|
|
// It backs rate-limit exemptions, query-log ignore lists and trusted-proxy
|
|
// configuration. Invalid entries are skipped rather than rejected: these lists
|
|
// are conveniences, and the security-critical recursion ACL uses a stricter
|
|
// constructor that reports errors.
|
|
type PrefixSet struct {
|
|
prefixes []netip.Prefix
|
|
}
|
|
|
|
// NewPrefixSet compiles a list of CIDR blocks or bare addresses.
|
|
func NewPrefixSet(in []string) *PrefixSet {
|
|
s := &PrefixSet{}
|
|
for _, v := range in {
|
|
if p, ok := ParsePrefix(v); ok {
|
|
s.prefixes = append(s.prefixes, p)
|
|
}
|
|
}
|
|
return s
|
|
}
|
|
|
|
// ParsePrefix accepts a CIDR block or a bare address, returning a masked
|
|
// prefix. A bare address becomes a host route.
|
|
func ParsePrefix(v string) (netip.Prefix, bool) {
|
|
v = strings.TrimSpace(v)
|
|
if v == "" {
|
|
return netip.Prefix{}, false
|
|
}
|
|
if strings.Contains(v, "/") {
|
|
p, err := netip.ParsePrefix(v)
|
|
if err != nil {
|
|
return netip.Prefix{}, false
|
|
}
|
|
return p.Masked(), true
|
|
}
|
|
addr, err := netip.ParseAddr(v)
|
|
if err != nil {
|
|
return netip.Prefix{}, false
|
|
}
|
|
a := addr.Unmap()
|
|
return netip.PrefixFrom(a, a.BitLen()), true
|
|
}
|
|
|
|
// Contains reports whether addr falls inside the set.
|
|
func (s *PrefixSet) Contains(addr netip.Addr) bool {
|
|
if s == nil || len(s.prefixes) == 0 {
|
|
return false
|
|
}
|
|
ip := addr.Unmap()
|
|
for _, p := range s.prefixes {
|
|
if p.Addr().Is4() == ip.Is4() && p.Contains(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Empty reports whether the set has no usable entries.
|
|
func (s *PrefixSet) Empty() bool { return s == nil || len(s.prefixes) == 0 }
|
|
|
|
// Len reports how many prefixes the set holds.
|
|
func (s *PrefixSet) Len() int {
|
|
if s == nil {
|
|
return 0
|
|
}
|
|
return len(s.prefixes)
|
|
}
|
|
|
|
// AddrFromNetAddr extracts the IP from a net.Addr, which is how both the DNS
|
|
// listeners and net/http hand us the client address.
|
|
func AddrFromNetAddr(a net.Addr) netip.Addr {
|
|
switch v := a.(type) {
|
|
case *net.UDPAddr:
|
|
if addr, ok := netip.AddrFromSlice(v.IP); ok {
|
|
return addr.Unmap()
|
|
}
|
|
case *net.TCPAddr:
|
|
if addr, ok := netip.AddrFromSlice(v.IP); ok {
|
|
return addr.Unmap()
|
|
}
|
|
}
|
|
if a == nil {
|
|
return netip.Addr{}
|
|
}
|
|
host, _, err := net.SplitHostPort(a.String())
|
|
if err != nil {
|
|
host = a.String()
|
|
}
|
|
addr, err := netip.ParseAddr(host)
|
|
if err != nil {
|
|
return netip.Addr{}
|
|
}
|
|
return addr.Unmap()
|
|
}
|
|
|
|
// AddrFromHostPort parses a "host:port" or bare-host string into an address.
|
|
func AddrFromHostPort(s string) (netip.Addr, bool) {
|
|
s = strings.TrimSpace(s)
|
|
if s == "" {
|
|
return netip.Addr{}, false
|
|
}
|
|
if host, _, err := net.SplitHostPort(s); err == nil {
|
|
s = host
|
|
}
|
|
addr, err := netip.ParseAddr(strings.Trim(s, "[]"))
|
|
if err != nil {
|
|
return netip.Addr{}, false
|
|
}
|
|
return addr.Unmap(), true
|
|
}
|