mirror of
https://github.com/foxcpp/maddy.git
synced 2025-04-07 06:57:37 +03:00
dns: Add even more hacks to AuthLoookupIPAddr, add tests
This commit is contained in:
parent
72d92e5d20
commit
50c1caed35
2 changed files with 232 additions and 21 deletions
|
@ -25,6 +25,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/foxcpp/maddy/framework/log"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -210,18 +211,26 @@ func (e ExtResolver) AuthLookupIPAddr(ctx context.Context, host string) (ad bool
|
|||
msg.AuthenticatedData = true
|
||||
|
||||
resp, err := e.exchange(ctx, msg)
|
||||
aaaaFailed := false
|
||||
var (
|
||||
v6ad bool
|
||||
v6addrs []net.IPAddr
|
||||
)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
v6addrs := make([]net.IPAddr, 0, len(resp.Answer))
|
||||
v6ad := resp.AuthenticatedData
|
||||
for _, rr := range resp.Answer {
|
||||
aaaaRR, ok := rr.(*dns.AAAA)
|
||||
if !ok {
|
||||
continue
|
||||
// Disregard the error for AAAA lookups.
|
||||
resp = &dns.Msg{}
|
||||
aaaaFailed = true
|
||||
log.DefaultLogger.Error("Network I/O error during AAAA lookup", err, "host", host)
|
||||
} else {
|
||||
v6addrs = make([]net.IPAddr, 0, len(resp.Answer))
|
||||
v6ad = resp.AuthenticatedData
|
||||
for _, rr := range resp.Answer {
|
||||
aaaaRR, ok := rr.(*dns.AAAA)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
v6addrs = append(v6addrs, net.IPAddr{IP: aaaaRR.AAAA})
|
||||
}
|
||||
v6addrs = append(v6addrs, net.IPAddr{IP: aaaaRR.AAAA})
|
||||
}
|
||||
|
||||
// Then repeat query with IPv4.
|
||||
|
@ -231,18 +240,26 @@ func (e ExtResolver) AuthLookupIPAddr(ctx context.Context, host string) (ad bool
|
|||
msg.AuthenticatedData = true
|
||||
|
||||
resp, err = e.exchange(ctx, msg)
|
||||
var (
|
||||
v4ad bool
|
||||
v4addrs []net.IPAddr
|
||||
)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
v4ad := resp.AuthenticatedData
|
||||
v4addrs := make([]net.IPAddr, 0, len(resp.Answer))
|
||||
for _, rr := range resp.Answer {
|
||||
aRR, ok := rr.(*dns.A)
|
||||
if !ok {
|
||||
continue
|
||||
if aaaaFailed {
|
||||
return false, nil, err
|
||||
}
|
||||
// Disregard A lookup error if AAAA succeeded.
|
||||
log.DefaultLogger.Error("Network I/O error during A lookup, using AAAA records", err, "host", host)
|
||||
} else {
|
||||
v4ad = resp.AuthenticatedData
|
||||
v4addrs = make([]net.IPAddr, 0, len(resp.Answer))
|
||||
for _, rr := range resp.Answer {
|
||||
aRR, ok := rr.(*dns.A)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
v4addrs = append(v4addrs, net.IPAddr{IP: aRR.A})
|
||||
}
|
||||
v4addrs = append(v4addrs, net.IPAddr{IP: aRR.A})
|
||||
}
|
||||
|
||||
// A little bit of careful handling is required if AD is inconsistent
|
||||
|
@ -255,10 +272,10 @@ func (e ExtResolver) AuthLookupIPAddr(ctx context.Context, host string) (ad bool
|
|||
addrs = append(addrs, v6addrs...)
|
||||
addrs = append(addrs, v4addrs...)
|
||||
} else {
|
||||
addrs = append(addrs, v4addrs...)
|
||||
if v6ad {
|
||||
addrs = append(addrs, v6addrs...)
|
||||
}
|
||||
addrs = append(addrs, v4addrs...)
|
||||
}
|
||||
return v4ad, addrs, nil
|
||||
}
|
||||
|
|
194
framework/dns/dnssec_test.go
Normal file
194
framework/dns/dnssec_test.go
Normal file
|
@ -0,0 +1,194 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/foxcpp/maddy/framework/log"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type TestSrvAction int
|
||||
|
||||
const (
|
||||
TestSrvTimeout TestSrvAction = iota
|
||||
TestSrvServfail
|
||||
TestSrvNoAddr
|
||||
TestSrvOk
|
||||
)
|
||||
|
||||
func (a TestSrvAction) String() string {
|
||||
switch a {
|
||||
case TestSrvTimeout:
|
||||
return "SrvTimeout"
|
||||
case TestSrvServfail:
|
||||
return "SrvServfail"
|
||||
case TestSrvNoAddr:
|
||||
return "SrvNoAddr"
|
||||
case TestSrvOk:
|
||||
return "SrvOk"
|
||||
default:
|
||||
panic("wtf action")
|
||||
}
|
||||
}
|
||||
|
||||
type IPAddrTestServer struct {
|
||||
udpServ dns.Server
|
||||
aAction TestSrvAction
|
||||
aAD bool
|
||||
aaaaAction TestSrvAction
|
||||
aaaaAD bool
|
||||
}
|
||||
|
||||
func (s *IPAddrTestServer) Run() {
|
||||
pconn, err := net.ListenPacket("udp4", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.udpServ.PacketConn = pconn
|
||||
s.udpServ.Handler = s
|
||||
go s.udpServ.ActivateAndServe()
|
||||
}
|
||||
|
||||
func (s *IPAddrTestServer) Close() {
|
||||
s.udpServ.PacketConn.Close()
|
||||
}
|
||||
|
||||
func (s *IPAddrTestServer) Addr() *net.UDPAddr {
|
||||
return s.udpServ.PacketConn.LocalAddr().(*net.UDPAddr)
|
||||
}
|
||||
|
||||
func (s *IPAddrTestServer) ServeDNS(w dns.ResponseWriter, m *dns.Msg) {
|
||||
q := m.Question[0]
|
||||
|
||||
var (
|
||||
act TestSrvAction
|
||||
ad bool
|
||||
)
|
||||
switch q.Qtype {
|
||||
case dns.TypeA:
|
||||
act = s.aAction
|
||||
ad = s.aAD
|
||||
case dns.TypeAAAA:
|
||||
act = s.aaaaAction
|
||||
ad = s.aaaaAD
|
||||
default:
|
||||
panic("wtf qtype")
|
||||
}
|
||||
|
||||
reply := new(dns.Msg)
|
||||
reply.SetReply(m)
|
||||
reply.RecursionAvailable = true
|
||||
reply.AuthenticatedData = ad
|
||||
|
||||
switch act {
|
||||
case TestSrvTimeout:
|
||||
return // no nobody heard from him since...
|
||||
case TestSrvServfail:
|
||||
reply.Rcode = dns.RcodeServerFailure
|
||||
case TestSrvNoAddr:
|
||||
case TestSrvOk:
|
||||
switch q.Qtype {
|
||||
case dns.TypeA:
|
||||
reply.Answer = append(reply.Answer, &dns.A{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 9999,
|
||||
},
|
||||
A: net.ParseIP("127.0.0.1"),
|
||||
})
|
||||
case dns.TypeAAAA:
|
||||
reply.Answer = append(reply.Answer, &dns.AAAA{
|
||||
Hdr: dns.RR_Header{
|
||||
Name: q.Name,
|
||||
Rrtype: dns.TypeAAAA,
|
||||
Class: dns.ClassINET,
|
||||
Ttl: 9999,
|
||||
},
|
||||
AAAA: net.ParseIP("::1"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
w.WriteMsg(reply)
|
||||
}
|
||||
|
||||
func TestExtResolver_AuthLookupIPAddr(t *testing.T) {
|
||||
// AuthLookupIPAddr has a rather convoluted logic for combined A/AAAA
|
||||
// lookups that return the best-effort result and also has some nuanced in
|
||||
// AD flag handling for use in DANE algorithms.
|
||||
|
||||
// Silence log messages about disregarded I/O errors.
|
||||
log.DefaultLogger.Out = nil
|
||||
|
||||
test := func(aAct, aaaaAct TestSrvAction, aAD, aaaaAD bool, ad bool, addrs []net.IP, err bool) {
|
||||
t.Helper()
|
||||
t.Run(fmt.Sprintln(aAct, aaaaAct, aAD, aaaaAD), func(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
s := IPAddrTestServer{}
|
||||
s.aAction = aAct
|
||||
s.aaaaAction = aaaaAct
|
||||
s.aAD = aAD
|
||||
s.aaaaAD = aaaaAD
|
||||
s.Run()
|
||||
defer s.Close()
|
||||
res := ExtResolver{
|
||||
cl: new(dns.Client),
|
||||
Cfg: &dns.ClientConfig{
|
||||
Servers: []string{"127.0.0.1"},
|
||||
Port: strconv.Itoa(s.Addr().Port),
|
||||
Timeout: 1,
|
||||
},
|
||||
}
|
||||
res.cl.Dialer = &net.Dialer{
|
||||
Timeout: 500 * time.Millisecond,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
actualAd, actualAddrs, actualErr := res.AuthLookupIPAddr(ctx, "maddy.test")
|
||||
if (actualErr != nil) != err {
|
||||
t.Fatal("actualErr:", actualErr, "expectedErr:", err)
|
||||
}
|
||||
if actualAd != ad {
|
||||
t.Error("actualAd:", actualAd, "expectedAd:", ad)
|
||||
}
|
||||
ipAddrs := make([]net.IPAddr, 0, len(addrs))
|
||||
if len(addrs) == 0 {
|
||||
ipAddrs = nil // lookup returns nil addrs for error cases
|
||||
}
|
||||
for _, a := range addrs {
|
||||
ipAddrs = append(ipAddrs, net.IPAddr{IP: a, Zone: ""})
|
||||
}
|
||||
if !reflect.DeepEqual(actualAddrs, ipAddrs) {
|
||||
t.Logf("actualAddrs: %#+v", actualAddrs)
|
||||
t.Logf("addrs: %#+v", ipAddrs)
|
||||
t.Fail()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
test(TestSrvOk, TestSrvOk, true, true, true, []net.IP{net.ParseIP("::1"), net.ParseIP("127.0.0.1").To4()}, false)
|
||||
test(TestSrvOk, TestSrvOk, true, false, true, []net.IP{net.ParseIP("127.0.0.1").To4()}, false)
|
||||
test(TestSrvOk, TestSrvOk, false, true, false, []net.IP{net.ParseIP("::1"), net.ParseIP("127.0.0.1").To4()}, false)
|
||||
test(TestSrvOk, TestSrvOk, false, false, false, []net.IP{net.ParseIP("::1"), net.ParseIP("127.0.0.1").To4()}, false)
|
||||
test(TestSrvOk, TestSrvTimeout, true, true, true, []net.IP{net.ParseIP("127.0.0.1").To4()}, false)
|
||||
test(TestSrvOk, TestSrvServfail, true, true, true, []net.IP{net.ParseIP("127.0.0.1").To4()}, false)
|
||||
test(TestSrvOk, TestSrvNoAddr, true, true, true, []net.IP{net.ParseIP("127.0.0.1").To4()}, false)
|
||||
test(TestSrvNoAddr, TestSrvOk, true, true, true, []net.IP{net.ParseIP("::1")}, false)
|
||||
test(TestSrvServfail, TestSrvServfail, true, true, false, nil, true)
|
||||
|
||||
// actualAd is false, we don't want to risk reporting positive AD result if
|
||||
// something is wrong with IPv4 lookup.
|
||||
test(TestSrvTimeout, TestSrvOk, true, true, false, []net.IP{net.ParseIP("::1")}, false)
|
||||
test(TestSrvServfail, TestSrvOk, true, true, false, []net.IP{net.ParseIP("::1")}, false)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue