mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
all: use private resolver for private IP address
These queries could not be resolved by Control D upstreams, so it's useless and less performance to send them to servers.
This commit is contained in:
committed by
Cuong Manh Le
parent
3fea92c8b1
commit
4816a09e3a
@@ -38,6 +38,12 @@ var osUpstreamConfig = &ctrld.UpstreamConfig{
|
|||||||
Timeout: 2000,
|
Timeout: 2000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var privateUpstreamConfig = &ctrld.UpstreamConfig{
|
||||||
|
Name: "Private resolver",
|
||||||
|
Type: ctrld.ResolverTypePrivate,
|
||||||
|
Timeout: 2000,
|
||||||
|
}
|
||||||
|
|
||||||
var errReload = errors.New("reload")
|
var errReload = errors.New("reload")
|
||||||
|
|
||||||
func (p *prog) serveDNS(listenerNum string, reload bool, reloadCh chan struct{}) error {
|
func (p *prog) serveDNS(listenerNum string, reload bool, reloadCh chan struct{}) error {
|
||||||
@@ -54,6 +60,11 @@ func (p *prog) serveDNS(listenerNum string, reload bool, reloadCh chan struct{})
|
|||||||
handler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
|
handler := dns.HandlerFunc(func(w dns.ResponseWriter, m *dns.Msg) {
|
||||||
p.sema.acquire()
|
p.sema.acquire()
|
||||||
defer p.sema.release()
|
defer p.sema.release()
|
||||||
|
if len(m.Question) == 0 {
|
||||||
|
answer := new(dns.Msg)
|
||||||
|
answer.SetRcode(m, dns.RcodeFormatError)
|
||||||
|
return
|
||||||
|
}
|
||||||
go p.detectLoop(m)
|
go p.detectLoop(m)
|
||||||
q := m.Question[0]
|
q := m.Question[0]
|
||||||
domain := canonicalName(q.Name)
|
domain := canonicalName(q.Name)
|
||||||
@@ -261,6 +272,11 @@ func (p *prog) proxy(ctx context.Context, upstreams []string, failoverRcodes []i
|
|||||||
upstreamConfigs = []*ctrld.UpstreamConfig{osUpstreamConfig}
|
upstreamConfigs = []*ctrld.UpstreamConfig{osUpstreamConfig}
|
||||||
upstreams = []string{upstreamOS}
|
upstreams = []string{upstreamOS}
|
||||||
}
|
}
|
||||||
|
if isPrivatePtrLookup(msg) {
|
||||||
|
ctrld.Log(ctx, mainLog.Load().Info(), "private PTR lookup -> [%s]", upstreamOS)
|
||||||
|
upstreamConfigs = []*ctrld.UpstreamConfig{privateUpstreamConfig}
|
||||||
|
upstreams = []string{upstreamOS}
|
||||||
|
}
|
||||||
// Inverse query should not be cached: https://www.rfc-editor.org/rfc/rfc1035#section-7.4
|
// Inverse query should not be cached: https://www.rfc-editor.org/rfc/rfc1035#section-7.4
|
||||||
if p.cache != nil && msg.Question[0].Qtype != dns.TypePTR {
|
if p.cache != nil && msg.Question[0].Qtype != dns.TypePTR {
|
||||||
for _, upstream := range upstreams {
|
for _, upstream := range upstreams {
|
||||||
@@ -634,3 +650,52 @@ func rfc1918Addresses() []string {
|
|||||||
})
|
})
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ipFromARPA parses a FQDN arpa domain and return the IP address if valid.
|
||||||
|
func ipFromARPA(arpa string) net.IP {
|
||||||
|
if arpa, ok := strings.CutSuffix(arpa, ".in-addr.arpa."); ok {
|
||||||
|
if ptrIP := net.ParseIP(arpa); ptrIP != nil {
|
||||||
|
return net.IP{ptrIP[15], ptrIP[14], ptrIP[13], ptrIP[12]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if arpa, ok := strings.CutSuffix(arpa, ".ip6.arpa."); ok {
|
||||||
|
l := net.IPv6len * 2
|
||||||
|
base := 16
|
||||||
|
ip := make(net.IP, net.IPv6len)
|
||||||
|
for i := 0; i < l && arpa != ""; i++ {
|
||||||
|
idx := strings.LastIndexByte(arpa, '.')
|
||||||
|
off := idx + 1
|
||||||
|
if idx == -1 {
|
||||||
|
idx = 0
|
||||||
|
off = 0
|
||||||
|
} else if idx == len(arpa)-1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n, err := strconv.ParseUint(arpa[off:], base, 8)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
b := byte(n)
|
||||||
|
ii := i / 2
|
||||||
|
if i&1 == 1 {
|
||||||
|
b |= ip[ii] << 4
|
||||||
|
}
|
||||||
|
ip[ii] = b
|
||||||
|
arpa = arpa[:idx]
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isPrivatePtrLookup reports whether DNS message is an PTR query for LAN network.
|
||||||
|
func isPrivatePtrLookup(m *dns.Msg) bool {
|
||||||
|
if m == nil || len(m.Question) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
q := m.Question[0]
|
||||||
|
if ip := ipFromARPA(q.Name); ip != nil {
|
||||||
|
return ip.IsPrivate() || ip.IsLoopback() || ip.IsLinkLocalUnicast()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -238,3 +238,30 @@ func Test_remoteAddrFromMsg(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_ipFromARPA(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
IP string
|
||||||
|
ARPA string
|
||||||
|
}{
|
||||||
|
{"1.2.3.4", "4.3.2.1.in-addr.arpa."},
|
||||||
|
{"245.110.36.114", "114.36.110.245.in-addr.arpa."},
|
||||||
|
{"::ffff:12.34.56.78", "78.56.34.12.in-addr.arpa."},
|
||||||
|
{"::1", "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.0.0.0.0.ip6.arpa."},
|
||||||
|
{"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.0.1.0.0.0.ip6.arpa."},
|
||||||
|
{"1234:567::89a:bcde", "e.d.c.b.a.9.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.6.5.0.4.3.2.1.ip6.arpa."},
|
||||||
|
{"1234:567:fefe:bcbc:adad:9e4a:89a:bcde", "e.d.c.b.a.9.8.0.a.4.e.9.d.a.d.a.c.b.c.b.e.f.e.f.7.6.5.0.4.3.2.1.ip6.arpa."},
|
||||||
|
{"", "asd.in-addr.arpa."},
|
||||||
|
{"", "asd.ip6.arpa."},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.IP, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
if got := ipFromARPA(tc.ARPA); !got.Equal(net.ParseIP(tc.IP)) {
|
||||||
|
t.Errorf("unexpected ip, want: %s, got: %s", tc.IP, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ const (
|
|||||||
ResolverTypeOS = "os"
|
ResolverTypeOS = "os"
|
||||||
// ResolverTypeLegacy specifies legacy resolver.
|
// ResolverTypeLegacy specifies legacy resolver.
|
||||||
ResolverTypeLegacy = "legacy"
|
ResolverTypeLegacy = "legacy"
|
||||||
|
// ResolverTypePrivate is like ResolverTypeOS, but use for local resolver only.
|
||||||
|
ResolverTypePrivate = "private"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapDNS = "76.76.2.0"
|
var bootstrapDNS = "76.76.2.0"
|
||||||
@@ -61,6 +63,8 @@ func NewResolver(uc *UpstreamConfig) (Resolver, error) {
|
|||||||
return or, nil
|
return or, nil
|
||||||
case ResolverTypeLegacy:
|
case ResolverTypeLegacy:
|
||||||
return &legacyResolver{uc: uc}, nil
|
return &legacyResolver{uc: uc}, nil
|
||||||
|
case ResolverTypePrivate:
|
||||||
|
return NewPrivateResolver(), nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("%w: %s", errUnknownResolver, typ)
|
return nil, fmt.Errorf("%w: %s", errUnknownResolver, typ)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user