Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(webconnectivityalgo): test DNSWhoamiService #1498

Merged
merged 1 commit into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
})
}

}
Loading