Skip to content

Commit

Permalink
feat: start merging filtering into testingx (ooni#1234)
Browse files Browse the repository at this point in the history
## Checklist

- [x] I have read the [contribution
guidelines](https://github.com/ooni/probe-cli/blob/master/CONTRIBUTING.md)
- [x] reference issue for this pull request:
ooni/probe#1803
- [x] if you changed anything related to how experiments work and you
need to reflect these changes in the ooni/spec repository, please link
to the related ooni/spec pull request: N/A
- [x] if you changed code inside an experiment, make sure you bump its
version number: N/A

## Description

To continue rewriting Jafar based tests to use netemx, the next step is
to adapt the proxies used by Jafar to be usable from within netemx to
implement equivalent test cases.

In turn, netemx is using testingx for general-purpose test servers that
could also be interesting for other packages.

We also have the netxlite/filtering package, which is ~fine but has a
string-based API, where an interface-based API would be more proper and
easier to compose. (We historically use a string-based API there because
we previously attempted to replace Jafar with code in userspace usinf
netxlite/filtering.)

The first step in this quest is therefore to rewrite the DNS code inside
netxlite/filtering and move it to testingx.

While there rename netemx.UDPResolverFactory to DNSOverUDPServerFactory
for consistency.
  • Loading branch information
bassosimone authored and Murphy-OrangeMud committed Feb 13, 2024
1 parent 44b093a commit d62ef83
Show file tree
Hide file tree
Showing 21 changed files with 1,055 additions and 649 deletions.
4 changes: 2 additions & 2 deletions internal/experiment/dnsping/dnsping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func TestMeasurer_run(t *testing.T) {

t.Run("with netem: without DPI: expect success", func(t *testing.T) {
// create a new test environment
env := netemx.MustNewQAEnv(netemx.QAEnvOptionNetStack("8.8.8.8", &netemx.UDPResolverFactory{}))
env := netemx.MustNewQAEnv(netemx.QAEnvOptionNetStack("8.8.8.8", &netemx.DNSOverUDPServerFactory{}))
defer env.Close()

// we use the same configuration for all resolvers
Expand Down Expand Up @@ -146,7 +146,7 @@ func TestMeasurer_run(t *testing.T) {

t.Run("with netem: with DNS spoofing: expect to see delayed responses", func(t *testing.T) {
// create a new test environment
env := netemx.MustNewQAEnv(netemx.QAEnvOptionNetStack("8.8.8.8", &netemx.UDPResolverFactory{}))
env := netemx.MustNewQAEnv(netemx.QAEnvOptionNetStack("8.8.8.8", &netemx.DNSOverUDPServerFactory{}))
defer env.Close()

// we use the same configuration for all resolvers
Expand Down
4 changes: 3 additions & 1 deletion internal/netemx/dnsoverhttps.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ var _ HTTPHandlerFactory = &DNSOverHTTPSHandlerFactory{}

// NewHandler implements QAEnvHTTPHandlerFactory.
func (f *DNSOverHTTPSHandlerFactory) NewHandler(env NetStackServerFactoryEnv, stack *netem.UNetStack) http.Handler {
return &testingx.DNSOverHTTPSHandler{Config: env.OtherResolversConfig()}
return &testingx.DNSOverHTTPSHandler{
RoundTripper: testingx.NewDNSRoundTripperWithDNSConfig(env.OtherResolversConfig()),
}
}
32 changes: 16 additions & 16 deletions internal/netemx/udpresolver.go → internal/netemx/dnsoverudp.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,37 @@ import (
"github.com/ooni/probe-cli/v3/internal/runtimex"
)

// UDPResolverFactory implements [NetStackServerFactory] for DNS-over-UDP servers.
// DNSOverUDPServerFactory implements [NetStackServerFactory] for DNS-over-UDP servers.
//
// When this factory constructs a [NetStackServer], it will use:
//
// 1. the [NetStackServerFactoryEnv.OtherResolversConfig] as DNS configuration;
//
// 2. the [NetStackServerFactoryEnv.Logger] as the logger.
// 2. the [NetStackServerFactoryEnv.Logger] as logger.
//
// Use this factory along with [QAEnvOptionNetStack] to create DNS-over-UDP servers.
type UDPResolverFactory struct{}
type DNSOverUDPServerFactory struct{}

var _ NetStackServerFactory = &UDPResolverFactory{}
var _ NetStackServerFactory = &DNSOverUDPServerFactory{}

// MustNewServer implements NetStackServerFactory.
func (f *UDPResolverFactory) MustNewServer(env NetStackServerFactoryEnv, stack *netem.UNetStack) NetStackServer {
return udpResolverMustNewServer(env.OtherResolversConfig(), env.Logger(), stack)
func (f *DNSOverUDPServerFactory) MustNewServer(env NetStackServerFactoryEnv, stack *netem.UNetStack) NetStackServer {
return dnsOverUDPResolverMustNewServer(env.OtherResolversConfig(), env.Logger(), stack)
}

type udpResolverFactoryForGetaddrinfo struct{}
type dnsOverUDPServerFactoryForGetaddrinfo struct{}

var _ NetStackServerFactory = &udpResolverFactoryForGetaddrinfo{}
var _ NetStackServerFactory = &dnsOverUDPServerFactoryForGetaddrinfo{}

// MustNewServer implements NetStackServerFactory.
func (f *udpResolverFactoryForGetaddrinfo) MustNewServer(env NetStackServerFactoryEnv, stack *netem.UNetStack) NetStackServer {
return udpResolverMustNewServer(env.ISPResolverConfig(), env.Logger(), stack)
func (f *dnsOverUDPServerFactoryForGetaddrinfo) MustNewServer(env NetStackServerFactoryEnv, stack *netem.UNetStack) NetStackServer {
return dnsOverUDPResolverMustNewServer(env.ISPResolverConfig(), env.Logger(), stack)
}

// udpResolverMustNewServer is an internal factory for creating a [NetStackServer] that
// dnsOverUDPResolverMustNewServer is an internal factory for creating a [NetStackServer] that
// runs a DNS-over-UDP server using the configured logger, DNS config, and stack.
func udpResolverMustNewServer(config *netem.DNSConfig, logger model.Logger, stack *netem.UNetStack) NetStackServer {
return &udpResolver{
func dnsOverUDPResolverMustNewServer(config *netem.DNSConfig, logger model.Logger, stack *netem.UNetStack) NetStackServer {
return &dnsOverUDPResolver{
closers: []io.Closer{},
config: config,
logger: logger,
Expand All @@ -50,7 +50,7 @@ func udpResolverMustNewServer(config *netem.DNSConfig, logger model.Logger, stac
}
}

type udpResolver struct {
type dnsOverUDPResolver struct {
closers []io.Closer
config *netem.DNSConfig
logger model.Logger
Expand All @@ -59,7 +59,7 @@ type udpResolver struct {
}

// Close implements NetStackServer.
func (srv *udpResolver) Close() error {
func (srv *dnsOverUDPResolver) Close() error {
// make the method locked as requested by the documentation
defer srv.mu.Unlock()
srv.mu.Lock()
Expand All @@ -75,7 +75,7 @@ func (srv *udpResolver) Close() error {
}

// MustStart implements NetStackServer.
func (srv *udpResolver) MustStart() {
func (srv *dnsOverUDPResolver) MustStart() {
// make the method locked as requested by the documentation
defer srv.mu.Unlock()
srv.mu.Lock()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
"github.com/ooni/probe-cli/v3/internal/netxlite"
)

func TestUDPResolverFactory(t *testing.T) {
func TestDNSOverUDPServerFactory(t *testing.T) {
env := MustNewQAEnv(
QAEnvOptionNetStack(AddressDNSGoogle8844, &UDPResolverFactory{}),
QAEnvOptionNetStack(AddressDNSGoogle8844, &DNSOverUDPServerFactory{}),
)
defer env.Close()

Expand Down
4 changes: 2 additions & 2 deletions internal/netemx/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import (
// to use this QA environment in all the examples for this package.
func exampleNewEnvironment() *netemx.QAEnv {
return netemx.MustNewQAEnv(
netemx.QAEnvOptionNetStack("8.8.4.4", &netemx.UDPResolverFactory{}),
netemx.QAEnvOptionNetStack("9.9.9.9", &netemx.UDPResolverFactory{}),
netemx.QAEnvOptionNetStack("8.8.4.4", &netemx.DNSOverUDPServerFactory{}),
netemx.QAEnvOptionNetStack("9.9.9.9", &netemx.DNSOverUDPServerFactory{}),
netemx.QAEnvOptionClientAddress(netemx.DefaultClientAddress),
netemx.QAEnvOptionHTTPServer(
netemx.AddressWwwExampleCom, netemx.ExampleWebPageHandlerFactory()),
Expand Down
4 changes: 2 additions & 2 deletions internal/netemx/qaenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,10 @@ func MustNewQAEnv(options ...QAEnvOption) *QAEnv {
}

// make sure we're going to create the ISP's DNS resolver.
qaEnvOptionNetStack(config.ispResolver, &udpResolverFactoryForGetaddrinfo{})(config)
qaEnvOptionNetStack(config.ispResolver, &dnsOverUDPServerFactoryForGetaddrinfo{})(config)

// make sure we're going to create the root DNS resolver.
qaEnvOptionNetStack(config.rootResolver, &UDPResolverFactory{})(config)
qaEnvOptionNetStack(config.rootResolver, &DNSOverUDPServerFactory{})(config)

// use a prefix logger for the QA env
prefixLogger := &logx.PrefixLogger{
Expand Down
2 changes: 1 addition & 1 deletion internal/netemx/scenario.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func MustNewScenario(config []*ScenarioDomainAddresses) *QAEnv {
for _, addr := range sad.Addresses {
opts = append(opts, QAEnvOptionNetStack(
addr,
&UDPResolverFactory{},
&DNSOverUDPServerFactory{},
&HTTPSecureServerFactory{
Factory: &DNSOverHTTPSHandlerFactory{},
Ports: []int{443},
Expand Down
84 changes: 35 additions & 49 deletions internal/netxlite/dnsoverudp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
"github.com/apex/log"
"github.com/google/go-cmp/cmp"
"github.com/miekg/dns"
"github.com/ooni/netem"
"github.com/ooni/probe-cli/v3/internal/mocks"
"github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/netxlite/filtering"
"github.com/ooni/probe-cli/v3/internal/testingx"
)

Expand Down Expand Up @@ -252,18 +252,14 @@ func TestDNSOverUDPTransport(t *testing.T) {
})

t.Run("using a real server", func(t *testing.T) {
srvr := &filtering.DNSServer{
OnQuery: func(domain string) filtering.DNSAction {
return filtering.DNSActionCache
},
Cache: map[string][]string{
"dns.google.": {"8.8.8.8"},
},
}
listener, err := srvr.Start("127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
udpAddr := &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 0,
}
dnsConfig := netem.NewDNSConfig()
dnsConfig.AddRecord("dns.google", "", "8.8.8.8")
dnsRtx := testingx.NewDNSRoundTripperWithDNSConfig(dnsConfig)
listener := testingx.MustNewDNSOverUDPListener(udpAddr, &testingx.DNSOverUDPStdlibListener{}, dnsRtx)
defer listener.Close()
dialer := NewDialerWithoutResolver(model.DiscardLogger)
txp := NewUnwrappedDNSOverUDPTransport(dialer, listener.LocalAddr().String())
Expand All @@ -284,20 +280,20 @@ func TestDNSOverUDPTransport(t *testing.T) {
})

t.Run("recording delayed DNS responses", func(t *testing.T) {
dnsConfigGood := netem.NewDNSConfig()
dnsConfigGood.AddRecord("dns.google", "", "8.8.8.8")

dnsConfigBogus := netem.NewDNSConfig()
dnsConfigBogus.AddRecord("dns.google", "", "127.0.0.1")

t.Run("without any context-injected traces", func(t *testing.T) {
srvr := &filtering.DNSServer{
OnQuery: func(domain string) filtering.DNSAction {
return filtering.DNSActionLocalHostPlusCache
},
Cache: map[string][]string{
"dns.google.": {"8.8.8.8"},
},
}
listener, err := srvr.Start("127.0.0.1:0")
if err != nil {
t.Fatal(err)
udpAddr := &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 0,
}
defer listener.Close()
listener := testingx.MustNewDNSSimulateGWFListener(
udpAddr, &testingx.DNSOverUDPStdlibListener{}, dnsConfigBogus,
dnsConfigGood, testingx.DNSNumBogusResponses(1))
dialer := NewDialerWithoutResolver(model.DiscardLogger)
expectedAddress := listener.LocalAddr().String()
txp := NewUnwrappedDNSOverUDPTransport(dialer, expectedAddress)
Expand Down Expand Up @@ -325,18 +321,13 @@ func TestDNSOverUDPTransport(t *testing.T) {
goodLookupAddrs bool
goodError bool
)
srvr := &filtering.DNSServer{
OnQuery: func(domain string) filtering.DNSAction {
return filtering.DNSActionLocalHostPlusCache
},
Cache: map[string][]string{
"dns.google.": {"8.8.8.8"},
},
}
listener, err := srvr.Start("127.0.0.1:0")
if err != nil {
t.Fatal(err)
udpAddr := &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 0,
}
listener := testingx.MustNewDNSSimulateGWFListener(
udpAddr, &testingx.DNSOverUDPStdlibListener{}, dnsConfigBogus,
dnsConfigGood, testingx.DNSNumBogusResponses(1))
defer listener.Close()
dialer := NewDialerWithoutResolver(model.DiscardLogger)
expectedAddress := listener.LocalAddr().String()
Expand Down Expand Up @@ -420,19 +411,14 @@ func TestDNSOverUDPTransport(t *testing.T) {
goodLookupAddrs bool
goodError bool
)
srvr := &filtering.DNSServer{
OnQuery: func(domain string) filtering.DNSAction {
return filtering.DNSActionLocalHostPlusCache
},
Cache: map[string][]string{
// Note: the cache here is nonexistent so we should
// get a "no such host" error from the server.
},
}
listener, err := srvr.Start("127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
udpAddr := &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 0,
}
// Note: the config here is empty so we should get a "no such host" error from the server.
listener := testingx.MustNewDNSSimulateGWFListener(
udpAddr, &testingx.DNSOverUDPStdlibListener{}, dnsConfigBogus,
netem.NewDNSConfig(), testingx.DNSNumBogusResponses(1))
defer listener.Close()
dialer := NewDialerWithoutResolver(model.DiscardLogger)
expectedAddress := listener.LocalAddr().String()
Expand Down
Loading

0 comments on commit d62ef83

Please sign in to comment.