Skip to content

Commit 1b6caba

Browse files
authored
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
1 parent 27e93a5 commit 1b6caba

9 files changed

Lines changed: 69 additions & 3 deletions

.ci/ci-test.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
7070
t || dig -p${DNS_PORT} +short www.cloaked2.com @127.0.0.1 | grep -Eq '1.1.1.1|1.0.0.1' || fail
7171
t || dig -p${DNS_PORT} +short www.dnscrypt-test @127.0.0.1 | grep -Fq '192.168.100.100' || fail
7272
t || dig -p${DNS_PORT} a.www.dnscrypt-test @127.0.0.1 | grep -Fq 'NXDOMAIN' || fail
73+
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
74+
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
7375

7476
section
7577
t || dig -p${DNS_PORT} telemetry.example @127.0.0.1 | grep -Fq 'locally blocked' || fail

.ci/cloaking-rules.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
cloaked.* one.one.one.one
22
*.cloaked2.* one.one.one.one # inline comment
33
=www.dnscrypt-test 192.168.100.100
4+
=www.dnscrypt-test.com 192.168.100.101
5+
=ipv6.dnscrypt-test.com fd02::1

.ci/test2-dnscrypt-proxy.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ block_unqualified = true
1010
block_undelegated = true
1111
forwarding_rules = 'forwarding-rules.txt'
1212
cloaking_rules = 'cloaking-rules.txt'
13+
cloak_ptr = true
1314
cache = true
1415

1516
[local_doh]

dnscrypt-proxy/common.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ const (
4747
InheritedDescriptorsBase = uintptr(50)
4848
)
4949

50+
const (
51+
IPv4Arpa = "in-addr.arpa"
52+
IPv6Arpa = "ip6.arpa"
53+
)
54+
5055
func PrefixWithSize(packet []byte) ([]byte, error) {
5156
packetLen := len(packet)
5257
if packetLen > 0xffff {

dnscrypt-proxy/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ type Config struct {
9898
RefusedCodeInResponses bool `toml:"refused_code_in_responses"`
9999
BlockedQueryResponse string `toml:"blocked_query_response"`
100100
QueryMeta []string `toml:"query_meta"`
101+
CloakedPTR bool `toml:"cloak_ptr"`
101102
AnonymizedDNS AnonymizedDNSConfig `toml:"anonymized_dns"`
102103
DoHClientX509Auth DoHClientX509AuthConfig `toml:"doh_client_x509_auth"`
103104
DoHClientX509AuthLegacy DoHClientX509AuthConfig `toml:"tls_client_auth"`
@@ -154,6 +155,7 @@ func newConfig() Config {
154155
AnonymizedDNS: AnonymizedDNSConfig{
155156
DirectCertFallback: true,
156157
},
158+
CloakedPTR: false,
157159
}
158160
}
159161

@@ -484,6 +486,7 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
484486
proxy.cacheMaxTTL = config.CacheMaxTTL
485487
proxy.rejectTTL = config.RejectTTL
486488
proxy.cloakTTL = config.CloakTTL
489+
proxy.cloakedPTR = config.CloakedPTR
487490

488491
proxy.queryMeta = config.QueryMeta
489492

dnscrypt-proxy/example-cloaking-rules.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,10 @@ localhost ::1
3535
# ads.* 192.168.100.1
3636
# ads.* 192.168.100.2
3737
# ads.* ::1
38+
39+
# PTR records can be created by setting cloak_ptr in the main configuration file
40+
# Entries with wild cards will not have PTR records created, but multiple
41+
# names for the same IP are supported
42+
43+
# example.com 192.168.100.1
44+
# my.example.com 192.168.100.1

dnscrypt-proxy/example-dnscrypt-proxy.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,8 @@ reject_ttl = 10
352352
## Cloaking returns a predefined address for a specific name.
353353
## In addition to acting as a HOSTS file, it can also return the IP address
354354
## of a different name. It will also do CNAME flattening.
355+
## If 'cloak_ptr' is set, then PTR (reverse lookups) are enabled
356+
## for cloaking rules that do not contain wild cards.
355357
##
356358
## See the `example-cloaking-rules.txt` file for an example
357359

@@ -360,6 +362,7 @@ reject_ttl = 10
360362
## TTL used when serving entries in cloaking-rules.txt
361363

362364
# cloak_ttl = 600
365+
# cloak_ptr = false
363366

364367

365368

dnscrypt-proxy/plugin_cloak.go

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ type CloakedName struct {
1919
lastUpdate *time.Time
2020
lineNo int
2121
isIP bool
22+
PTR []string
2223
}
2324

2425
type PluginCloak struct {
2526
sync.RWMutex
2627
patternMatcher *PatternMatcher
2728
ttl uint32
29+
createPTR bool
2830
}
2931

3032
func (plugin *PluginCloak) Name() string {
@@ -42,6 +44,7 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
4244
return err
4345
}
4446
plugin.ttl = proxy.cloakTTL
47+
plugin.createPTR = proxy.cloakedPTR
4548
plugin.patternMatcher = NewPatternMatcher()
4649
cloakedNames := make(map[string]*CloakedName)
4750
for lineNo, line := range strings.Split(string(bin), "\n") {
@@ -67,7 +70,8 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
6770
if !found {
6871
cloakedName = &CloakedName{}
6972
}
70-
if ip := net.ParseIP(target); ip != nil {
73+
ip := net.ParseIP(target)
74+
if ip != nil {
7175
if ipv4 := ip.To4(); ipv4 != nil {
7276
cloakedName.ipv4 = append((*cloakedName).ipv4, ipv4)
7377
} else if ipv6 := ip.To16(); ipv6 != nil {
@@ -82,6 +86,28 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
8286
}
8387
cloakedName.lineNo = lineNo + 1
8488
cloakedNames[line] = cloakedName
89+
90+
if !plugin.createPTR || strings.Contains(line, "*") || !cloakedName.isIP == true {
91+
continue
92+
}
93+
94+
var ptrLine string
95+
if ipv4 := ip.To4(); ipv4 != nil {
96+
reversed, _ := dns.ReverseAddr(ip.To4().String())
97+
ptrLine = strings.TrimSuffix(reversed, ".")
98+
} else {
99+
reversed, _ := dns.ReverseAddr(cloakedName.ipv6[0].To16().String())
100+
ptrLine = strings.TrimSuffix(reversed, ".")
101+
}
102+
ptrQueryLine := ptrEntryToQuery(ptrLine)
103+
ptrCloakedName, found := cloakedNames[ptrQueryLine]
104+
if !found {
105+
ptrCloakedName = &CloakedName{}
106+
}
107+
ptrCloakedName.isIP = true
108+
ptrCloakedName.PTR = append((*ptrCloakedName).PTR, ptrNameToFQDN(line))
109+
ptrCloakedName.lineNo = lineNo + 1
110+
cloakedNames[ptrQueryLine] = ptrCloakedName
85111
}
86112
for line, cloakedName := range cloakedNames {
87113
if err := plugin.patternMatcher.Add(line, cloakedName, cloakedName.lineNo); err != nil {
@@ -91,6 +117,15 @@ func (plugin *PluginCloak) Init(proxy *Proxy) error {
91117
return nil
92118
}
93119

120+
func ptrEntryToQuery(ptrEntry string) string {
121+
return "=" + ptrEntry
122+
}
123+
124+
func ptrNameToFQDN(ptrLine string) string {
125+
ptrLine = strings.TrimPrefix(ptrLine, "=")
126+
return ptrLine + "."
127+
}
128+
94129
func (plugin *PluginCloak) Drop() error {
95130
return nil
96131
}
@@ -101,7 +136,7 @@ func (plugin *PluginCloak) Reload() error {
101136

102137
func (plugin *PluginCloak) Eval(pluginsState *PluginsState, msg *dns.Msg) error {
103138
question := msg.Question[0]
104-
if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA) {
139+
if question.Qclass != dns.ClassINET || (question.Qtype != dns.TypeA && question.Qtype != dns.TypeAAAA && question.Qtype != dns.TypePTR) {
105140
return nil
106141
}
107142
now := time.Now()
@@ -157,13 +192,20 @@ func (plugin *PluginCloak) Eval(pluginsState *PluginsState, msg *dns.Msg) error
157192
rr.A = ip
158193
synth.Answer = append(synth.Answer, rr)
159194
}
160-
} else {
195+
} else if question.Qtype == dns.TypeAAAA {
161196
for _, ip := range cloakedName.ipv6 {
162197
rr := new(dns.AAAA)
163198
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}
164199
rr.AAAA = ip
165200
synth.Answer = append(synth.Answer, rr)
166201
}
202+
} else if question.Qtype == dns.TypePTR {
203+
for _, ptr := range cloakedName.PTR {
204+
rr := new(dns.PTR)
205+
rr.Hdr = dns.RR_Header{Name: question.Name, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: ttl}
206+
rr.Ptr = ptr
207+
synth.Answer = append(synth.Answer, rr)
208+
}
167209
}
168210
rand.Shuffle(len(synth.Answer), func(i, j int) { synth.Answer[i], synth.Answer[j] = synth.Answer[j], synth.Answer[i] })
169211
pluginsState.synthResponse = synth

dnscrypt-proxy/proxy.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ type Proxy struct {
8383
cacheMinTTL uint32
8484
cacheNegMaxTTL uint32
8585
cloakTTL uint32
86+
cloakedPTR bool
8687
cache bool
8788
pluginBlockIPv6 bool
8889
ephemeralKeys bool

0 commit comments

Comments
 (0)