From 8a0c062844490efbb88118ed334a93e9ae46d5bf Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Sat, 27 Aug 2022 15:47:48 +0200 Subject: [PATCH] feat: clearly indicate which resolver we're using (#885) See what we documented at https://github.com/ooni/spec/pull/257 Reference issue: https://github.com/ooni/probe/issues/2238 See also the related ooni/spec PR: https://github.com/ooni/spec/pull/257 See also https://github.com/ooni/probe/issues/2237 While there, bump webconnectivity@v0.5 version because this change has an impact onto the generated data format. The drop in coverage is unavoidable because we've written some tests for `measurex` to ensure we deal with DNS resolvers and transport names correctly depending on the splitting policy we use. (However, `measurex` is only used for the `tor` experiment and, per the step-by-step design document, new experiments should use `measurexlite` instead, so this is hopefully fine(TM).) While there, fix a broken integration test that does not run in `-short` mode. --- .../engine/experiment_integration_test.go | 13 +- .../experiment/webconnectivity/measurer.go | 2 +- internal/measurex/dnsx.go | 3 +- internal/measurex/dnsx_test.go | 47 +++++ internal/measurex/resolver.go | 5 +- internal/measurex/resolver_test.go | 58 ++++++ internal/measurexlite/dns_test.go | 8 +- internal/netxlite/dnsovergetaddrinfo.go | 5 + internal/netxlite/doc.go | 2 +- internal/netxlite/getaddrinfo.go | 17 ++ internal/netxlite/getaddrinfo_cgo.go | 7 +- internal/netxlite/getaddrinfo_otherwise.go | 11 +- internal/netxlite/resolvercore.go | 6 +- internal/netxlite/resolvercore_test.go | 12 +- internal/tracex/resolver.go | 28 ++- internal/tracex/resolver_test.go | 173 +++++++++++++++--- internal/tutorial/netxlite/README.md | 2 +- .../tutorial/netxlite/chapter05/README.md | 11 +- internal/tutorial/netxlite/chapter05/main.go | 11 +- 19 files changed, 362 insertions(+), 59 deletions(-) create mode 100644 internal/measurex/dnsx_test.go create mode 100644 internal/measurex/resolver_test.go diff --git a/internal/engine/experiment_integration_test.go b/internal/engine/experiment_integration_test.go index 1ba6431..449edc6 100644 --- a/internal/engine/experiment_integration_test.go +++ b/internal/engine/experiment_integration_test.go @@ -29,7 +29,18 @@ func TestCreateAll(t *testing.T) { exp := builder.NewExperiment() good := (exp.Name() == name) if !good { - t.Fatal("unexpected experiment name") + // We have introduced the concept of versioned experiments in + // https://github.com/ooni/probe-cli/pull/882. This works like + // in brew: we append @vX.Y to the experiment name. So, here + // we're stripping the version specification and retry. + index := strings.Index(name, "@") + if index >= 0 { + name = name[:index] + if good := (exp.Name() == name); good { + continue + } + } + t.Fatal("unexpected experiment name", exp.Name(), name) } } } diff --git a/internal/experiment/webconnectivity/measurer.go b/internal/experiment/webconnectivity/measurer.go index fe844ba..2a1795d 100644 --- a/internal/experiment/webconnectivity/measurer.go +++ b/internal/experiment/webconnectivity/measurer.go @@ -36,7 +36,7 @@ func (m *Measurer) ExperimentName() string { // ExperimentVersion implements model.ExperimentMeasurer. func (m *Measurer) ExperimentVersion() string { - return "0.5.1" + return "0.5.2" } // Run implements model.ExperimentMeasurer. diff --git a/internal/measurex/dnsx.go b/internal/measurex/dnsx.go index 26bef6e..e2da8be 100644 --- a/internal/measurex/dnsx.go +++ b/internal/measurex/dnsx.go @@ -11,6 +11,7 @@ import ( "time" "github.com/ooni/probe-cli/v3/internal/model" + "github.com/ooni/probe-cli/v3/internal/tracex" ) // WrapDNSXRoundTripper creates a new DNSXRoundTripper that @@ -42,7 +43,7 @@ func (txp *dnsxRoundTripperDB) RoundTrip( response, err := txp.DNSTransport.RoundTrip(ctx, query) finished := time.Since(txp.begin).Seconds() txp.db.InsertIntoDNSRoundTrip(&DNSRoundTripEvent{ - Network: txp.DNSTransport.Network(), + Network: tracex.ResolverNetworkAdaptNames(txp.DNSTransport.Network()), Address: txp.DNSTransport.Address(), Query: txp.maybeQueryBytes(query), Started: started, diff --git a/internal/measurex/dnsx_test.go b/internal/measurex/dnsx_test.go new file mode 100644 index 0000000..a00dff2 --- /dev/null +++ b/internal/measurex/dnsx_test.go @@ -0,0 +1,47 @@ +package measurex + +import ( + "context" + "testing" + + "github.com/miekg/dns" + "github.com/ooni/probe-cli/v3/internal/model/mocks" + "github.com/ooni/probe-cli/v3/internal/netxlite" +) + +func TestDNSXModifiesStdlibTransportName(t *testing.T) { + // See https://github.com/ooni/spec/pull/257 for more information. + child := netxlite.NewDNSOverGetaddrinfoTransport() + mx := NewMeasurerWithDefaultSettings() + dbout := &MeasurementDB{} + txp := mx.WrapDNSXRoundTripper(dbout, child) + ctx, cancel := context.WithCancel(context.Background()) + cancel() // we want to fail immediately + query := &mocks.DNSQuery{ + MockDomain: func() string { + return "dns.google" + }, + MockType: func() uint16 { + return dns.TypeANY + }, + MockBytes: func() ([]byte, error) { + return []byte{}, nil + }, + MockID: func() uint16 { + return 1453 + }, + } + _, _ = txp.RoundTrip(ctx, query) + measurement := dbout.AsMeasurement() + var good int + for _, rtinfo := range measurement.DNSRoundTrip { + network := rtinfo.Network + if network != netxlite.StdlibResolverSystem { + t.Fatal("unexpected network", network) + } + good++ + } + if good < 1 { + t.Fatal("no good entry seen") + } +} diff --git a/internal/measurex/resolver.go b/internal/measurex/resolver.go index bb508fa..4df8f09 100644 --- a/internal/measurex/resolver.go +++ b/internal/measurex/resolver.go @@ -13,6 +13,7 @@ import ( "github.com/ooni/probe-cli/v3/internal/model" "github.com/ooni/probe-cli/v3/internal/netxlite" + "github.com/ooni/probe-cli/v3/internal/tracex" ) // WrapResolver creates a new Resolver that saves events into the WritableDB. @@ -105,7 +106,7 @@ func (r *resolverDB) LookupHost(ctx context.Context, domain string) ([]string, e func (r *resolverDB) saveLookupResults(domain string, started, finished float64, err error, addrs []string, qtype string) { ev := &DNSLookupEvent{ - Network: r.Resolver.Network(), + Network: tracex.ResolverNetworkAdaptNames(r.Resolver.Network()), Address: r.Resolver.Address(), Failure: NewFailure(err), Domain: domain, @@ -158,7 +159,7 @@ func (r *resolverDB) LookupHTTPS(ctx context.Context, domain string) (*model.HTT https, err := r.Resolver.LookupHTTPS(ctx, domain) finished := time.Since(r.begin).Seconds() ev := &DNSLookupEvent{ - Network: r.Resolver.Network(), + Network: tracex.ResolverNetworkAdaptNames(r.Resolver.Network()), Address: r.Resolver.Address(), Domain: domain, QueryType: "HTTPS", diff --git a/internal/measurex/resolver_test.go b/internal/measurex/resolver_test.go new file mode 100644 index 0000000..e067e01 --- /dev/null +++ b/internal/measurex/resolver_test.go @@ -0,0 +1,58 @@ +package measurex + +import ( + "context" + "testing" + + "github.com/ooni/probe-cli/v3/internal/model" + "github.com/ooni/probe-cli/v3/internal/netxlite" +) + +func TestResolverModifiesStdlibResolverName(t *testing.T) { + // See https://github.com/ooni/spec/pull/257 for more information. + + t.Run("for LookupHost", func(t *testing.T) { + child := netxlite.NewStdlibResolver(model.DiscardLogger) + mx := NewMeasurerWithDefaultSettings() + dbout := &MeasurementDB{} + txp := mx.WrapResolver(dbout, child) + ctx, cancel := context.WithCancel(context.Background()) + cancel() // we want to fail immediately + _, _ = txp.LookupHost(ctx, "dns.google") + measurement := dbout.AsMeasurement() + var good int + for _, rtinfo := range measurement.LookupHost { + network := rtinfo.Network + if network != netxlite.StdlibResolverSystem { + t.Fatal("unexpected network", network) + } + good++ + } + if good < 1 { + t.Fatal("no good entry seen") + } + }) + + t.Run("for LookupHTTPS", func(t *testing.T) { + child := netxlite.NewStdlibResolver(model.DiscardLogger) + mx := NewMeasurerWithDefaultSettings() + dbout := &MeasurementDB{} + txp := mx.WrapResolver(dbout, child) + ctx, cancel := context.WithCancel(context.Background()) + cancel() // we want to fail immediately + _, _ = txp.LookupHTTPS(ctx, "dns.google") + measurement := dbout.AsMeasurement() + var good int + for _, rtinfo := range measurement.LookupHTTPSSvc { + network := rtinfo.Network + if network != netxlite.StdlibResolverSystem { + t.Fatal("unexpected network", network) + } + good++ + } + if good < 1 { + t.Fatal("no good entry seen") + } + }) + +} diff --git a/internal/measurexlite/dns_test.go b/internal/measurexlite/dns_test.go index c1a4777..f6e5e22 100644 --- a/internal/measurexlite/dns_test.go +++ b/internal/measurexlite/dns_test.go @@ -301,8 +301,12 @@ func TestNewWrappedResolvers(t *testing.T) { if resolvert.tx != trace { t.Fatal("invalid trace") } - if resolver.Network() != "system" { - t.Fatal("unexpected resolver network") + switch network := resolver.Network(); network { + case netxlite.StdlibResolverGetaddrinfo, + netxlite.StdlibResolverGolangNetResolver: + // ok + default: + t.Fatal("unexpected resolver network", network) } }) } diff --git a/internal/netxlite/dnsovergetaddrinfo.go b/internal/netxlite/dnsovergetaddrinfo.go index d55125c..8fd5bc6 100644 --- a/internal/netxlite/dnsovergetaddrinfo.go +++ b/internal/netxlite/dnsovergetaddrinfo.go @@ -24,6 +24,11 @@ type dnsOverGetaddrinfoTransport struct { testableLookupANY func(ctx context.Context, domain string) ([]string, string, error) } +// NewDNSOverGetaddrinfoTransport creates a new dns-over-getaddrinfo transport. +func NewDNSOverGetaddrinfoTransport() model.DNSTransport { + return &dnsOverGetaddrinfoTransport{} +} + var _ model.DNSTransport = &dnsOverGetaddrinfoTransport{} func (txp *dnsOverGetaddrinfoTransport) RoundTrip( diff --git a/internal/netxlite/doc.go b/internal/netxlite/doc.go index 678e06b..9bb2774 100644 --- a/internal/netxlite/doc.go +++ b/internal/netxlite/doc.go @@ -47,7 +47,7 @@ // // 1. establishing a TCP connection; // -// 2. performing a domain name resolution with the "system" resolver +// 2. performing a domain name resolution with the "stdlib" resolver // (i.e., getaddrinfo on Unix) or custom DNS transports (e.g., DoT, DoH); // // 3. performing the TLS handshake; diff --git a/internal/netxlite/getaddrinfo.go b/internal/netxlite/getaddrinfo.go index ad775bb..a69b542 100644 --- a/internal/netxlite/getaddrinfo.go +++ b/internal/netxlite/getaddrinfo.go @@ -2,6 +2,23 @@ package netxlite import "errors" +// Name of the resolver we use when we link with libc and use getaddrinfo directly. +// +// See https://github.com/ooni/spec/pull/257 for more info. +const StdlibResolverGetaddrinfo = "getaddrinfo" + +// Name of the resolver we use when we don't link with libc and use net.Resolver. +// +// See https://github.com/ooni/spec/pull/257 for more info. +const StdlibResolverGolangNetResolver = "golang_net_resolver" + +// Legacy name of the resolver we use when we're don't know whether we're using +// getaddrinfo, but we're using net.Resolver, and we're splitting the answer +// in two A and AAAA queries. Eventually will become deprecated. +// +// See https://github.com/ooni/spec/pull/257 for more info. +const StdlibResolverSystem = "system" + // ErrGetaddrinfo represents a getaddrinfo failure. type ErrGetaddrinfo struct { // Err is the error proper. diff --git a/internal/netxlite/getaddrinfo_cgo.go b/internal/netxlite/getaddrinfo_cgo.go index 91b5c0f..251c3d5 100644 --- a/internal/netxlite/getaddrinfo_cgo.go +++ b/internal/netxlite/getaddrinfo_cgo.go @@ -28,10 +28,13 @@ import ( // been used to implement the getaddrinfo resolver. // // This is the CGO_ENABLED=1 implementation of this function, which -// always returns the string "system", because in this scenario +// always returns the string [StdlibResolverGetaddrinfo], because in this scenario // we are actually calling the getaddrinfo libc function. +// +// See https://github.com/ooni/spec/pull/257 for more information on how +// we evolved our naming of the "stdlib" resolver over time. func getaddrinfoResolverNetwork() string { - return "system" + return StdlibResolverGetaddrinfo } // getaddrinfoLookupANY attempts to perform an ANY lookup using getaddrinfo. diff --git a/internal/netxlite/getaddrinfo_otherwise.go b/internal/netxlite/getaddrinfo_otherwise.go index d9b8747..c348ab9 100644 --- a/internal/netxlite/getaddrinfo_otherwise.go +++ b/internal/netxlite/getaddrinfo_otherwise.go @@ -11,13 +11,16 @@ import ( // been used to implement the getaddrinfo resolver. // // This is the CGO_ENABLED=0 implementation of this function, which -// always returns the string "go", because in this scenario we are actually -// using whatever resolver is used under the hood by the stdlib. +// always returns the string [StdlibResolverGolangNetResolver], because in this scenario +// we are actually using whatever resolver is used under the hood by the stdlib. // // See https://github.com/ooni/probe/issues/2029#issuecomment-1140805266 -// for an explanation of why calling this resolver "netgo" is wrong. +// for an explanation of why calling this resolver "netgo" was wrong. +// +// See https://github.com/ooni/spec/pull/257 for additional documentation +// regarding using "golang_net_resolver" instead of "go". func getaddrinfoResolverNetwork() string { - return "go" + return StdlibResolverGolangNetResolver } // getaddrinfoLookupANY attempts to perform an ANY lookup using getaddrinfo. diff --git a/internal/netxlite/resolvercore.go b/internal/netxlite/resolvercore.go index 8b4243e..cb9940c 100644 --- a/internal/netxlite/resolvercore.go +++ b/internal/netxlite/resolvercore.go @@ -20,11 +20,11 @@ import ( // ErrNoDNSTransport is the error returned when you attempt to perform // a DNS operation that requires a custom DNSTransport (e.g., DNSOverHTTPSTransport) -// but you are using the "system" resolver instead. +// but you are using the "stdlib" resolver instead. var ErrNoDNSTransport = errors.New("operation requires a DNS transport") // NewStdlibResolver creates a new Resolver by combining WrapResolver -// with an internal "system" resolver type. The list of optional wrappers +// with an internal "stdlib" resolver type. The list of optional wrappers // allow to wrap the underlying getaddrinfo transport. Any nil wrapper // will be silently ignored by the code that performs the wrapping. func NewStdlibResolver(logger model.DebugLogger, wrappers ...model.DNSTransportWrapper) model.Resolver { @@ -45,7 +45,7 @@ func NewParallelDNSOverHTTPSResolver(logger model.DebugLogger, URL string) model // implies, this function returns an unwrapped resolver. func NewUnwrappedStdlibResolver(wrappers ...model.DNSTransportWrapper) model.Resolver { return &resolverSystem{ - t: WrapDNSTransport(&dnsOverGetaddrinfoTransport{}, wrappers...), + t: WrapDNSTransport(NewDNSOverGetaddrinfoTransport(), wrappers...), } } diff --git a/internal/netxlite/resolvercore_test.go b/internal/netxlite/resolvercore_test.go index 334f97f..9c3713e 100644 --- a/internal/netxlite/resolvercore_test.go +++ b/internal/netxlite/resolvercore_test.go @@ -347,7 +347,7 @@ func TestResolverLogger(t *testing.T) { return expected, nil }, MockNetwork: func() string { - return "system" + return StdlibResolverGetaddrinfo }, MockAddress: func() string { return "" @@ -381,7 +381,7 @@ func TestResolverLogger(t *testing.T) { return nil, expected }, MockNetwork: func() string { - return "system" + return StdlibResolverGetaddrinfo }, MockAddress: func() string { return "" @@ -420,7 +420,7 @@ func TestResolverLogger(t *testing.T) { return expected, nil }, MockNetwork: func() string { - return "system" + return StdlibResolverGetaddrinfo }, MockAddress: func() string { return "" @@ -454,7 +454,7 @@ func TestResolverLogger(t *testing.T) { return nil, expected }, MockNetwork: func() string { - return "system" + return StdlibResolverGetaddrinfo }, MockAddress: func() string { return "" @@ -509,7 +509,7 @@ func TestResolverLogger(t *testing.T) { return expected, nil }, MockNetwork: func() string { - return "system" + return StdlibResolverGetaddrinfo }, MockAddress: func() string { return "" @@ -543,7 +543,7 @@ func TestResolverLogger(t *testing.T) { return nil, expected }, MockNetwork: func() string { - return "system" + return StdlibResolverGetaddrinfo }, MockAddress: func() string { return "" diff --git a/internal/tracex/resolver.go b/internal/tracex/resolver.go index 9cc6af8..363bec8 100644 --- a/internal/tracex/resolver.go +++ b/internal/tracex/resolver.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ooni/probe-cli/v3/internal/model" + "github.com/ooni/probe-cli/v3/internal/netxlite" ) // ResolverSaver is a resolver that saves events. @@ -42,7 +43,7 @@ func (r *ResolverSaver) LookupHost(ctx context.Context, hostname string) ([]stri r.Saver.Write(&EventResolveStart{&EventValue{ Address: r.Resolver.Address(), Hostname: hostname, - Proto: r.Resolver.Network(), + Proto: r.Network(), Time: start, }}) addrs, err := r.Resolver.LookupHost(ctx, hostname) @@ -53,14 +54,29 @@ func (r *ResolverSaver) LookupHost(ctx context.Context, hostname string) ([]stri Duration: stop.Sub(start), Err: NewFailureStr(err), Hostname: hostname, - Proto: r.Resolver.Network(), + Proto: r.Network(), Time: stop, }}) return addrs, err } +// ResolverNetworkAdaptNames makes sure we map the [netxlite.StdlibResolverGolangNetResolver] and +// [netxlite.StdlibResolverGetaddrinfo] resolver names to [netxlite.StdlibResolverSystem]. You MUST +// call this function when your resolver splits the "stdlib" resolver results into two fake AAAA +// and A queries rather than faking a single ANY query. +// +// See https://github.com/ooni/spec/pull/257 for more information. +func ResolverNetworkAdaptNames(input string) string { + switch input { + case netxlite.StdlibResolverGetaddrinfo, netxlite.StdlibResolverGolangNetResolver: + return netxlite.StdlibResolverSystem + default: + return input + } +} + func (r *ResolverSaver) Network() string { - return r.Resolver.Network() + return ResolverNetworkAdaptNames(r.Resolver.Network()) } func (r *ResolverSaver) Address() string { @@ -112,7 +128,7 @@ func (txp *DNSTransportSaver) RoundTrip( txp.Saver.Write(&EventDNSRoundTripStart{&EventValue{ Address: txp.DNSTransport.Address(), DNSQuery: dnsMaybeQueryBytes(query), - Proto: txp.DNSTransport.Network(), + Proto: txp.Network(), Time: start, }}) response, err := txp.DNSTransport.RoundTrip(ctx, query) @@ -123,14 +139,14 @@ func (txp *DNSTransportSaver) RoundTrip( DNSResponse: dnsMaybeResponseBytes(response), Duration: stop.Sub(start), Err: NewFailureStr(err), - Proto: txp.DNSTransport.Network(), + Proto: txp.Network(), Time: stop, }}) return response, err } func (txp *DNSTransportSaver) Network() string { - return txp.DNSTransport.Network() + return ResolverNetworkAdaptNames(txp.DNSTransport.Network()) } func (txp *DNSTransportSaver) Address() string { diff --git a/internal/tracex/resolver_test.go b/internal/tracex/resolver_test.go index 58b6295..fab0dfc 100644 --- a/internal/tracex/resolver_test.go +++ b/internal/tracex/resolver_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "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" @@ -75,7 +76,7 @@ func TestResolverSaver(t *testing.T) { reso := saver.WrapResolver(newFakeResolverWithResult(expected)) addrs, err := reso.LookupHost(context.Background(), "www.google.com") if err != nil { - t.Fatal("expected nil error here") + t.Fatal(err) } if !reflect.DeepEqual(addrs, expected) { t.Fatal("not the result we expected") @@ -112,19 +113,56 @@ func TestResolverSaver(t *testing.T) { t.Fatal("the saved time is wrong") } }) + + t.Run("with stdlib resolver there's correct .Network remapping", func(t *testing.T) { + saver := &Saver{} + reso := saver.WrapResolver(netxlite.NewStdlibResolver(model.DiscardLogger)) + ctx, cancel := context.WithCancel(context.Background()) + cancel() // immediately fail the operation + _, _ = reso.LookupHost(ctx, "www.google.com") + // basically, we just want to ensure that the engine name is converted + ev := saver.Read() + if len(ev) != 2 { + t.Fatal("expected number of events") + } + if ev[0].Value().Proto != netxlite.StdlibResolverSystem { + t.Fatal("unexpected Proto") + } + if ev[1].Value().Proto != netxlite.StdlibResolverSystem { + t.Fatal("unexpected Proto") + } + }) }) t.Run("Network", func(t *testing.T) { - saver := &Saver{} - child := &mocks.Resolver{ - MockNetwork: func() string { - return "x" - }, - } - reso := saver.WrapResolver(child) - if reso.Network() != "x" { - t.Fatal("unexpected result") - } + t.Run("when using a custom resolver", func(t *testing.T) { + saver := &Saver{} + child := &mocks.Resolver{ + MockNetwork: func() string { + return "x" + }, + } + reso := saver.WrapResolver(child) + if reso.Network() != "x" { + t.Fatal("unexpected result") + } + }) + + t.Run("when using the stdlib resolver", func(t *testing.T) { + child := netxlite.NewStdlibResolver(model.DiscardLogger) + switch network := child.Network(); network { + case netxlite.StdlibResolverGetaddrinfo, + netxlite.StdlibResolverGolangNetResolver: + // ok + default: + t.Fatal("unexpected child resolver network", network) + } + saver := &Saver{} + reso := saver.WrapResolver(child) + if network := reso.Network(); network != netxlite.StdlibResolverSystem { + t.Fatal("unexpected wrapped resolver network", network) + } + }) }) t.Run("Address", func(t *testing.T) { @@ -326,19 +364,70 @@ func TestDNSTransportSaver(t *testing.T) { t.Fatal("the saved time is wrong") } }) + + t.Run("with getaddrinfo transport there's correct .Network remapping", func(t *testing.T) { + saver := &Saver{} + reso := saver.WrapDNSTransport(netxlite.NewDNSOverGetaddrinfoTransport()) + ctx, cancel := context.WithCancel(context.Background()) + cancel() // immediately fail the operation + query := &mocks.DNSQuery{ + MockBytes: func() ([]byte, error) { + return []byte{}, nil + }, + MockType: func() uint16 { + return dns.TypeANY + }, + MockID: func() uint16 { + return 1453 + }, + MockDomain: func() string { + return "dns.google" + }, + } + _, _ = reso.RoundTrip(ctx, query) + // basically, we just want to ensure that the engine name is converted + ev := saver.Read() + if len(ev) != 2 { + t.Fatal("expected number of events") + } + if ev[0].Value().Proto != netxlite.StdlibResolverSystem { + t.Fatal("unexpected Proto") + } + if ev[1].Value().Proto != netxlite.StdlibResolverSystem { + t.Fatal("unexpected Proto") + } + }) }) t.Run("Network", func(t *testing.T) { - saver := &Saver{} - child := &mocks.DNSTransport{ - MockNetwork: func() string { - return "x" - }, - } - txp := saver.WrapDNSTransport(child) - if txp.Network() != "x" { - t.Fatal("unexpected result") - } + t.Run("with custom child transport", func(t *testing.T) { + saver := &Saver{} + child := &mocks.DNSTransport{ + MockNetwork: func() string { + return "x" + }, + } + txp := saver.WrapDNSTransport(child) + if txp.Network() != "x" { + t.Fatal("unexpected result") + } + }) + + t.Run("when using the stdlib resolver", func(t *testing.T) { + child := netxlite.NewDNSOverGetaddrinfoTransport() + switch network := child.Network(); network { + case netxlite.StdlibResolverGetaddrinfo, + netxlite.StdlibResolverGolangNetResolver: + // ok + default: + t.Fatal("unexpected child resolver network", network) + } + saver := &Saver{} + reso := saver.WrapDNSTransport(child) + if network := reso.Network(); network != netxlite.StdlibResolverSystem { + t.Fatal("unexpected wrapped resolver network", network) + } + }) }) t.Run("Address", func(t *testing.T) { @@ -429,3 +518,45 @@ func newFakeResolverWithResult(r []string) model.Resolver { }, } } + +func TestResolverNetworkAdaptNames(t *testing.T) { + type args struct { + input string + } + tests := []struct { + name string + args args + want string + }{{ + name: "with StdlibResolverGetaddrinfo", + args: args{ + input: netxlite.StdlibResolverGetaddrinfo, + }, + want: netxlite.StdlibResolverSystem, + }, { + name: "with StdlibResolverGolangNetResolver", + args: args{ + input: netxlite.StdlibResolverGolangNetResolver, + }, + want: netxlite.StdlibResolverSystem, + }, { + name: "with StdlibResolverSystem", + args: args{ + input: netxlite.StdlibResolverSystem, + }, + want: netxlite.StdlibResolverSystem, + }, { + name: "with any other name", + args: args{ + input: "doh", + }, + want: "doh", + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ResolverNetworkAdaptNames(tt.args.input); got != tt.want { + t.Errorf("ResolverNetworkAdaptNames() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/tutorial/netxlite/README.md b/internal/tutorial/netxlite/README.md index 7cb7685..d221e7f 100644 --- a/internal/tutorial/netxlite/README.md +++ b/internal/tutorial/netxlite/README.md @@ -25,7 +25,7 @@ Index: - [chapter04](chapter04) shows how to establish QUIC sessions; -- [chapter05](chapter05) is about the "system" DNS resolver; +- [chapter05](chapter05) is about the "stdlib" DNS resolver; - [chapter06](chapter06) discusses custom DNS-over-UDP resolvers; diff --git a/internal/tutorial/netxlite/chapter05/README.md b/internal/tutorial/netxlite/chapter05/README.md index 6660e94..c58e14c 100644 --- a/internal/tutorial/netxlite/chapter05/README.md +++ b/internal/tutorial/netxlite/chapter05/README.md @@ -1,10 +1,13 @@ -# Chapter I: Using the "system" DNS resolver +# Chapter I: Using the "stdlib" DNS resolver In this chapter we will write together a `main.go` file that -uses the "system" DNS resolver to lookup domain names. +uses the "stdlib" DNS resolver to lookup domain names. -The "system" resolver is the one used by `getaddrinfo` on Unix. +The "stdlib" resolver is `getaddrinfo` on Unix. If we're compiled with +`CGO_ENABLED=1`, we use the `getaddrinfo` stdlib call directly. Otherwise, +we use the `net.Resolver` resolver, which may or may not use +`getaddrinfo` (or equivalent stdlib calls) under the hood. (This file is auto-generated from the corresponding source file, so make sure you don't edit it manually.) @@ -114,4 +117,4 @@ should cause a timeout error, because the timeout is ridicolously small. ## Conclusions -We have seen how to use the "system" DNS resolver. +We have seen how to use the "stdlib" DNS resolver. diff --git a/internal/tutorial/netxlite/chapter05/main.go b/internal/tutorial/netxlite/chapter05/main.go index cf0c0ea..a0273f0 100644 --- a/internal/tutorial/netxlite/chapter05/main.go +++ b/internal/tutorial/netxlite/chapter05/main.go @@ -1,11 +1,14 @@ // -=-=- StartHere -=-=- // -// # Chapter I: Using the "system" DNS resolver +// # Chapter I: Using the "stdlib" DNS resolver // // In this chapter we will write together a `main.go` file that -// uses the "system" DNS resolver to lookup domain names. +// uses the "stdlib" DNS resolver to lookup domain names. // -// The "system" resolver is the one used by `getaddrinfo` on Unix. +// The "stdlib" resolver is `getaddrinfo` on Unix. If we're compiled with +// `CGO_ENABLED=1`, we use the `getaddrinfo` stdlib call directly. Otherwise, +// we use the `net.Resolver` resolver, which may or may not use +// `getaddrinfo` (or equivalent stdlib calls) under the hood. // // (This file is auto-generated from the corresponding source file, // so make sure you don't edit it manually.) @@ -115,4 +118,4 @@ func fatal(err error) { // // ## Conclusions // -// We have seen how to use the "system" DNS resolver. +// We have seen how to use the "stdlib" DNS resolver.