2022-07-08 19:42:24 +02:00
|
|
|
package measurexlite
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-08-22 14:21:32 +02:00
|
|
|
"errors"
|
2022-08-11 18:18:25 +02:00
|
|
|
"net"
|
2022-07-08 19:42:24 +02:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2022-08-11 18:18:25 +02:00
|
|
|
"github.com/google/go-cmp/cmp"
|
2022-07-08 19:42:24 +02:00
|
|
|
"github.com/miekg/dns"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/testingx"
|
|
|
|
)
|
|
|
|
|
2022-08-11 18:18:25 +02:00
|
|
|
func TestNewResolver(t *testing.T) {
|
2022-08-11 16:00:37 +02:00
|
|
|
t.Run("WrapResolver creates a wrapped resolver with Trace", func(t *testing.T) {
|
2022-07-08 19:42:24 +02:00
|
|
|
underlying := &mocks.Resolver{}
|
|
|
|
zeroTime := time.Now()
|
|
|
|
trace := NewTrace(0, zeroTime)
|
2022-08-11 16:00:37 +02:00
|
|
|
resolvert := trace.wrapResolver(underlying).(*resolverTrace)
|
2022-07-08 19:42:24 +02:00
|
|
|
if resolvert.r != underlying {
|
|
|
|
t.Fatal("invalid parallel resolver")
|
|
|
|
}
|
|
|
|
if resolvert.tx != trace {
|
|
|
|
t.Fatal("invalid trace")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Trace-aware resolver forwards underlying functions", func(t *testing.T) {
|
|
|
|
var called bool
|
|
|
|
zeroTime := time.Now()
|
|
|
|
trace := NewTrace(0, zeroTime)
|
2022-08-11 16:00:37 +02:00
|
|
|
mockResolver := &mocks.Resolver{
|
|
|
|
MockAddress: func() string {
|
|
|
|
return "dns.google"
|
|
|
|
},
|
|
|
|
MockNetwork: func() string {
|
|
|
|
return "udp"
|
|
|
|
},
|
2022-08-11 18:18:25 +02:00
|
|
|
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
|
|
|
|
return []string{"1.1.1.1"}, nil
|
|
|
|
},
|
|
|
|
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
|
|
|
return &model.HTTPSSvc{
|
|
|
|
IPv4: []string{"1.1.1.1"},
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
MockLookupNS: func(ctx context.Context, domain string) ([]*net.NS, error) {
|
|
|
|
return []*net.NS{{
|
|
|
|
Host: "1.1.1.1",
|
|
|
|
}}, nil
|
|
|
|
},
|
2022-08-11 16:00:37 +02:00
|
|
|
MockCloseIdleConnections: func() {
|
|
|
|
called = true
|
|
|
|
},
|
2022-07-08 19:42:24 +02:00
|
|
|
}
|
2022-08-11 16:00:37 +02:00
|
|
|
resolver := trace.wrapResolver(mockResolver)
|
2022-07-08 19:42:24 +02:00
|
|
|
|
|
|
|
t.Run("Address is correctly forwarded", func(t *testing.T) {
|
|
|
|
got := resolver.Address()
|
|
|
|
if got != "dns.google" {
|
|
|
|
t.Fatal("Address not called")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Network is correctly forwarded", func(t *testing.T) {
|
|
|
|
got := resolver.Network()
|
|
|
|
if got != "udp" {
|
|
|
|
t.Fatal("Network not called")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2022-08-11 18:18:25 +02:00
|
|
|
t.Run("LookupHost is correctly forwarded", func(t *testing.T) {
|
|
|
|
want := []string{"1.1.1.1"}
|
|
|
|
ctx := context.Background()
|
|
|
|
got, err := resolver.LookupHost(ctx, "example.com")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("expected nil error")
|
|
|
|
}
|
|
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("LookupHTTPS is correctly forwarded", func(t *testing.T) {
|
|
|
|
want := &model.HTTPSSvc{
|
|
|
|
IPv4: []string{"1.1.1.1"},
|
|
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
got, err := resolver.LookupHTTPS(ctx, "example.com")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("expected nil error")
|
|
|
|
}
|
|
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("LookupNS is correctly forwarded", func(t *testing.T) {
|
|
|
|
want := []*net.NS{{
|
|
|
|
Host: "1.1.1.1",
|
|
|
|
}}
|
|
|
|
ctx := context.Background()
|
|
|
|
got, err := resolver.LookupNS(ctx, "example.com")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("expected nil error")
|
|
|
|
}
|
|
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2022-07-08 19:42:24 +02:00
|
|
|
t.Run("CloseIdleConnections is correctly forwarded", func(t *testing.T) {
|
|
|
|
resolver.CloseIdleConnections()
|
|
|
|
if !called {
|
|
|
|
t.Fatal("CloseIdleConnections not called")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("LookupHost saves into trace", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
td := testingx.NewTimeDeterministic(zeroTime)
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
trace.TimeNowFn = td.Now
|
|
|
|
txp := &mocks.DNSTransport{
|
|
|
|
MockRoundTrip: func(ctx context.Context, query model.DNSQuery) (model.DNSResponse, error) {
|
|
|
|
response := &mocks.DNSResponse{
|
|
|
|
MockDecodeLookupHost: func() ([]string, error) {
|
|
|
|
if query.Type() != dns.TypeA {
|
|
|
|
return []string{"fe80::a00:20ff:feb9:4c54"}, nil
|
|
|
|
}
|
|
|
|
return []string{"1.1.1.1"}, nil
|
|
|
|
},
|
2022-08-23 15:13:37 +02:00
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "dns.google.", nil
|
|
|
|
},
|
2022-08-23 16:12:04 +02:00
|
|
|
MockRcode: func() int {
|
|
|
|
return 0
|
|
|
|
},
|
|
|
|
MockBytes: func() []byte {
|
|
|
|
return []byte{}
|
|
|
|
},
|
2022-07-08 19:42:24 +02:00
|
|
|
}
|
|
|
|
return response, nil
|
|
|
|
},
|
|
|
|
MockRequiresPadding: func() bool {
|
|
|
|
return true
|
|
|
|
},
|
|
|
|
MockNetwork: func() string {
|
2022-08-11 16:00:37 +02:00
|
|
|
return "mocked"
|
2022-07-08 19:42:24 +02:00
|
|
|
},
|
|
|
|
MockAddress: func() string {
|
|
|
|
return "dns.google"
|
|
|
|
},
|
|
|
|
}
|
2022-08-11 16:00:37 +02:00
|
|
|
r := netxlite.NewUnwrappedParallelResolver(txp)
|
|
|
|
resolver := trace.wrapResolver(r)
|
2022-07-08 19:42:24 +02:00
|
|
|
ctx := context.Background()
|
|
|
|
addrs, err := resolver.LookupHost(ctx, "example.com")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("unexpected err", err)
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2022-08-11 16:00:37 +02:00
|
|
|
t.Run("DNSLookup events", func(t *testing.T) {
|
|
|
|
events := trace.DNSLookupsFromRoundTrip()
|
|
|
|
if len(events) != 2 {
|
2022-09-08 11:05:38 +02:00
|
|
|
t.Fatal("unexpected DNS events length")
|
2022-08-11 16:00:37 +02:00
|
|
|
}
|
|
|
|
for _, ev := range events {
|
|
|
|
if ev.ResolverAddress != "dns.google" {
|
|
|
|
t.Fatal("unexpected resolver address")
|
|
|
|
}
|
|
|
|
if ev.Engine != "mocked" {
|
|
|
|
t.Fatal("unexpected engine")
|
|
|
|
}
|
2022-08-23 15:13:37 +02:00
|
|
|
if len(ev.Answers) != 2 {
|
2022-08-11 16:00:37 +02:00
|
|
|
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")
|
|
|
|
}
|
2022-08-23 15:13:37 +02:00
|
|
|
if ev.Answers[1].AnswerType != "CNAME " && ev.Answers[1].Hostname != "dns.google." {
|
|
|
|
t.Fatal("unexpected second answer (expected CNAME)", ev.Answers[1])
|
|
|
|
}
|
2022-07-08 19:42:24 +02:00
|
|
|
}
|
|
|
|
})
|
2022-09-08 11:05:38 +02:00
|
|
|
|
|
|
|
t.Run("Network events", func(t *testing.T) {
|
|
|
|
events := trace.NetworkEvents()
|
|
|
|
if len(events) != 2 {
|
|
|
|
t.Fatal("unexpected network events length")
|
|
|
|
}
|
|
|
|
foundNames := map[string]int{}
|
|
|
|
for _, ev := range events {
|
|
|
|
foundNames[ev.Operation]++
|
|
|
|
}
|
|
|
|
if foundNames["resolve_start"] != 1 {
|
|
|
|
t.Fatal("missing resolve_start")
|
|
|
|
}
|
|
|
|
if foundNames["resolve_done"] != 1 {
|
|
|
|
t.Fatal("missing resolve_done")
|
|
|
|
}
|
|
|
|
})
|
2022-07-08 19:42:24 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("LookupHost discards events when buffers are full", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
td := testingx.NewTimeDeterministic(zeroTime)
|
|
|
|
trace := NewTrace(0, zeroTime)
|
2022-08-17 20:10:48 +02:00
|
|
|
trace.dnsLookup = make(chan *model.ArchivalDNSLookupResult) // no buffer
|
2022-09-08 11:05:38 +02:00
|
|
|
trace.networkEvent = make(chan *model.ArchivalNetworkEvent) // ditto
|
2022-07-08 19:42:24 +02:00
|
|
|
trace.TimeNowFn = td.Now
|
|
|
|
txp := &mocks.DNSTransport{
|
|
|
|
MockRoundTrip: func(ctx context.Context, query model.DNSQuery) (model.DNSResponse, error) {
|
|
|
|
response := &mocks.DNSResponse{
|
|
|
|
MockDecodeLookupHost: func() ([]string, error) {
|
|
|
|
if query.Type() != dns.TypeA {
|
|
|
|
return []string{"fe80::a00:20ff:feb9:4c54"}, nil
|
|
|
|
}
|
|
|
|
return []string{"1.1.1.1"}, nil
|
|
|
|
},
|
2022-08-23 15:13:37 +02:00
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "dns.google.", nil
|
|
|
|
},
|
2022-08-23 16:12:04 +02:00
|
|
|
MockRcode: func() int {
|
|
|
|
return 0
|
|
|
|
},
|
|
|
|
MockBytes: func() []byte {
|
|
|
|
return []byte{}
|
|
|
|
},
|
2022-07-08 19:42:24 +02:00
|
|
|
}
|
|
|
|
return response, nil
|
|
|
|
},
|
|
|
|
MockRequiresPadding: func() bool {
|
|
|
|
return true
|
|
|
|
},
|
|
|
|
MockNetwork: func() string {
|
|
|
|
return ""
|
|
|
|
},
|
|
|
|
MockAddress: func() string {
|
|
|
|
return "dns.google"
|
|
|
|
},
|
|
|
|
}
|
2022-08-11 16:00:37 +02:00
|
|
|
r := netxlite.NewUnwrappedParallelResolver(txp)
|
|
|
|
resolver := trace.wrapResolver(r)
|
2022-07-08 19:42:24 +02:00
|
|
|
ctx := context.Background()
|
|
|
|
addrs, err := resolver.LookupHost(ctx, "example.com")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("unexpected err", err)
|
|
|
|
}
|
|
|
|
if len(addrs) != 2 {
|
|
|
|
t.Fatal("unexpected array output", addrs)
|
|
|
|
}
|
2022-08-11 16:00:37 +02:00
|
|
|
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)
|
|
|
|
}
|
2022-07-08 19:42:24 +02:00
|
|
|
|
2022-08-11 16:00:37 +02:00
|
|
|
t.Run("DNSLookup Events", func(t *testing.T) {
|
|
|
|
events := trace.DNSLookupsFromRoundTrip()
|
2022-07-08 19:42:24 +02:00
|
|
|
if len(events) != 0 {
|
2022-08-11 16:00:37 +02:00
|
|
|
t.Fatal("expected to see no DNSLookup events")
|
2022-07-08 19:42:24 +02:00
|
|
|
}
|
|
|
|
})
|
2022-09-08 11:05:38 +02:00
|
|
|
|
|
|
|
t.Run("Network events", func(t *testing.T) {
|
|
|
|
events := trace.NetworkEvents()
|
|
|
|
if len(events) != 0 {
|
|
|
|
t.Fatal("unexpected to see no network events")
|
|
|
|
}
|
|
|
|
})
|
2022-07-08 19:42:24 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-11 18:18:25 +02:00
|
|
|
func TestNewWrappedResolvers(t *testing.T) {
|
|
|
|
t.Run("NewParallelDNSOverHTTPSResolver works as intended", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
resolver := trace.NewParallelDNSOverHTTPSResolver(model.DiscardLogger, "https://dns.google.com")
|
|
|
|
resolvert := resolver.(*resolverTrace)
|
|
|
|
if resolvert.tx != trace {
|
|
|
|
t.Fatal("invalid trace")
|
|
|
|
}
|
|
|
|
if resolver.Network() != "doh" {
|
|
|
|
t.Fatal("unexpected resolver network")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("NewParallelUDPResolver works as intended", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
dialer := netxlite.NewDialerWithStdlibResolver(model.DiscardLogger)
|
|
|
|
resolver := trace.NewParallelUDPResolver(model.DiscardLogger, dialer, "1.1.1.1:53")
|
|
|
|
resolvert := resolver.(*resolverTrace)
|
|
|
|
if resolvert.tx != trace {
|
|
|
|
t.Fatal("invalid trace")
|
|
|
|
}
|
|
|
|
if resolver.Network() != "udp" {
|
|
|
|
t.Fatal("unexpected resolver network")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("NewStdlibResolver works as intended", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
resolver := trace.NewStdlibResolver(model.DiscardLogger)
|
|
|
|
resolvert := resolver.(*resolverTrace)
|
|
|
|
if resolvert.tx != trace {
|
|
|
|
t.Fatal("invalid trace")
|
|
|
|
}
|
2022-08-27 15:47:48 +02:00
|
|
|
switch network := resolver.Network(); network {
|
|
|
|
case netxlite.StdlibResolverGetaddrinfo,
|
|
|
|
netxlite.StdlibResolverGolangNetResolver:
|
|
|
|
// ok
|
|
|
|
default:
|
|
|
|
t.Fatal("unexpected resolver network", network)
|
2022-08-11 18:18:25 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-17 20:10:48 +02:00
|
|
|
func TestFirstDNSLookup(t *testing.T) {
|
|
|
|
t.Run("returns nil when buffer is empty", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
got := trace.FirstDNSLookup()
|
|
|
|
if got != nil {
|
|
|
|
t.Fatal("expected nil event")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("return first non-nil DNSLookup", func(t *testing.T) {
|
|
|
|
filler := func(tx *Trace, events []*model.ArchivalDNSLookupResult) {
|
|
|
|
for _, ev := range events {
|
|
|
|
tx.dnsLookup <- ev
|
|
|
|
}
|
|
|
|
}
|
|
|
|
zeroTime := time.Now()
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
expect := []*model.ArchivalDNSLookupResult{{
|
|
|
|
Engine: "doh",
|
|
|
|
Failure: nil,
|
|
|
|
Hostname: "example.com",
|
|
|
|
QueryType: "A",
|
|
|
|
}, {
|
|
|
|
Engine: "doh",
|
|
|
|
Failure: nil,
|
|
|
|
Hostname: "example.com",
|
|
|
|
QueryType: "AAAA",
|
|
|
|
}}
|
|
|
|
filler(trace, expect)
|
|
|
|
got := trace.FirstDNSLookup()
|
|
|
|
if diff := cmp.Diff(got, expect[0]); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-22 14:21:32 +02:00
|
|
|
func TestDelayedDNSResponseWithTimeout(t *testing.T) {
|
|
|
|
t.Run("OnDelayedDNSResponse saves into the trace", func(t *testing.T) {
|
|
|
|
t.Run("when buffer is not full", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
td := testingx.NewTimeDeterministic(zeroTime)
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
trace.TimeNowFn = td.Now
|
|
|
|
txp := &mocks.DNSTransport{
|
|
|
|
MockNetwork: func() string {
|
|
|
|
return "udp"
|
|
|
|
},
|
|
|
|
MockAddress: func() string {
|
|
|
|
return "1.1.1.1"
|
|
|
|
},
|
|
|
|
}
|
|
|
|
started := trace.TimeNow()
|
|
|
|
query := &mocks.DNSQuery{
|
|
|
|
MockType: func() uint16 {
|
|
|
|
return dns.TypeA
|
|
|
|
},
|
|
|
|
MockDomain: func() string {
|
|
|
|
return "dns.google.com"
|
|
|
|
},
|
|
|
|
}
|
|
|
|
addrs := []string{"1.1.1.1"}
|
|
|
|
finished := trace.TimeNow()
|
|
|
|
// 1. fill the trace
|
2022-08-23 15:13:37 +02:00
|
|
|
dnsResponse := &mocks.DNSResponse{
|
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "", netxlite.ErrOODNSNoAnswer
|
|
|
|
},
|
2022-08-23 16:12:04 +02:00
|
|
|
MockRcode: func() int {
|
|
|
|
return 0
|
|
|
|
},
|
|
|
|
MockBytes: func() []byte {
|
|
|
|
return []byte{}
|
|
|
|
},
|
2022-08-23 15:13:37 +02:00
|
|
|
}
|
|
|
|
err := trace.OnDelayedDNSResponse(started, txp, query, dnsResponse, addrs, nil, finished)
|
2022-08-22 14:21:32 +02:00
|
|
|
// 2. read the trace
|
|
|
|
got := trace.DelayedDNSResponseWithTimeout(context.Background(), time.Second)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("unexpected error", err)
|
|
|
|
}
|
|
|
|
if len(got) != 1 {
|
|
|
|
t.Fatal("unexpected output from trace")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("when buffer is full", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
td := testingx.NewTimeDeterministic(zeroTime)
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
trace.TimeNowFn = td.Now
|
|
|
|
trace.delayedDNSResponse = make(chan *model.ArchivalDNSLookupResult) // no buffer
|
|
|
|
txp := &mocks.DNSTransport{
|
|
|
|
MockNetwork: func() string {
|
|
|
|
return "udp"
|
|
|
|
},
|
|
|
|
MockAddress: func() string {
|
|
|
|
return "1.1.1.1"
|
|
|
|
},
|
|
|
|
}
|
|
|
|
started := trace.TimeNow()
|
|
|
|
query := &mocks.DNSQuery{
|
|
|
|
MockType: func() uint16 {
|
|
|
|
return dns.TypeA
|
|
|
|
},
|
|
|
|
MockDomain: func() string {
|
|
|
|
return "dns.google.com"
|
|
|
|
},
|
|
|
|
}
|
|
|
|
addrs := []string{"1.1.1.1"}
|
|
|
|
finished := trace.TimeNow()
|
|
|
|
// 1. attempt to write into the trace
|
2022-08-23 15:13:37 +02:00
|
|
|
dnsResponse := &mocks.DNSResponse{
|
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "", netxlite.ErrOODNSNoAnswer
|
|
|
|
},
|
2022-08-23 16:12:04 +02:00
|
|
|
MockRcode: func() int {
|
|
|
|
return 0
|
|
|
|
},
|
|
|
|
MockBytes: func() []byte {
|
|
|
|
return []byte{}
|
|
|
|
},
|
2022-08-23 15:13:37 +02:00
|
|
|
}
|
|
|
|
err := trace.OnDelayedDNSResponse(started, txp, query, dnsResponse, addrs, nil, finished)
|
2022-08-22 14:21:32 +02:00
|
|
|
if !errors.Is(err, ErrDelayedDNSResponseBufferFull) {
|
|
|
|
t.Fatal("unexpected error", err)
|
|
|
|
}
|
|
|
|
// 2. confirm we didn't write anything
|
|
|
|
got := trace.DelayedDNSResponseWithTimeout(context.Background(), time.Second)
|
|
|
|
if len(got) != 0 {
|
|
|
|
t.Fatal("unexpected output from trace")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("DelayedDNSResponseWithTimeout drains the trace", func(t *testing.T) {
|
|
|
|
t.Run("context is already cancelled and we still drain the trace", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
td := testingx.NewTimeDeterministic(zeroTime)
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
trace.TimeNowFn = td.Now
|
|
|
|
txp := &mocks.DNSTransport{
|
|
|
|
MockNetwork: func() string {
|
|
|
|
return "udp"
|
|
|
|
},
|
|
|
|
MockAddress: func() string {
|
|
|
|
return "1.1.1.1"
|
|
|
|
},
|
|
|
|
}
|
|
|
|
started := trace.TimeNow()
|
|
|
|
query := &mocks.DNSQuery{
|
|
|
|
MockType: func() uint16 {
|
|
|
|
return dns.TypeA
|
|
|
|
},
|
|
|
|
MockDomain: func() string {
|
|
|
|
return "dns.google.com"
|
|
|
|
},
|
|
|
|
}
|
|
|
|
addrs := []string{"1.1.1.1"}
|
|
|
|
finished := trace.TimeNow()
|
|
|
|
events := 4
|
2022-08-23 15:13:37 +02:00
|
|
|
dnsResponse := &mocks.DNSResponse{
|
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "", netxlite.ErrOODNSNoAnswer
|
|
|
|
},
|
2022-08-23 16:12:04 +02:00
|
|
|
MockRcode: func() int {
|
|
|
|
return 0
|
|
|
|
},
|
|
|
|
MockBytes: func() []byte {
|
|
|
|
return []byte{}
|
|
|
|
},
|
2022-08-23 15:13:37 +02:00
|
|
|
}
|
2022-08-22 14:21:32 +02:00
|
|
|
for i := 0; i < events; i++ {
|
|
|
|
// fill the trace
|
|
|
|
trace.delayedDNSResponse <- NewArchivalDNSLookupResultFromRoundTrip(trace.Index, started.Sub(trace.ZeroTime),
|
2022-08-23 15:13:37 +02:00
|
|
|
txp, query, dnsResponse, addrs, nil, finished.Sub(trace.ZeroTime))
|
2022-08-22 14:21:32 +02:00
|
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
cancel() // we ensure that the context cancels before draining all the events
|
|
|
|
// drain the trace
|
|
|
|
got := trace.DelayedDNSResponseWithTimeout(ctx, 10*time.Second)
|
|
|
|
if len(got) != 4 {
|
|
|
|
t.Fatal("unexpected output from trace", len(got))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("normal case where the context times out after we start draining", func(t *testing.T) {
|
|
|
|
zeroTime := time.Now()
|
|
|
|
td := testingx.NewTimeDeterministic(zeroTime)
|
|
|
|
trace := NewTrace(0, zeroTime)
|
|
|
|
trace.TimeNowFn = td.Now
|
|
|
|
txp := &mocks.DNSTransport{
|
|
|
|
MockNetwork: func() string {
|
|
|
|
return "udp"
|
|
|
|
},
|
|
|
|
MockAddress: func() string {
|
|
|
|
return "1.1.1.1"
|
|
|
|
},
|
|
|
|
}
|
|
|
|
started := trace.TimeNow()
|
|
|
|
query := &mocks.DNSQuery{
|
|
|
|
MockType: func() uint16 {
|
|
|
|
return dns.TypeA
|
|
|
|
},
|
|
|
|
MockDomain: func() string {
|
|
|
|
return "dns.google.com"
|
|
|
|
},
|
|
|
|
}
|
|
|
|
addrs := []string{"1.1.1.1"}
|
|
|
|
finished := trace.TimeNow()
|
2022-08-23 15:13:37 +02:00
|
|
|
dnsResponse := &mocks.DNSResponse{
|
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "", netxlite.ErrOODNSNoAnswer
|
|
|
|
},
|
2022-08-23 16:12:04 +02:00
|
|
|
MockRcode: func() int {
|
|
|
|
return 0
|
|
|
|
},
|
|
|
|
MockBytes: func() []byte {
|
|
|
|
return []byte{}
|
|
|
|
},
|
2022-08-23 15:13:37 +02:00
|
|
|
}
|
2022-08-22 14:21:32 +02:00
|
|
|
trace.delayedDNSResponse <- NewArchivalDNSLookupResultFromRoundTrip(trace.Index, started.Sub(trace.ZeroTime),
|
2022-08-23 15:13:37 +02:00
|
|
|
txp, query, dnsResponse, addrs, nil, finished.Sub(trace.ZeroTime))
|
2022-08-22 14:21:32 +02:00
|
|
|
got := trace.DelayedDNSResponseWithTimeout(context.Background(), time.Second)
|
|
|
|
if len(got) != 1 {
|
|
|
|
t.Fatal("unexpected output from trace")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-08-23 15:13:37 +02:00
|
|
|
func TestNewArchivalDNSAnswers(t *testing.T) {
|
2022-07-08 19:42:24 +02:00
|
|
|
tests := []struct {
|
2022-08-23 15:13:37 +02:00
|
|
|
name string
|
|
|
|
addrs []string
|
|
|
|
resp model.DNSResponse
|
|
|
|
expected []model.ArchivalDNSAnswer
|
2022-07-08 19:42:24 +02:00
|
|
|
}{{
|
|
|
|
name: "with valid input",
|
2022-08-23 15:13:37 +02:00
|
|
|
addrs: []string{
|
|
|
|
"8.8.4.4",
|
|
|
|
"2001:4860:4860::8844",
|
|
|
|
},
|
|
|
|
resp: nil,
|
|
|
|
expected: []model.ArchivalDNSAnswer{{
|
|
|
|
ASN: 15169,
|
|
|
|
ASOrgName: "Google LLC",
|
|
|
|
AnswerType: "A",
|
|
|
|
Hostname: "",
|
|
|
|
IPv4: "8.8.4.4",
|
|
|
|
IPv6: "",
|
|
|
|
TTL: nil,
|
|
|
|
}, {
|
|
|
|
ASN: 15169,
|
|
|
|
ASOrgName: "Google LLC",
|
|
|
|
AnswerType: "AAAA",
|
|
|
|
Hostname: "",
|
|
|
|
IPv4: "",
|
|
|
|
IPv6: "2001:4860:4860::8844",
|
|
|
|
TTL: nil,
|
|
|
|
}},
|
2022-07-08 19:42:24 +02:00
|
|
|
}, {
|
|
|
|
name: "with invalid IPv4 address",
|
2022-08-23 15:13:37 +02:00
|
|
|
addrs: []string{
|
|
|
|
"1.1.1.1.1", // invalid because it has five dots
|
|
|
|
"2001:4860:4860::8844",
|
|
|
|
},
|
|
|
|
resp: nil,
|
|
|
|
expected: []model.ArchivalDNSAnswer{{
|
|
|
|
ASN: 15169,
|
|
|
|
ASOrgName: "Google LLC",
|
|
|
|
AnswerType: "AAAA",
|
|
|
|
Hostname: "",
|
|
|
|
IPv4: "",
|
|
|
|
IPv6: "2001:4860:4860::8844",
|
|
|
|
TTL: nil,
|
|
|
|
}},
|
2022-07-08 19:42:24 +02:00
|
|
|
}, {
|
|
|
|
name: "with invalid IPv6 address",
|
2022-08-23 15:13:37 +02:00
|
|
|
addrs: []string{
|
|
|
|
"8.8.4.4",
|
|
|
|
"fe80::a00:20ff:feb9:::4c54", // invalid because it has :::
|
|
|
|
},
|
|
|
|
resp: nil,
|
|
|
|
expected: []model.ArchivalDNSAnswer{{
|
|
|
|
ASN: 15169,
|
|
|
|
ASOrgName: "Google LLC",
|
|
|
|
AnswerType: "A",
|
|
|
|
Hostname: "",
|
|
|
|
IPv4: "8.8.4.4",
|
|
|
|
IPv6: "",
|
|
|
|
TTL: nil,
|
|
|
|
}},
|
|
|
|
}, {
|
|
|
|
name: "with empty input",
|
|
|
|
addrs: []string{},
|
|
|
|
resp: nil,
|
|
|
|
expected: nil,
|
2022-07-08 19:42:24 +02:00
|
|
|
}, {
|
2022-08-23 15:13:37 +02:00
|
|
|
name: "with nil input",
|
|
|
|
addrs: nil,
|
|
|
|
resp: nil,
|
|
|
|
expected: nil,
|
2022-07-08 19:42:24 +02:00
|
|
|
}, {
|
2022-08-23 15:13:37 +02:00
|
|
|
name: "with valid IPv4 address and CNAME",
|
|
|
|
addrs: []string{
|
|
|
|
"8.8.8.8",
|
|
|
|
},
|
|
|
|
resp: &mocks.DNSResponse{
|
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "dns.google.", nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expected: []model.ArchivalDNSAnswer{{
|
|
|
|
ASN: 15169,
|
|
|
|
ASOrgName: "Google LLC",
|
|
|
|
AnswerType: "A",
|
|
|
|
Hostname: "",
|
|
|
|
IPv4: "8.8.8.8",
|
|
|
|
IPv6: "",
|
|
|
|
TTL: nil,
|
|
|
|
}, {
|
|
|
|
ASN: 0,
|
|
|
|
ASOrgName: "",
|
|
|
|
AnswerType: "CNAME",
|
|
|
|
Hostname: "dns.google.",
|
|
|
|
IPv4: "",
|
|
|
|
IPv6: "",
|
|
|
|
TTL: nil,
|
|
|
|
}},
|
|
|
|
}, {
|
|
|
|
name: "with valid IPv6 address and CNAME",
|
|
|
|
addrs: []string{
|
|
|
|
"2001:4860:4860::8844",
|
|
|
|
},
|
|
|
|
resp: &mocks.DNSResponse{
|
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "dns.google.", nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expected: []model.ArchivalDNSAnswer{{
|
|
|
|
ASN: 15169,
|
|
|
|
ASOrgName: "Google LLC",
|
|
|
|
AnswerType: "AAAA",
|
|
|
|
Hostname: "",
|
|
|
|
IPv4: "",
|
|
|
|
IPv6: "2001:4860:4860::8844",
|
|
|
|
TTL: nil,
|
|
|
|
}, {
|
|
|
|
ASN: 0,
|
|
|
|
ASOrgName: "",
|
|
|
|
AnswerType: "CNAME",
|
|
|
|
Hostname: "dns.google.",
|
|
|
|
IPv4: "",
|
|
|
|
IPv6: "",
|
|
|
|
TTL: nil,
|
|
|
|
}},
|
|
|
|
}, {
|
|
|
|
name: "with DecodeCNAME error",
|
|
|
|
addrs: []string{},
|
|
|
|
resp: &mocks.DNSResponse{
|
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "", errors.New("mocked errorr")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expected: nil,
|
|
|
|
}, {
|
|
|
|
name: "with DecodeCNAME success and no CNAME",
|
|
|
|
addrs: []string{},
|
|
|
|
resp: &mocks.DNSResponse{
|
|
|
|
MockDecodeCNAME: func() (string, error) {
|
|
|
|
return "", nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expected: nil,
|
2022-07-08 19:42:24 +02:00
|
|
|
}}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
2022-08-23 15:13:37 +02:00
|
|
|
got := newArchivalDNSAnswers(tt.addrs, tt.resp)
|
|
|
|
if diff := cmp.Diff(tt.expected, got); diff != "" {
|
|
|
|
t.Fatal(diff)
|
2022-07-08 19:42:24 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|