feat: refactor dns implementation in measurexlite (#857)

* refactor: remove query-based mapping and introducing resolver wrapper

* refactor dnsping to adapt to measurexlite

* dnsping: extra comments

* Apply suggestions from code review

* Update internal/measurexlite/dns_test.go

See https://github.com/ooni/probe/issues/2208

Co-authored-by: decfox <decfox@github.com>
Co-authored-by: Simone Basso <bassosimone@gmail.com>
This commit is contained in:
DecFox 2022-08-11 19:30:37 +05:30 committed by GitHub
commit fc51590a67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 172 additions and 220 deletions

View file

@ -13,17 +13,11 @@ import (
)
func TestNewUnwrappedParallelResolver(t *testing.T) {
t.Run("NewUnwrappedParallelResolver creates an UnwrappedParallelResolver with Trace", func(t *testing.T) {
t.Run("WrapResolver creates a wrapped resolver with Trace", func(t *testing.T) {
underlying := &mocks.Resolver{}
zeroTime := time.Now()
trace := NewTrace(0, zeroTime)
trace.NewParallelResolverFn = func() model.Resolver {
return underlying
}
resolver := trace.newParallelResolverTrace(func() model.Resolver {
return nil
})
resolvert := resolver.(*resolverTrace)
resolvert := trace.wrapResolver(underlying).(*resolverTrace)
if resolvert.r != underlying {
t.Fatal("invalid parallel resolver")
}
@ -36,20 +30,18 @@ func TestNewUnwrappedParallelResolver(t *testing.T) {
var called bool
zeroTime := time.Now()
trace := NewTrace(0, zeroTime)
newMockResolver := func() model.Resolver {
return &mocks.Resolver{
MockAddress: func() string {
return "dns.google"
},
MockNetwork: func() string {
return "udp"
},
MockCloseIdleConnections: func() {
called = true
},
}
mockResolver := &mocks.Resolver{
MockAddress: func() string {
return "dns.google"
},
MockNetwork: func() string {
return "udp"
},
MockCloseIdleConnections: func() {
called = true
},
}
resolver := trace.newParallelResolver(newMockResolver)
resolver := trace.wrapResolver(mockResolver)
t.Run("Address is correctly forwarded", func(t *testing.T) {
got := resolver.Address()
@ -94,16 +86,14 @@ func TestNewUnwrappedParallelResolver(t *testing.T) {
return true
},
MockNetwork: func() string {
return ""
return "mocked"
},
MockAddress: func() string {
return "dns.google"
},
}
newResolver := func() model.Resolver {
return netxlite.NewUnwrappedParallelResolver(txp)
}
resolver := trace.newParallelResolverTrace(newResolver)
r := netxlite.NewUnwrappedParallelResolver(txp)
resolver := trace.wrapResolver(r)
ctx := context.Background()
addrs, err := resolver.LookupHost(ctx, "example.com")
if err != nil {
@ -119,45 +109,27 @@ func TestNewUnwrappedParallelResolver(t *testing.T) {
t.Fatal("unexpected array output", addrs)
}
t.Run("DNSLookups QueryType A", func(t *testing.T) {
events := trace.DNSLookupsFromRoundTrip(dns.TypeA)
if len(events) != 1 {
t.Fatal("expected to see single DNSLookup event")
t.Run("DNSLookup events", func(t *testing.T) {
events := trace.DNSLookupsFromRoundTrip()
if len(events) != 2 {
t.Fatal("unexpected DNS events")
}
lookup := events[0]
answers := lookup.Answers
if lookup.Failure != nil {
t.Fatal("unexpected err", *(lookup.Failure))
}
if lookup.ResolverAddress != "dns.google" {
t.Fatal("unexpected address field")
}
if len(answers) != 1 {
t.Fatal("expected 1 DNS answer, got", len(answers))
}
if answers[0].AnswerType != "A" || answers[0].IPv4 != "1.1.1.1" {
t.Fatal("unexpected DNS answer", answers)
}
})
t.Run("DNSLookups QueryType AAAA", func(t *testing.T) {
events := trace.DNSLookupsFromRoundTrip(dns.TypeAAAA)
if len(events) != 1 {
t.Fatal("expected to see single DNSLookup event")
}
lookup := events[0]
answers := lookup.Answers
if lookup.Failure != nil {
t.Fatal("unexpected err", *(lookup.Failure))
}
if lookup.ResolverAddress != "dns.google" {
t.Fatal("unexpected address field")
}
if len(answers) != 1 {
t.Fatal("expected 1 DNS answer, got", len(answers))
}
if answers[0].AnswerType != "AAAA" || answers[0].IPv6 != "fe80::a00:20ff:feb9:4c54" {
t.Fatal("unexpected DNS answer", answers)
for _, ev := range events {
if ev.ResolverAddress != "dns.google" {
t.Fatal("unexpected resolver address")
}
if ev.Engine != "mocked" {
t.Fatal("unexpected engine")
}
if len(ev.Answers) != 1 {
t.Fatal("expected single answer in DNSLookup event")
}
if ev.QueryType == "A" && ev.Answers[0].IPv4 != "1.1.1.1" {
t.Fatal("unexpected A query result")
}
if ev.QueryType == "AAAA" && ev.Answers[0].IPv6 != "fe80::a00:20ff:feb9:4c54" {
t.Fatal("unexpected AAAA query result")
}
}
})
})
@ -166,10 +138,7 @@ func TestNewUnwrappedParallelResolver(t *testing.T) {
zeroTime := time.Now()
td := testingx.NewTimeDeterministic(zeroTime)
trace := NewTrace(0, zeroTime)
trace.DNSLookup = map[uint16]chan *model.ArchivalDNSLookupResult{
dns.TypeA: make(chan *model.ArchivalDNSLookupResult), // no buffer
dns.TypeAAAA: make(chan *model.ArchivalDNSLookupResult), // no buffer
}
trace.DNSLookup = make(chan *model.ArchivalDNSLookupResult) // no buffer
trace.TimeNowFn = td.Now
txp := &mocks.DNSTransport{
MockRoundTrip: func(ctx context.Context, query model.DNSQuery) (model.DNSResponse, error) {
@ -193,10 +162,8 @@ func TestNewUnwrappedParallelResolver(t *testing.T) {
return "dns.google"
},
}
newResolver := func() model.Resolver {
return netxlite.NewUnwrappedParallelResolver(txp)
}
resolver := trace.newParallelResolverTrace(newResolver)
r := netxlite.NewUnwrappedParallelResolver(txp)
resolver := trace.wrapResolver(r)
ctx := context.Background()
addrs, err := resolver.LookupHost(ctx, "example.com")
if err != nil {
@ -205,17 +172,17 @@ func TestNewUnwrappedParallelResolver(t *testing.T) {
if len(addrs) != 2 {
t.Fatal("unexpected array output", addrs)
}
if addrs[0] != "1.1.1.1" && addrs[1] != "1.1.1.1" {
t.Fatal("unexpected array output", addrs)
}
if addrs[0] != "fe80::a00:20ff:feb9:4c54" && addrs[1] != "fe80::a00:20ff:feb9:4c54" {
t.Fatal("unexpected array output", addrs)
}
t.Run("DNSLookups QueryType A", func(t *testing.T) {
events := trace.DNSLookupsFromRoundTrip(dns.TypeA)
t.Run("DNSLookup Events", func(t *testing.T) {
events := trace.DNSLookupsFromRoundTrip()
if len(events) != 0 {
t.Fatal("expected to see no DNSLookup")
}
})
t.Run("DNSLookups QueryType AAAA", func(t *testing.T) {
events := trace.DNSLookupsFromRoundTrip(dns.TypeAAAA)
if len(events) != 0 {
t.Fatal("expected to see no DNSLookup")
t.Fatal("expected to see no DNSLookup events")
}
})
})
@ -271,26 +238,3 @@ func TestAnswersFromAddrs(t *testing.T) {
})
}
}
func TestDNSLookupsFromRoundTrips(t *testing.T) {
zeroTime := time.Now()
trace := NewTrace(0, zeroTime)
checkPanic := func(query uint16, f func(uint16) []*model.ArchivalDNSLookupResult) {
defer func() {
if r := recover(); r != nil {
t.Fatal("unexpected panic encoutered")
}
}()
f(query)
}
t.Run("DNSLookup is nil", func(t *testing.T) {
trace.DNSLookup = nil
checkPanic(dns.TypeA, trace.DNSLookupsFromRoundTrip)
})
t.Run("Query has nil channel", func(t *testing.T) {
trace.DNSLookup = map[uint16]chan *model.ArchivalDNSLookupResult{
dns.TypeA: nil,
}
checkPanic(dns.TypeA, trace.DNSLookupsFromRoundTrip)
})
}