96 lines
2.4 KiB
Go
96 lines
2.4 KiB
Go
// DNS Rebinding protection
|
|
|
|
package dnsforward
|
|
|
|
import (
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/AdguardTeam/golibs/log"
|
|
)
|
|
|
|
type dnsRebindChecker struct {
|
|
}
|
|
|
|
// IsPrivate reports whether ip is a private address, according to
|
|
// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses).
|
|
func (*dnsRebindChecker) isPrivate(ip net.IP) bool {
|
|
//TODO: remove once https://github.com/golang/go/pull/42793 makes it to stdlib
|
|
if ip4 := ip.To4(); ip4 != nil {
|
|
return ip4[0] == 10 ||
|
|
(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
|
|
(ip4[0] == 192 && ip4[1] == 168)
|
|
}
|
|
return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
|
|
}
|
|
|
|
func (c *dnsRebindChecker) isRebindHost(host string) bool {
|
|
if ip := net.ParseIP(host); ip != nil {
|
|
return c.isRebindIP(ip)
|
|
}
|
|
|
|
return host == "localhost"
|
|
}
|
|
|
|
func (c *dnsRebindChecker) isRebindIP(ip net.IP) bool {
|
|
// This is compatible with dnsmasq definition
|
|
// See: https://github.com/imp/dnsmasq/blob/4e7694d7107d2299f4aaededf8917fceb5dfb924/src/rfc1035.c#L412
|
|
|
|
rebind := false
|
|
if ip4 := ip.To4(); ip4 != nil {
|
|
|
|
/* 0.0.0.0/8 (RFC 5735 section 3. "here" network) */
|
|
rebind = ip4[0] == 0 ||
|
|
|
|
/* 10.0.0.0/8 (private) */
|
|
ip4[0] == 10 ||
|
|
|
|
/* 172.16.0.0/12 (private) */
|
|
(ip4[0] == 172 && ip4[1]&0x10 == 0x10) ||
|
|
|
|
/* 169.254.0.0/16 (zeroconf) */
|
|
(ip4[0] == 169 && ip4[1] == 254) ||
|
|
|
|
/* 192.0.2.0/24 (test-net) */
|
|
(ip4[0] == 192 && ip4[1] == 0 && ip4[2] == 2) ||
|
|
|
|
/* 198.51.100.0/24(test-net) */
|
|
(ip4[0] == 198 && ip4[1] == 51 && ip4[2] == 100) ||
|
|
|
|
/* 203.0.113.0/24 (test-net) */
|
|
(ip4[0] == 203 && ip4[1] == 0 && ip4[2] == 113) ||
|
|
|
|
/* 255.255.255.255/32 (broadcast)*/
|
|
ip4.Equal(net.IPv4bcast)
|
|
} else {
|
|
rebind = ip.Equal(net.IPv6zero) || ip.Equal(net.IPv6unspecified) ||
|
|
ip.Equal(net.IPv6interfacelocalallnodes) ||
|
|
ip.Equal(net.IPv6linklocalallnodes) ||
|
|
ip.Equal(net.IPv6linklocalallrouters)
|
|
}
|
|
|
|
return rebind || c.isPrivate(ip) || ip.IsLoopback()
|
|
}
|
|
|
|
// Checks DNS rebinding attacks
|
|
// Note both whitelisted and cached hosts will bypass rebinding check (see: processFilteringAfterResponse()).
|
|
func (s *Server) isResponseRebind(domain, host string) bool {
|
|
if !s.conf.RebindingEnabled {
|
|
return false
|
|
}
|
|
|
|
if log.GetLevel() >= log.DEBUG {
|
|
timer := log.StartTimer()
|
|
defer timer.LogElapsed("DNS Rebinding check for %s -> %s", domain, host)
|
|
}
|
|
|
|
for _, h := range s.conf.RebindingAllowedHosts {
|
|
if strings.HasSuffix(domain, h) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
c := dnsRebindChecker{}
|
|
return c.isRebindHost(host)
|
|
}
|