Skip to content

Commit

Permalink
refactor(netxlite): introduce the getaddrinfo transport (#775)
Browse files Browse the repository at this point in the history
This diff modifies the system resolver to use a getaddrinf transport.

Obviously the transport is a fake, but its existence will allow us
to observe DNS events more naturally.

A lookup using the system resolver would be a ANY lookup that will
contain all the resolved IP addresses into the same response.

This change was also part of websteps-illustrated, albeit the way in
which I did it there was less clean than what we have here.

Ref issue: ooni/probe#2096
  • Loading branch information
bassosimone authored Jun 1, 2022
1 parent 7e0b473 commit 923d81c
Show file tree
Hide file tree
Showing 14 changed files with 432 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestWorkingAsIntended(t *testing.T) {
Client: http.DefaultClient,
Dialer: netxlite.DefaultDialer,
MaxAcceptableBody: 1 << 24,
Resolver: &netxlite.ResolverSystem{},
Resolver: netxlite.NewResolverSystem(),
}
srv := httptest.NewServer(handler)
defer srv.Close()
Expand Down
4 changes: 2 additions & 2 deletions internal/engine/netx/netx.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ var defaultCertPool *x509.CertPool = netxlite.NewDefaultCertPool()
// NewResolver creates a new resolver from the specified config
func NewResolver(config Config) model.Resolver {
if config.BaseResolver == nil {
config.BaseResolver = &netxlite.ResolverSystem{}
config.BaseResolver = netxlite.NewResolverSystem()
}
var r model.Resolver = config.BaseResolver
r = &netxlite.AddressResolver{
Expand Down Expand Up @@ -260,7 +260,7 @@ func NewDNSClientWithOverrides(config Config, URL, hostOverride, SNIOverride,
}
switch resolverURL.Scheme {
case "system":
return &netxlite.ResolverSystem{}, nil
return netxlite.NewResolverSystem(), nil
case "https":
config.TLSConfig.NextProtos = []string{"h2", "http/1.1"}
httpClient := &http.Client{Transport: NewHTTPTransport(config)}
Expand Down
16 changes: 8 additions & 8 deletions internal/engine/netx/netx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestNewResolverVanilla(t *testing.T) {
if !ok {
t.Fatal("not the resolver we expected")
}
_, ok = ar.Resolver.(*netxlite.ResolverSystem)
_, ok = ar.Resolver.(*netxlite.ResolverSystemDoNotInstantiate)
if !ok {
t.Fatal("not the resolver we expected")
}
Expand Down Expand Up @@ -82,7 +82,7 @@ func TestNewResolverWithBogonFilter(t *testing.T) {
if !ok {
t.Fatal("not the resolver we expected")
}
_, ok = ar.Resolver.(*netxlite.ResolverSystem)
_, ok = ar.Resolver.(*netxlite.ResolverSystemDoNotInstantiate)
if !ok {
t.Fatal("not the resolver we expected")
}
Expand Down Expand Up @@ -111,7 +111,7 @@ func TestNewResolverWithLogging(t *testing.T) {
if !ok {
t.Fatal("not the resolver we expected")
}
_, ok = ar.Resolver.(*netxlite.ResolverSystem)
_, ok = ar.Resolver.(*netxlite.ResolverSystemDoNotInstantiate)
if !ok {
t.Fatal("not the resolver we expected")
}
Expand Down Expand Up @@ -141,7 +141,7 @@ func TestNewResolverWithSaver(t *testing.T) {
if !ok {
t.Fatal("not the resolver we expected")
}
_, ok = ar.Resolver.(*netxlite.ResolverSystem)
_, ok = ar.Resolver.(*netxlite.ResolverSystemDoNotInstantiate)
if !ok {
t.Fatal("not the resolver we expected")
}
Expand Down Expand Up @@ -170,7 +170,7 @@ func TestNewResolverWithReadWriteCache(t *testing.T) {
if !ok {
t.Fatal("not the resolver we expected")
}
_, ok = ar.Resolver.(*netxlite.ResolverSystem)
_, ok = ar.Resolver.(*netxlite.ResolverSystemDoNotInstantiate)
if !ok {
t.Fatal("not the resolver we expected")
}
Expand Down Expand Up @@ -204,7 +204,7 @@ func TestNewResolverWithPrefilledReadonlyCache(t *testing.T) {
if !ok {
t.Fatal("not the resolver we expected")
}
_, ok = ar.Resolver.(*netxlite.ResolverSystem)
_, ok = ar.Resolver.(*netxlite.ResolverSystemDoNotInstantiate)
if !ok {
t.Fatal("not the resolver we expected")
}
Expand Down Expand Up @@ -556,7 +556,7 @@ func TestNewDNSClientSystemResolver(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if _, ok := dnsclient.(*netxlite.ResolverSystem); !ok {
if _, ok := dnsclient.(*netxlite.ResolverSystemDoNotInstantiate); !ok {
t.Fatal("not the resolver we expected")
}
dnsclient.CloseIdleConnections()
Expand All @@ -568,7 +568,7 @@ func TestNewDNSClientEmpty(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if _, ok := dnsclient.(*netxlite.ResolverSystem); !ok {
if _, ok := dnsclient.(*netxlite.ResolverSystemDoNotInstantiate); !ok {
t.Fatal("not the resolver we expected")
}
dnsclient.CloseIdleConnections()
Expand Down
2 changes: 1 addition & 1 deletion internal/engine/netx/resolver/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func testresolverquickidna(t *testing.T, reso model.Resolver) {
}

func TestNewResolverSystem(t *testing.T) {
reso := &netxlite.ResolverSystem{}
reso := netxlite.NewResolverSystem()
testresolverquick(t, reso)
testresolverquickidna(t, reso)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/netxlite/dialer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func TestDialerResolver(t *testing.T) {
t.Run("fails without a port", func(t *testing.T) {
d := &dialerResolver{
Dialer: &DialerSystem{},
Resolver: &resolverSystem{},
Resolver: newResolverSystem(),
}
const missingPort = "ooni.nu"
conn, err := d.DialContext(context.Background(), "tcp", missingPort)
Expand Down
125 changes: 125 additions & 0 deletions internal/netxlite/dnsovergetaddrinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package netxlite

//
// DNS over getaddrinfo: fake transport to allow us to observe
// lookups using getaddrinfo as a DNSTransport.
//

import (
"context"
"net"
"time"

"github.com/miekg/dns"
"github.com/ooni/probe-cli/v3/internal/model"
)

// dnsOverGetaddrinfoTransport is a DNSTransport using getaddrinfo.
type dnsOverGetaddrinfoTransport struct {
testableTimeout time.Duration
testableLookupHost func(ctx context.Context, domain string) ([]string, error)
}

var _ model.DNSTransport = &dnsOverGetaddrinfoTransport{}

func (txp *dnsOverGetaddrinfoTransport) RoundTrip(
ctx context.Context, query model.DNSQuery) (model.DNSResponse, error) {
if query.Type() != dns.TypeANY {
return nil, ErrNoDNSTransport
}
addrs, err := txp.lookup(ctx, query.Domain())
if err != nil {
return nil, err
}
resp := &dnsOverGetaddrinfoResponse{
addrs: addrs,
query: query,
}
return resp, nil
}

type dnsOverGetaddrinfoResponse struct {
addrs []string
query model.DNSQuery
}

func (txp *dnsOverGetaddrinfoTransport) lookup(
ctx context.Context, hostname string) ([]string, error) {
// This code forces adding a shorter timeout to the domain name
// resolutions when using the system resolver. We have seen cases
// in which such a timeout becomes too large. One such case is
// described in https://github.com/ooni/probe/issues/1726.
addrsch, errch := make(chan []string, 1), make(chan error, 1)
ctx, cancel := context.WithTimeout(ctx, txp.timeout())
defer cancel()
go func() {
addrs, err := txp.lookupfn()(ctx, hostname)
if err != nil {
errch <- err
return
}
addrsch <- addrs
}()
select {
case <-ctx.Done():
return nil, ctx.Err()
case addrs := <-addrsch:
return addrs, nil
case err := <-errch:
return nil, err
}
}

func (txp *dnsOverGetaddrinfoTransport) timeout() time.Duration {
if txp.testableTimeout > 0 {
return txp.testableTimeout
}
return 15 * time.Second
}

func (txp *dnsOverGetaddrinfoTransport) lookupfn() func(ctx context.Context, domain string) ([]string, error) {
if txp.testableLookupHost != nil {
return txp.testableLookupHost
}
return TProxy.DefaultResolver().LookupHost
}

func (txp *dnsOverGetaddrinfoTransport) RequiresPadding() bool {
return false
}

func (txp *dnsOverGetaddrinfoTransport) Network() string {
return TProxy.DefaultResolver().Network()
}

func (txp *dnsOverGetaddrinfoTransport) Address() string {
return ""
}

func (txp *dnsOverGetaddrinfoTransport) CloseIdleConnections() {
// nothing
}

func (r *dnsOverGetaddrinfoResponse) Query() model.DNSQuery {
return r.query
}

func (r *dnsOverGetaddrinfoResponse) Bytes() []byte {
return nil
}

func (r *dnsOverGetaddrinfoResponse) Rcode() int {
return 0
}

func (r *dnsOverGetaddrinfoResponse) DecodeHTTPS() (*model.HTTPSSvc, error) {
return nil, ErrNoDNSTransport
}

func (r *dnsOverGetaddrinfoResponse) DecodeLookupHost() ([]string, error) {
return r.addrs, nil
}

func (r *dnsOverGetaddrinfoResponse) DecodeNS() ([]*net.NS, error) {
return nil, ErrNoDNSTransport
}
Loading

0 comments on commit 923d81c

Please sign in to comment.