/
resolver-env.go
158 lines (134 loc) Β· 4.31 KB
/
resolver-env.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package resolver
import (
"context"
"fmt"
"net"
"strings"
"github.com/miekg/dns"
"github.com/safing/portbase/log"
"github.com/safing/portmaster/service/netenv"
"github.com/safing/portmaster/service/network/netutils"
)
const (
// InternalSpecialUseDomain is the domain scope used for internal services.
InternalSpecialUseDomain = "portmaster.home.arpa."
routerDomain = "router.local." + InternalSpecialUseDomain
captivePortalDomain = "captiveportal.local." + InternalSpecialUseDomain
)
var (
envResolver = &Resolver{
ConfigURL: ServerSourceEnv,
Info: &ResolverInfo{
Type: ServerTypeEnv,
Source: ServerSourceEnv,
IPScope: netutils.SiteLocal,
},
Conn: &envResolverConn{},
}
envResolvers = []*Resolver{envResolver}
internalSpecialUseSOA dns.RR
internalSpecialUseComment dns.RR
)
func prepEnvResolver() (err error) {
netenv.SpecialCaptivePortalDomain = captivePortalDomain
internalSpecialUseSOA, err = dns.NewRR(fmt.Sprintf(
"%s 17 IN SOA localhost. none.localhost. 0 0 0 0 0",
InternalSpecialUseDomain,
))
if err != nil {
return err
}
internalSpecialUseComment, err = dns.NewRR(fmt.Sprintf(
`%s 17 IN TXT "This is a special use TLD of the Portmaster."`,
InternalSpecialUseDomain,
))
return err
}
type envResolverConn struct{}
func (er *envResolverConn) Query(ctx context.Context, q *Query) (*RRCache, error) {
switch uint16(q.QType) {
case dns.TypeA, dns.TypeAAAA: // We respond with all IPv4/6 addresses we can find.
// Check for exact matches.
switch q.FQDN {
case captivePortalDomain:
// Get IP address of the captive portal.
portal := netenv.GetCaptivePortal()
portalIP := portal.IP
if portalIP == nil {
portalIP = netenv.PortalTestIP
}
// Convert IP to record and respond.
records, err := netutils.IPsToRRs(q.FQDN, []net.IP{portalIP})
if err != nil {
log.Warningf("nameserver: failed to create captive portal response to %s: %s", q.FQDN, err)
return er.nxDomain(q), nil
}
return er.makeRRCache(q, records), nil
case routerDomain:
// Get gateways from netenv system.
routers := netenv.Gateways()
if len(routers) == 0 {
return er.nxDomain(q), nil
}
// Convert IP to record and respond.
records, err := netutils.IPsToRRs(q.FQDN, routers)
if err != nil {
log.Warningf("nameserver: failed to create gateway response to %s: %s", q.FQDN, err)
return er.nxDomain(q), nil
}
return er.makeRRCache(q, records), nil
}
// Check for suffix matches.
if strings.HasSuffix(q.FQDN, CompatDNSCheckInternalDomainScope) {
subdomain := strings.TrimSuffix(q.FQDN, CompatDNSCheckInternalDomainScope)
respondWith := CompatSubmitDNSCheckDomain(subdomain)
// We'll get an A record. Only respond if it's an A question.
if respondWith != nil && uint16(q.QType) == dns.TypeA {
records, err := netutils.IPsToRRs(q.FQDN, []net.IP{respondWith})
if err != nil {
log.Warningf("nameserver: failed to create dns check response to %s: %s", q.FQDN, err)
return er.nxDomain(q), nil
}
return er.makeRRCache(q, records), nil
}
}
case dns.TypeSOA:
// Direct query for the SOA record.
if q.FQDN == InternalSpecialUseDomain {
return er.makeRRCache(q, []dns.RR{internalSpecialUseSOA}), nil
}
}
// No match, reply with NXDOMAIN and SOA record
reply := er.nxDomain(q)
reply.Ns = []dns.RR{internalSpecialUseSOA}
return reply, nil
}
func (er *envResolverConn) nxDomain(q *Query) *RRCache {
return er.makeRRCache(q, nil)
}
func (er *envResolverConn) makeRRCache(q *Query, answers []dns.RR) *RRCache {
// Disable caching, as the env always has the raw data available.
q.NoCaching = true
rrCache := &RRCache{
Domain: q.FQDN,
Question: q.QType,
RCode: dns.RcodeSuccess,
Answer: answers,
Extra: []dns.RR{internalSpecialUseComment}, // Always add comment about this TLD.
Resolver: envResolver.Info.Copy(),
}
if len(rrCache.Answer) == 0 {
rrCache.RCode = dns.RcodeNameError
}
return rrCache
}
func (er *envResolverConn) ReportFailure() {}
func (er *envResolverConn) IsFailing() bool {
return false
}
func (er *envResolverConn) ResetFailure() {}
func (er *envResolverConn) ForceReconnect(_ context.Context) {}
// QueryPortmasterEnv queries the environment resolver directly.
func QueryPortmasterEnv(ctx context.Context, q *Query) (*RRCache, error) {
return envResolver.Conn.Query(ctx, q)
}