c9943dff38
This pull request started as a draft to enable users to see CNAME answers. It contained several patches which we merged separately (see https://github.com/ooni/probe-cli/pull/873#issuecomment-1222406732 and 2301a30630...60b7d1f87b
for details on what has actually changed, which is based on patches originally part of this PR). In its final form, however, this PR only deals with exposing more low-level DNS fields to the archival data format.
Closes: https://github.com/ooni/probe/issues/2228
Related PR spec: https://github.com/ooni/spec/pull/256
695 lines
18 KiB
Go
695 lines
18 KiB
Go
package measurexlite
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"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"
|
|
)
|
|
|
|
func TestNewResolver(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)
|
|
resolvert := trace.wrapResolver(underlying).(*resolverTrace)
|
|
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)
|
|
mockResolver := &mocks.Resolver{
|
|
MockAddress: func() string {
|
|
return "dns.google"
|
|
},
|
|
MockNetwork: func() string {
|
|
return "udp"
|
|
},
|
|
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
|
|
},
|
|
MockCloseIdleConnections: func() {
|
|
called = true
|
|
},
|
|
}
|
|
resolver := trace.wrapResolver(mockResolver)
|
|
|
|
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")
|
|
}
|
|
})
|
|
|
|
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)
|
|
}
|
|
})
|
|
|
|
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
|
|
},
|
|
MockDecodeCNAME: func() (string, error) {
|
|
return "dns.google.", nil
|
|
},
|
|
MockRcode: func() int {
|
|
return 0
|
|
},
|
|
MockBytes: func() []byte {
|
|
return []byte{}
|
|
},
|
|
}
|
|
return response, nil
|
|
},
|
|
MockRequiresPadding: func() bool {
|
|
return true
|
|
},
|
|
MockNetwork: func() string {
|
|
return "mocked"
|
|
},
|
|
MockAddress: func() string {
|
|
return "dns.google"
|
|
},
|
|
}
|
|
r := netxlite.NewUnwrappedParallelResolver(txp)
|
|
resolver := trace.wrapResolver(r)
|
|
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)
|
|
}
|
|
|
|
t.Run("DNSLookup events", func(t *testing.T) {
|
|
events := trace.DNSLookupsFromRoundTrip()
|
|
if len(events) != 2 {
|
|
t.Fatal("unexpected DNS events")
|
|
}
|
|
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) != 2 {
|
|
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")
|
|
}
|
|
if ev.Answers[1].AnswerType != "CNAME " && ev.Answers[1].Hostname != "dns.google." {
|
|
t.Fatal("unexpected second answer (expected CNAME)", ev.Answers[1])
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("LookupHost discards events when buffers are full", func(t *testing.T) {
|
|
zeroTime := time.Now()
|
|
td := testingx.NewTimeDeterministic(zeroTime)
|
|
trace := NewTrace(0, zeroTime)
|
|
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) {
|
|
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
|
|
},
|
|
MockDecodeCNAME: func() (string, error) {
|
|
return "dns.google.", nil
|
|
},
|
|
MockRcode: func() int {
|
|
return 0
|
|
},
|
|
MockBytes: func() []byte {
|
|
return []byte{}
|
|
},
|
|
}
|
|
return response, nil
|
|
},
|
|
MockRequiresPadding: func() bool {
|
|
return true
|
|
},
|
|
MockNetwork: func() string {
|
|
return ""
|
|
},
|
|
MockAddress: func() string {
|
|
return "dns.google"
|
|
},
|
|
}
|
|
r := netxlite.NewUnwrappedParallelResolver(txp)
|
|
resolver := trace.wrapResolver(r)
|
|
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)
|
|
}
|
|
|
|
t.Run("DNSLookup Events", func(t *testing.T) {
|
|
events := trace.DNSLookupsFromRoundTrip()
|
|
if len(events) != 0 {
|
|
t.Fatal("expected to see no DNSLookup events")
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
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")
|
|
}
|
|
if resolver.Network() != "system" {
|
|
t.Fatal("unexpected resolver network")
|
|
}
|
|
})
|
|
}
|
|
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
|
|
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
|
|
dnsResponse := &mocks.DNSResponse{
|
|
MockDecodeCNAME: func() (string, error) {
|
|
return "", netxlite.ErrOODNSNoAnswer
|
|
},
|
|
MockRcode: func() int {
|
|
return 0
|
|
},
|
|
MockBytes: func() []byte {
|
|
return []byte{}
|
|
},
|
|
}
|
|
err := trace.OnDelayedDNSResponse(started, txp, query, dnsResponse, addrs, nil, finished)
|
|
// 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
|
|
dnsResponse := &mocks.DNSResponse{
|
|
MockDecodeCNAME: func() (string, error) {
|
|
return "", netxlite.ErrOODNSNoAnswer
|
|
},
|
|
MockRcode: func() int {
|
|
return 0
|
|
},
|
|
MockBytes: func() []byte {
|
|
return []byte{}
|
|
},
|
|
}
|
|
err := trace.OnDelayedDNSResponse(started, txp, query, dnsResponse, addrs, nil, finished)
|
|
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
|
|
dnsResponse := &mocks.DNSResponse{
|
|
MockDecodeCNAME: func() (string, error) {
|
|
return "", netxlite.ErrOODNSNoAnswer
|
|
},
|
|
MockRcode: func() int {
|
|
return 0
|
|
},
|
|
MockBytes: func() []byte {
|
|
return []byte{}
|
|
},
|
|
}
|
|
for i := 0; i < events; i++ {
|
|
// fill the trace
|
|
trace.delayedDNSResponse <- NewArchivalDNSLookupResultFromRoundTrip(trace.Index, started.Sub(trace.ZeroTime),
|
|
txp, query, dnsResponse, addrs, nil, finished.Sub(trace.ZeroTime))
|
|
}
|
|
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()
|
|
dnsResponse := &mocks.DNSResponse{
|
|
MockDecodeCNAME: func() (string, error) {
|
|
return "", netxlite.ErrOODNSNoAnswer
|
|
},
|
|
MockRcode: func() int {
|
|
return 0
|
|
},
|
|
MockBytes: func() []byte {
|
|
return []byte{}
|
|
},
|
|
}
|
|
trace.delayedDNSResponse <- NewArchivalDNSLookupResultFromRoundTrip(trace.Index, started.Sub(trace.ZeroTime),
|
|
txp, query, dnsResponse, addrs, nil, finished.Sub(trace.ZeroTime))
|
|
got := trace.DelayedDNSResponseWithTimeout(context.Background(), time.Second)
|
|
if len(got) != 1 {
|
|
t.Fatal("unexpected output from trace")
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestNewArchivalDNSAnswers(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
addrs []string
|
|
resp model.DNSResponse
|
|
expected []model.ArchivalDNSAnswer
|
|
}{{
|
|
name: "with valid input",
|
|
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,
|
|
}},
|
|
}, {
|
|
name: "with invalid IPv4 address",
|
|
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,
|
|
}},
|
|
}, {
|
|
name: "with invalid IPv6 address",
|
|
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,
|
|
}, {
|
|
name: "with nil input",
|
|
addrs: nil,
|
|
resp: nil,
|
|
expected: nil,
|
|
}, {
|
|
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,
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := newArchivalDNSAnswers(tt.addrs, tt.resp)
|
|
if diff := cmp.Diff(tt.expected, got); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
})
|
|
}
|
|
}
|