Skip to content

Commit

Permalink
feat(webconnectivityalgo): test DNSWhoamiService (#1498)
Browse files Browse the repository at this point in the history
  • Loading branch information
bassosimone authored Feb 8, 2024
1 parent 7355ba0 commit 9c6cc44
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 15 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ require (
github.com/miekg/dns v1.1.57
github.com/mitchellh/go-wordwrap v1.0.1
github.com/montanaflynn/stats v0.7.1
github.com/ooni/netem v0.0.0-20240205182847-14e4ce92d41e
github.com/ooni/netem v0.0.0-20240208095707-608dcbcd82b8
github.com/ooni/oocrypto v0.5.7
github.com/ooni/oohttp v0.6.7
github.com/ooni/probe-assets v0.21.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
github.com/ooni/netem v0.0.0-20240205182847-14e4ce92d41e h1:OwDVOPs8NBLb1yEZXnn7rbfbBQJVOpIgEZsqq6QpqG8=
github.com/ooni/netem v0.0.0-20240205182847-14e4ce92d41e/go.mod h1:b/wAvTR5n92Vk2b0SBmuMU0xO4ZGVrsXtU7zjTby7vw=
github.com/ooni/netem v0.0.0-20240208095707-608dcbcd82b8 h1:kJ2wn19lIP/y9ng85BbFRdWKHK6Er116Bbt5uhqHVD4=
github.com/ooni/netem v0.0.0-20240208095707-608dcbcd82b8/go.mod h1:b/wAvTR5n92Vk2b0SBmuMU0xO4ZGVrsXtU7zjTby7vw=
github.com/ooni/oocrypto v0.5.7 h1:QEb1KTh5gZ9s1IQjk7rNF076YVwit+2sDKNbo39IEa8=
github.com/ooni/oocrypto v0.5.7/go.mod h1:HjEQ5pQBl6btcWgAsKKq1tFo8CfBrZu63C/vPAUGIDk=
github.com/ooni/oohttp v0.6.7 h1:wmCjx9+gzx7p1xc/kMAmgXSgXKu7G8CAmil4Zii3g10=
Expand Down
3 changes: 2 additions & 1 deletion internal/experiment/webconnectivitylte/dnswhoami.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package webconnectivitylte

import (
"github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/webconnectivityalgo"
)

// DNSWhoamiSingleton is the DNSWhoamiService singleton.
var DNSWhoamiSingleton = webconnectivityalgo.NewDNSWhoamiService()
var DNSWhoamiSingleton = webconnectivityalgo.NewDNSWhoamiService(model.DiscardLogger)
34 changes: 23 additions & 11 deletions internal/webconnectivityalgo/dnswhoami.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,41 @@ type DNSWhoamiInfoEntry struct {
// TODO(bassosimone): consider factoring this code and keeping state
// on disk rather than on memory.

// TODO(bassosimone): we should periodically invalidate the whoami lookup results.

// DNSWhoamiService is a service that performs DNS whoami lookups.
//
// The zero value of this struct is invalid. Please, construct using
// the [NewDNSWhoamiService] factory function.
type DNSWhoamiService struct {
// logger is the logger
logger model.Logger

// mu provides mutual exclusion
mu *sync.Mutex

// netx is the underlying network we're using
netx *netxlite.Netx

// systemv4 contains systemv4 results
systemv4 []DNSWhoamiInfoEntry

// udpv4 contains udpv4 results
udpv4 map[string][]DNSWhoamiInfoEntry

// whoamiDomain is the whoamiDomain to query for.
whoamiDomain string
}

// NewDNSWhoamiService constructs a new [*DNSWhoamiService].
func NewDNSWhoamiService() *DNSWhoamiService {
func NewDNSWhoamiService(logger model.Logger) *DNSWhoamiService {
return &DNSWhoamiService{
mu: &sync.Mutex{},
systemv4: []DNSWhoamiInfoEntry{},
udpv4: map[string][]DNSWhoamiInfoEntry{},
logger: logger,
mu: &sync.Mutex{},
netx: &netxlite.Netx{Underlying: nil},
systemv4: []DNSWhoamiInfoEntry{},
udpv4: map[string][]DNSWhoamiInfoEntry{},
whoamiDomain: "whoami.v4.powerdns.org",
}
}

Expand All @@ -61,9 +75,8 @@ func (svc *DNSWhoamiService) SystemV4(ctx context.Context) ([]DNSWhoamiInfoEntry
if len(svc.systemv4) <= 0 {
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
defer cancel()
netx := &netxlite.Netx{}
reso := netx.NewStdlibResolver(model.DiscardLogger)
addrs, err := reso.LookupHost(ctx, "whoami.v4.powerdns.org")
reso := svc.netx.NewStdlibResolver(svc.logger)
addrs, err := reso.LookupHost(ctx, svc.whoamiDomain)
if err != nil || len(addrs) < 1 {
return nil, false
}
Expand All @@ -81,12 +94,11 @@ func (svc *DNSWhoamiService) UDPv4(ctx context.Context, address string) ([]DNSWh
if len(svc.udpv4[address]) <= 0 {
ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
defer cancel()
netx := &netxlite.Netx{}
dialer := netxlite.NewDialerWithStdlibResolver(model.DiscardLogger)
reso := netx.NewParallelUDPResolver(model.DiscardLogger, dialer, address)
dialer := svc.netx.NewDialerWithResolver(svc.logger, svc.netx.NewStdlibResolver(svc.logger))
reso := svc.netx.NewParallelUDPResolver(svc.logger, dialer, address)
// TODO(bassosimone): this should actually only send an A query. Sending an AAAA
// query is _way_ unnecessary since we know that only A is going to work.
addrs, err := reso.LookupHost(ctx, "whoami.v4.powerdns.org")
addrs, err := reso.LookupHost(ctx, svc.whoamiDomain)
if err != nil || len(addrs) < 1 {
return nil, false
}
Expand Down
97 changes: 97 additions & 0 deletions internal/webconnectivityalgo/dnswhoami_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package webconnectivityalgo

import (
"context"
"testing"

"github.com/apex/log"
"github.com/google/go-cmp/cmp"
"github.com/ooni/probe-cli/v3/internal/netemx"
"github.com/ooni/probe-cli/v3/internal/netxlite"
)

func TestDNSWhoamiService(t *testing.T) {
// expectation describes expectations
type expectation struct {
Entries []DNSWhoamiInfoEntry
Good bool
}

// testcase is a test case defined by this function
type testcase struct {
// name is the test case name
name string

// domain is the domain to query for
domain string

// expectations contains the expecations
expectations []expectation
}

cases := []testcase{{
name: "common case using the default domain",
domain: "", // forces using default
expectations: []expectation{{
Entries: []DNSWhoamiInfoEntry{{
Address: netemx.DefaultClientAddress,
}},
Good: true,
}, {
Entries: []DNSWhoamiInfoEntry{{
Address: netemx.DefaultClientAddress,
}},
Good: true,
}},
}, {
name: "error case using another domain",
domain: "example.xyz",
expectations: []expectation{{
Entries: nil,
Good: false,
}, {
Entries: nil,
Good: false,
}},
}}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
// create testing scenario
env := netemx.MustNewScenario(netemx.InternetScenario)
defer env.Close()

// create the service
svc := NewDNSWhoamiService(log.Log)

// override fields
svc.netx = &netxlite.Netx{Underlying: &netxlite.NetemUnderlyingNetworkAdapter{UNet: env.ClientStack}}
if tc.domain != "" {
svc.whoamiDomain = tc.domain
}

// prepare collecting results
var results []expectation

// run with the system resolver
sysEntries, sysGood := svc.SystemV4(context.Background())
results = append(results, expectation{
Entries: sysEntries,
Good: sysGood,
})

// run with an UDP resolver
udpEntries, udpGood := svc.UDPv4(context.Background(), "8.8.8.8:53")
results = append(results, expectation{
Entries: udpEntries,
Good: udpGood,
})

// check whether we've got what we expected
if diff := cmp.Diff(tc.expectations, results); diff != "" {
t.Fatal(diff)
}
})
}

}

0 comments on commit 9c6cc44

Please sign in to comment.