feat(netxlite): implements NS queries (#734)

This diff has been extracted from https://github.com/bassosimone/websteps-illustrated/commit/eb0bf38957e79fbad198fcdc9f9c7b36f61a8e2c.

See https://github.com/ooni/probe/issues/2096.

While there, skip the broken tests caused by issue
https://github.com/ooni/probe/issues/2098.
This commit is contained in:
Simone Basso
2022-05-16 10:46:53 +02:00
committed by GitHub
parent c1b06a2d09
commit ce052b665e
26 changed files with 857 additions and 73 deletions
@@ -29,6 +29,7 @@ import (
"errors"
"fmt"
"math/rand"
"net"
"net/url"
"sync"
"time"
@@ -110,9 +111,16 @@ func (r *Resolver) Stats() string {
return fmt.Sprintf("sessionresolver: %s", string(data))
}
var errNotImplemented = errors.New("not implemented")
// LookupHTTPS implements Resolver.LookupHTTPS.
func (r *Resolver) LookupHTTPS(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
return nil, errors.New("not implemented")
return nil, errNotImplemented
}
// LookupNS implements Resolver.LookupNS.
func (r *Resolver) LookupNS(ctx context.Context, domain string) ([]*net.NS, error) {
return nil, errNotImplemented
}
// ErrLookupHost indicates that LookupHost failed.
@@ -343,3 +343,27 @@ func TestShouldSkipWithProxyWorks(t *testing.T) {
}
}
}
func TestUnimplementedFunctions(t *testing.T) {
t.Run("LookupHTTPS", func(t *testing.T) {
r := &Resolver{}
https, err := r.LookupHTTPS(context.Background(), "dns.google")
if !errors.Is(err, errNotImplemented) {
t.Fatal("unexpected error", err)
}
if https != nil {
t.Fatal("expected nil result")
}
})
t.Run("LookupNS", func(t *testing.T) {
r := &Resolver{}
ns, err := r.LookupNS(context.Background(), "dns.google")
if !errors.Is(err, errNotImplemented) {
t.Fatal("unexpected error", err)
}
if len(ns) > 0 {
t.Fatal("expected empty result")
}
})
}
+1 -1
View File
@@ -25,7 +25,7 @@ func (r *CacheResolver) LookupHost(
if err != nil {
return nil, err
}
if r.ReadOnly == false {
if !r.ReadOnly {
r.Set(hostname, entry)
}
return entry, nil
+5 -13
View File
@@ -6,14 +6,11 @@ import (
"testing"
"github.com/ooni/probe-cli/v3/internal/engine/netx/resolver"
"github.com/ooni/probe-cli/v3/internal/model"
)
func TestCacheFailure(t *testing.T) {
expected := errors.New("mocked error")
var r model.Resolver = resolver.FakeResolver{
Err: expected,
}
r := resolver.NewFakeResolverWithExplicitError(expected)
cache := &resolver.CacheResolver{Resolver: r}
addrs, err := cache.LookupHost(context.Background(), "www.google.com")
if !errors.Is(err, expected) {
@@ -28,9 +25,8 @@ func TestCacheFailure(t *testing.T) {
}
func TestCacheHitSuccess(t *testing.T) {
var r model.Resolver = resolver.FakeResolver{
Err: errors.New("mocked error"),
}
expected := errors.New("mocked error")
r := resolver.NewFakeResolverWithExplicitError(expected)
cache := &resolver.CacheResolver{Resolver: r}
cache.Set("dns.google.com", []string{"8.8.8.8"})
addrs, err := cache.LookupHost(context.Background(), "dns.google.com")
@@ -43,9 +39,7 @@ func TestCacheHitSuccess(t *testing.T) {
}
func TestCacheMissSuccess(t *testing.T) {
var r model.Resolver = resolver.FakeResolver{
Result: []string{"8.8.8.8"},
}
r := resolver.NewFakeResolverWithResult([]string{"8.8.8.8"})
cache := &resolver.CacheResolver{Resolver: r}
addrs, err := cache.LookupHost(context.Background(), "dns.google.com")
if err != nil {
@@ -60,9 +54,7 @@ func TestCacheMissSuccess(t *testing.T) {
}
func TestCacheReadonlySuccess(t *testing.T) {
var r model.Resolver = resolver.FakeResolver{
Result: []string{"8.8.8.8"},
}
r := resolver.NewFakeResolverWithResult([]string{"8.8.8.8"})
cache := &resolver.CacheResolver{Resolver: r, ReadOnly: true}
addrs, err := cache.LookupHost(context.Background(), "dns.google.com")
if err != nil {
+47 -40
View File
@@ -7,8 +7,10 @@ import (
"net"
"time"
"github.com/ooni/probe-cli/v3/internal/atomicx"
"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"
"github.com/ooni/probe-cli/v3/internal/runtimex"
)
type FakeDialer struct {
@@ -108,48 +110,53 @@ func (fe FakeEncoder) Encode(domain string, qtype uint16, padding bool) ([]byte,
return fe.Data, fe.Err
}
type FakeResolver struct {
NumFailures *atomicx.Int64
Err error
Result []string
func NewFakeResolverThatFails() model.Resolver {
return NewFakeResolverWithExplicitError(netxlite.ErrOODNSNoSuchHost)
}
func NewFakeResolverThatFails() FakeResolver {
return FakeResolver{NumFailures: &atomicx.Int64{}, Err: errNotFound}
}
func NewFakeResolverWithResult(r []string) FakeResolver {
return FakeResolver{NumFailures: &atomicx.Int64{}, Result: r}
}
var errNotFound = &net.DNSError{
Err: "no such host",
}
func (c FakeResolver) LookupHost(ctx context.Context, hostname string) ([]string, error) {
time.Sleep(10 * time.Microsecond)
if c.Err != nil {
if c.NumFailures != nil {
c.NumFailures.Add(1)
}
return nil, c.Err
func NewFakeResolverWithExplicitError(err error) model.Resolver {
runtimex.PanicIfNil(err, "passed nil error")
return &mocks.Resolver{
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
return nil, err
},
MockNetwork: func() string {
return "fake"
},
MockAddress: func() string {
return ""
},
MockCloseIdleConnections: func() {
// nothing
},
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
return nil, errors.New("not implemented")
},
MockLookupNS: func(ctx context.Context, domain string) ([]*net.NS, error) {
return nil, errors.New("not implemented")
},
}
return c.Result, nil
}
func (c FakeResolver) Network() string {
return "fake"
func NewFakeResolverWithResult(r []string) model.Resolver {
return &mocks.Resolver{
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
return r, nil
},
MockNetwork: func() string {
return "fake"
},
MockAddress: func() string {
return ""
},
MockCloseIdleConnections: func() {
// nothing
},
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
return nil, errors.New("not implemented")
},
MockLookupNS: func(ctx context.Context, domain string) ([]*net.NS, error) {
return nil, errors.New("not implemented")
},
}
}
func (c FakeResolver) Address() string {
return ""
}
func (c FakeResolver) CloseIdleConnections() {}
func (c FakeResolver) LookupHTTPS(
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
return nil, errors.New("not implemented")
}
var _ model.Resolver = FakeResolver{}
+4 -8
View File
@@ -16,10 +16,8 @@ func TestSaverResolverFailure(t *testing.T) {
expected := errors.New("no such host")
saver := &trace.Saver{}
reso := resolver.SaverResolver{
Resolver: resolver.FakeResolver{
Err: expected,
},
Saver: saver,
Resolver: resolver.NewFakeResolverWithExplicitError(expected),
Saver: saver,
}
addrs, err := reso.LookupHost(context.Background(), "www.google.com")
if !errors.Is(err, expected) {
@@ -65,10 +63,8 @@ func TestSaverResolverSuccess(t *testing.T) {
expected := []string{"8.8.8.8", "8.8.4.4"}
saver := &trace.Saver{}
reso := resolver.SaverResolver{
Resolver: resolver.FakeResolver{
Result: expected,
},
Saver: saver,
Resolver: resolver.NewFakeResolverWithResult(expected),
Saver: saver,
}
addrs, err := reso.LookupHost(context.Background(), "www.google.com")
if err != nil {
@@ -14,6 +14,7 @@ import (
)
func TestCheckReportIDWorkingAsIntended(t *testing.T) {
t.Skip("see https://github.com/ooni/probe/issues/2098")
client := probeservices.Client{
APIClientTemplate: httpx.APIClientTemplate{
BaseURL: "https://ams-pg.ooni.org/",