initial commit
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user