allow ptr queries for cloaked domains (#1958)

* allow ptr queries for cloaked domains

* multi ips per PTR returned + cleanup

* some string tidy up

* enable config file switch

* add cloaked ptr test

* enable cloak ptrs in test scenario

* fix reverse ipv6 ptr lookup

* added ipv6 cloaked ptr test
This commit is contained in:
Ian Bashford 2021-12-13 13:00:13 +00:00 committed by GitHub
parent 27e93a53cf
commit 1b6caba307
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 69 additions and 3 deletions

View File

@ -70,6 +70,8 @@ t || dig -p${DNS_PORT} +short cloaked.com @127.0.0.1 | grep -Eq '1.1.1.1|1.0.0.1
t || dig -p${DNS_PORT} +short www.cloaked2.com @127.0.0.1 | grep -Eq '1.1.1.1|1.0.0.1' || fail
t || dig -p${DNS_PORT} +short www.dnscrypt-test @127.0.0.1 | grep -Fq '192.168.100.100' || fail
t || dig -p${DNS_PORT} a.www.dnscrypt-test @127.0.0.1 | grep -Fq 'NXDOMAIN' || fail
t || dig -p${DNS_PORT} +short ptr 101.100.168.192.in-addr.arpa. @127.0.0.1 | grep -Eq 'www.dnscrypt-test.com' || fail
t || dig -p${DNS_PORT} +short ptr 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.d.f.ip6.arpa. @127.0.0.1 | grep -Eq 'ipv6.dnscrypt-test.com' || fail
section
t || dig -p${DNS_PORT} telemetry.example @127.0.0.1 | grep -Fq 'locally blocked' || fail

View File

@ -1,3 +1,5 @@
cloaked.* one.one.one.one
*.cloaked2.* one.one.one.one # inline comment
=www.dnscrypt-test 192.168.100.100
=www.dnscrypt-test.com 192.168.100.101
=ipv6.dnscrypt-test.com fd02::1

View File

@ -10,6 +10,7 @@ block_unqualified = true
block_undelegated = true
forwarding_rules = 'forwarding-rules.txt'
cloaking_rules = 'cloaking-rules.txt'
cloak_ptr = true
cache = true
[local_doh]

View File

@ -47,6 +47,11 @@ const (
InheritedDescriptorsBase = uintptr(50)
)
const (
IPv4Arpa = "in-addr.arpa"
IPv6Arpa = "ip6.arpa"
)
func PrefixWithSize(packet []byte) ([]byte, error) {
packetLen := len(packet)
if packetLen > 0xffff {

View File

@ -98,6 +98,7 @@ type Config struct {
RefusedCodeInResponses bool `toml:"refused_code_in_responses"`
BlockedQueryResponse string `toml:"blocked_query_response"`
QueryMeta []string `toml:"query_meta"`
CloakedPTR bool `toml:"cloak_ptr"`
AnonymizedDNS AnonymizedDNSConfig `toml:"anonymized_dns"`
DoHClientX509Auth DoHClientX509AuthConfig `toml:"doh_client_x509_auth"`
DoHClientX509AuthLegacy DoHClientX509AuthConfig `toml:"tls_client_auth"`
@ -154,6 +155,7 @@ func newConfig() Config {
AnonymizedDNS: AnonymizedDNSConfig{
DirectCertFallback: true,
},
CloakedPTR: false,
}
}
@ -484,6 +486,7 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
proxy.cacheMaxTTL = config.CacheMaxTTL
proxy.rejectTTL = config.RejectTTL
proxy.cloakTTL = config.CloakTTL
proxy.cloakedPTR = config.CloakedPTR
proxy.queryMeta = config.QueryMeta

View File

@ -35,3 +35,10 @@ localhost ::1
# ads.* 192.168.100.1
# ads.* 192.168.100.2
# ads.* ::1
# PTR records can be created by setting cloak_ptr in the main configuration file
# Entries with wild cards will not have PTR records created, but multiple
# names for the same IP are supported
# example.com 192.168.100.1
# my.example.com 192.168.100.1

View File

@ -352,6 +352,8 @@ reject_ttl = 10
## Cloaking returns a predefined address for a specific name.
## In addition to acting as a HOSTS file, it can also return the IP address
## of a different name. It will also do CNAME flattening.
## If 'cloak_ptr' is set, then PTR (reverse lookups) are enabled
## for cloaking rules that do not contain wild cards.
##
## See the `example-cloaking-rules.txt` file for an example
@ -360,6 +362,7 @@ reject_ttl = 10
## TTL used when serving entries in cloaking-rules.txt
# cloak_ttl = 600
# cloak_ptr = false

View File

@ -19,12 +19,14 @@ type CloakedName struct {
lastUpdate *time.Time
lineNo int
isIP bool
PTR []string
}
type PluginCloak struct {
sync.RWMutex
patternMatcher *PatternMatcher
ttl uint32
createPTR bool
}
func (plugin *PluginCloak) Name() string {
@ -42,6 +44,7 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
return err
}
plugin.ttl = proxy.cloakTTL
plugin.createPTR = proxy.cloakedPTR
plugin.patternMatcher = NewPatternMatcher()
cloakedNames := make(map[string]*CloakedName)
for lineNo, line := range strings.Split(string(bin), "\n") {
@ -67,7 +70,8 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
if !found {
cloakedName = &CloakedName{}
}
if ip := net.ParseIP(target); ip != nil {
ip := net.ParseIP(target)
if ip != nil {
if ipv4 := ip.To4(); ipv4 != nil {
cloakedName.ipv4 = append((*cloakedName).ipv4, ipv4)
} else if ipv6 := ip.To16(); ipv6 != nil {
@ -82,6 +86,28 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
}
cloakedName.lineNo = lineNo + 1
cloakedNames[line] = cloakedName
if !plugin.createPTR || strings.Contains(line, "*") || !cloakedName.isIP == true {
continue
}
var ptrLine string
if ipv4 := ip.To4(); ipv4 != nil {
reversed, _ := dns.ReverseAddr(ip.To4().String())
ptrLine = strings.TrimSuffix(reversed, ".")
} else {
reversed, _ := dns.ReverseAddr(cloakedName.ipv6[0].To16().String())
ptrLine = strings.TrimSuffix(reversed, ".")
}
ptrQueryLine := ptrEntryToQuery(ptrLine)
ptrCloakedName, found := cloakedNames[ptrQueryLine]
if !found {
ptrCloakedName = &CloakedName{}
}
ptrCloakedName.isIP = true
ptrCloakedName.PTR = append((*ptrCloakedName).PTR, ptrNameToFQDN(line))
ptrCloakedName.lineNo = lineNo + 1
cloakedNames[ptrQueryLine] = ptrCloakedName
}
for line, cloakedName := range cloakedNames {
if err := plugin.patternMatcher.Add(line, cloakedName, cloakedName.lineNo); err != nil {
@ -91,6 +117,15 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
return nil
}
func ptrEntryToQuery(ptrEntry string) string {
return "=" + ptrEntry
}
func ptrNameToFQDN(ptrLine string) string {
ptrLine = strings.TrimPrefix(ptrLine, "=")
return ptrLine + "."
}
func (plugin *PluginCloak) Drop() error {
return nil
}
@ -101,7 +136,7 @@ func (plugin *PluginCloak) Reload() error {
func (plugin *PluginCloak) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
question := msg.Question[0]
if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA) {
if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA && question.Qtype != dns.TypePTR) {
return nil
}
now := time.Now()
@ -157,13 +192,20 @@ func (plugin *PluginCloak) Eval(pluginsState *PluginsState, msg *dns.Msg) error
rr.A = ip
synth.Answer = append(synth.Answer, rr)
}
} else {
} else if question.Qtype == dns.TypeAAAA {
for _, ip := range cloakedName.ipv6 {
rr := new(dns.AAAA)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}
rr.AAAA = ip
synth.Answer = append(synth.Answer, rr)
}
} else if question.Qtype == dns.TypePTR {
for _, ptr := range cloakedName.PTR {
rr := new(dns.PTR)
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: ttl}
rr.Ptr = ptr
synth.Answer = append(synth.Answer, rr)
}
}
rand.Shuffle(len(synth.Answer), func(i, j int) { synth.Answer[i], synth.Answer[j] = synth.Answer[j], synth.Answer[i] })
pluginsState.synthResponse = synth

View File

@ -83,6 +83,7 @@ type Proxy struct {
cacheMinTTL uint32
cacheNegMaxTTL uint32
cloakTTL uint32
cloakedPTR bool
cache bool
pluginBlockIPv6 bool
ephemeralKeys bool