2d3d5d9cdc
This diff modifies netx to stop using most netxlite resolver internals but the internal function that creates a new, unwrapped system resolver, which will be dealt with in a subsequent pull request. See https://github.com/ooni/probe/issues/2121
479 lines
12 KiB
Go
479 lines
12 KiB
Go
package netx
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"errors"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/apex/log"
|
|
"github.com/ooni/probe-cli/v3/internal/bytecounter"
|
|
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
|
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
|
"github.com/ooni/probe-cli/v3/internal/netxlite/filtering"
|
|
"github.com/ooni/probe-cli/v3/internal/tracex"
|
|
)
|
|
|
|
func TestNewTLSDialer(t *testing.T) {
|
|
t.Run("we always have error wrapping", func(t *testing.T) {
|
|
server := filtering.NewTLSServer(filtering.TLSActionReset)
|
|
defer server.Close()
|
|
tdx := NewTLSDialer(Config{})
|
|
conn, err := tdx.DialTLSContext(context.Background(), "tcp", server.Endpoint())
|
|
if err == nil || err.Error() != netxlite.FailureConnectionReset {
|
|
t.Fatal("unexpected err", err)
|
|
}
|
|
if conn != nil {
|
|
t.Fatal("expected nil conn")
|
|
}
|
|
})
|
|
|
|
t.Run("we can collect measurements", func(t *testing.T) {
|
|
server := filtering.NewTLSServer(filtering.TLSActionReset)
|
|
defer server.Close()
|
|
saver := &tracex.Saver{}
|
|
tdx := NewTLSDialer(Config{
|
|
Saver: saver,
|
|
})
|
|
conn, err := tdx.DialTLSContext(context.Background(), "tcp", server.Endpoint())
|
|
if err == nil || err.Error() != netxlite.FailureConnectionReset {
|
|
t.Fatal("unexpected err", err)
|
|
}
|
|
if conn != nil {
|
|
t.Fatal("expected nil conn")
|
|
}
|
|
if len(saver.Read()) <= 0 {
|
|
t.Fatal("did not read any event")
|
|
}
|
|
})
|
|
|
|
t.Run("we can skip TLS verification", func(t *testing.T) {
|
|
server := filtering.NewTLSServer(filtering.TLSActionBlockText)
|
|
defer server.Close()
|
|
tdx := NewTLSDialer(Config{TLSConfig: &tls.Config{
|
|
InsecureSkipVerify: true,
|
|
}})
|
|
conn, err := tdx.DialTLSContext(context.Background(), "tcp", server.Endpoint())
|
|
if err != nil {
|
|
t.Fatal(err.(*netxlite.ErrWrapper).WrappedErr)
|
|
}
|
|
conn.Close()
|
|
})
|
|
|
|
t.Run("we can set the cert pool", func(t *testing.T) {
|
|
server := filtering.NewTLSServer(filtering.TLSActionBlockText)
|
|
defer server.Close()
|
|
tdx := NewTLSDialer(Config{
|
|
TLSConfig: &tls.Config{
|
|
RootCAs: server.CertPool(),
|
|
ServerName: "dns.google",
|
|
},
|
|
})
|
|
conn, err := tdx.DialTLSContext(context.Background(), "tcp", server.Endpoint())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
conn.Close()
|
|
})
|
|
}
|
|
|
|
func TestNewWithDialer(t *testing.T) {
|
|
expected := errors.New("mocked error")
|
|
dialer := &mocks.Dialer{
|
|
MockDialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
return nil, expected
|
|
},
|
|
}
|
|
txp := NewHTTPTransport(Config{
|
|
Dialer: dialer,
|
|
})
|
|
client := &http.Client{Transport: txp}
|
|
resp, err := client.Get("http://www.google.com")
|
|
if !errors.Is(err, expected) {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("not the response we expected")
|
|
}
|
|
}
|
|
|
|
func TestNewWithByteCounter(t *testing.T) {
|
|
counter := bytecounter.New()
|
|
txp := NewHTTPTransport(Config{
|
|
ByteCounter: counter,
|
|
})
|
|
bctxp, ok := txp.(*bytecounter.HTTPTransport)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
if bctxp.Counter != counter {
|
|
t.Fatal("not the byte counter we expected")
|
|
}
|
|
// We are going to trust the underlying transport returned by netxlite
|
|
}
|
|
|
|
func TestNewWithSaver(t *testing.T) {
|
|
saver := new(tracex.Saver)
|
|
txp := NewHTTPTransport(Config{
|
|
Saver: saver,
|
|
})
|
|
stxptxp, ok := txp.(*tracex.HTTPTransportSaver)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
if stxptxp.Saver != saver {
|
|
t.Fatal("not the logger we expected")
|
|
}
|
|
if stxptxp.Saver != saver {
|
|
t.Fatal("not the logger we expected")
|
|
}
|
|
// We are going to trust the underlying type returned by netxlite
|
|
}
|
|
|
|
func TestNewDNSClientInvalidURL(t *testing.T) {
|
|
dnsclient, err := NewDNSClient(Config{}, "\t\t\t")
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if dnsclient != nil {
|
|
t.Fatal("expected nil resolver here")
|
|
}
|
|
}
|
|
|
|
func TestNewDNSClientUnsupportedScheme(t *testing.T) {
|
|
dnsclient, err := NewDNSClient(Config{}, "antani:///")
|
|
if err == nil || err.Error() != "unsupported resolver scheme" {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if dnsclient != nil {
|
|
t.Fatal("expected nil resolver here")
|
|
}
|
|
}
|
|
|
|
func TestNewDNSClientPowerdnsDoH(t *testing.T) {
|
|
dnsclient, err := NewDNSClient(
|
|
Config{}, "doh://powerdns")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
if _, ok := r.Transport().(*netxlite.DNSOverHTTPSTransport); !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSClientGoogleDoH(t *testing.T) {
|
|
dnsclient, err := NewDNSClient(
|
|
Config{}, "doh://google")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
if _, ok := r.Transport().(*netxlite.DNSOverHTTPSTransport); !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSClientCloudflareDoH(t *testing.T) {
|
|
dnsclient, err := NewDNSClient(
|
|
Config{}, "doh://cloudflare")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
if _, ok := r.Transport().(*netxlite.DNSOverHTTPSTransport); !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSClientCloudflareDoHSaver(t *testing.T) {
|
|
saver := new(tracex.Saver)
|
|
dnsclient, err := NewDNSClient(
|
|
Config{Saver: saver}, "doh://cloudflare")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
txp, ok := r.Transport().(*tracex.DNSTransportSaver)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
if _, ok := txp.DNSTransport.(*netxlite.DNSOverHTTPSTransport); !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSClientUDP(t *testing.T) {
|
|
dnsclient, err := NewDNSClient(
|
|
Config{}, "udp://8.8.8.8:53")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
if _, ok := r.Transport().(*netxlite.DNSOverUDPTransport); !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSClientUDPDNSSaver(t *testing.T) {
|
|
saver := new(tracex.Saver)
|
|
dnsclient, err := NewDNSClient(
|
|
Config{Saver: saver}, "udp://8.8.8.8:53")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
txp, ok := r.Transport().(*tracex.DNSTransportSaver)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
if _, ok := txp.DNSTransport.(*netxlite.DNSOverUDPTransport); !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSClientTCP(t *testing.T) {
|
|
dnsclient, err := NewDNSClient(
|
|
Config{}, "tcp://8.8.8.8:53")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
txp, ok := r.Transport().(*netxlite.DNSOverTCPTransport)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
if txp.Network() != "tcp" {
|
|
t.Fatal("not the Network we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSClientTCPDNSSaver(t *testing.T) {
|
|
saver := new(tracex.Saver)
|
|
dnsclient, err := NewDNSClient(
|
|
Config{Saver: saver}, "tcp://8.8.8.8:53")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
txp, ok := r.Transport().(*tracex.DNSTransportSaver)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
dotcp, ok := txp.DNSTransport.(*netxlite.DNSOverTCPTransport)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
if dotcp.Network() != "tcp" {
|
|
t.Fatal("not the Network we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSClientDoT(t *testing.T) {
|
|
dnsclient, err := NewDNSClient(
|
|
Config{}, "dot://8.8.8.8:53")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
txp, ok := r.Transport().(*netxlite.DNSOverTCPTransport)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
if txp.Network() != "dot" {
|
|
t.Fatal("not the Network we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSClientDoTDNSSaver(t *testing.T) {
|
|
saver := new(tracex.Saver)
|
|
dnsclient, err := NewDNSClient(
|
|
Config{Saver: saver}, "dot://8.8.8.8:53")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, ok := dnsclient.(*netxlite.SerialResolver)
|
|
if !ok {
|
|
t.Fatal("not the resolver we expected")
|
|
}
|
|
txp, ok := r.Transport().(*tracex.DNSTransportSaver)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
dotls, ok := txp.DNSTransport.(*netxlite.DNSOverTCPTransport)
|
|
if !ok {
|
|
t.Fatal("not the transport we expected")
|
|
}
|
|
if dotls.Network() != "dot" {
|
|
t.Fatal("not the Network we expected")
|
|
}
|
|
dnsclient.CloseIdleConnections()
|
|
}
|
|
|
|
func TestNewDNSCLientDoTWithoutPort(t *testing.T) {
|
|
c, err := NewDNSClientWithOverrides(
|
|
Config{}, "dot://8.8.8.8", "", "8.8.8.8", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.Address() != "8.8.8.8:853" {
|
|
t.Fatal("expected default port to be added")
|
|
}
|
|
}
|
|
|
|
func TestNewDNSCLientTCPWithoutPort(t *testing.T) {
|
|
c, err := NewDNSClientWithOverrides(
|
|
Config{}, "tcp://8.8.8.8", "", "8.8.8.8", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.Address() != "8.8.8.8:53" {
|
|
t.Fatal("expected default port to be added")
|
|
}
|
|
}
|
|
|
|
func TestNewDNSCLientUDPWithoutPort(t *testing.T) {
|
|
c, err := NewDNSClientWithOverrides(
|
|
Config{}, "udp://8.8.8.8", "", "8.8.8.8", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if c.Address() != "8.8.8.8:53" {
|
|
t.Fatal("expected default port to be added")
|
|
}
|
|
}
|
|
|
|
func TestNewDNSClientBadDoTEndpoint(t *testing.T) {
|
|
_, err := NewDNSClient(
|
|
Config{}, "dot://bad:endpoint:53")
|
|
if err == nil || !strings.Contains(err.Error(), "too many colons in address") {
|
|
t.Fatal("expected error with bad endpoint")
|
|
}
|
|
}
|
|
|
|
func TestNewDNSClientBadTCPEndpoint(t *testing.T) {
|
|
_, err := NewDNSClient(
|
|
Config{}, "tcp://bad:endpoint:853")
|
|
if err == nil || !strings.Contains(err.Error(), "too many colons in address") {
|
|
t.Fatal("expected error with bad endpoint")
|
|
}
|
|
}
|
|
|
|
func TestNewDNSClientBadUDPEndpoint(t *testing.T) {
|
|
_, err := NewDNSClient(
|
|
Config{}, "udp://bad:endpoint:853")
|
|
if err == nil || !strings.Contains(err.Error(), "too many colons in address") {
|
|
t.Fatal("expected error with bad endpoint")
|
|
}
|
|
}
|
|
|
|
func TestNewDNSCLientWithInvalidTLSVersion(t *testing.T) {
|
|
_, err := NewDNSClientWithOverrides(
|
|
Config{}, "dot://8.8.8.8", "", "", "TLSv999")
|
|
if !errors.Is(err, netxlite.ErrInvalidTLSVersion) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
}
|
|
|
|
func TestSuccess(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skip test in short mode")
|
|
}
|
|
log.SetLevel(log.DebugLevel)
|
|
counter := bytecounter.New()
|
|
config := Config{
|
|
BogonIsError: true,
|
|
ByteCounter: counter,
|
|
CacheResolutions: true,
|
|
ContextByteCounting: true,
|
|
Logger: log.Log,
|
|
ReadWriteSaver: &tracex.Saver{},
|
|
Saver: &tracex.Saver{},
|
|
}
|
|
txp := NewHTTPTransport(config)
|
|
client := &http.Client{Transport: txp}
|
|
resp, err := client.Get("https://www.google.com")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err = netxlite.ReadAllContext(context.Background(), resp.Body); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = resp.Body.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if counter.Sent.Load() <= 0 {
|
|
t.Fatal("no bytes sent?!")
|
|
}
|
|
if counter.Received.Load() <= 0 {
|
|
t.Fatal("no bytes received?!")
|
|
}
|
|
if ev := config.ReadWriteSaver.Read(); len(ev) <= 0 {
|
|
t.Fatal("no R/W events?!")
|
|
}
|
|
if ev := config.Saver.Read(); len(ev) <= 0 {
|
|
t.Fatal("no non-I/O events?!")
|
|
}
|
|
}
|
|
|
|
func TestBogonResolutionNotBroken(t *testing.T) {
|
|
saver := new(tracex.Saver)
|
|
r := NewResolver(Config{
|
|
BogonIsError: true,
|
|
DNSCache: map[string][]string{
|
|
"www.google.com": {"127.0.0.1"},
|
|
},
|
|
Saver: saver,
|
|
Logger: log.Log,
|
|
})
|
|
addrs, err := r.LookupHost(context.Background(), "www.google.com")
|
|
if !errors.Is(err, netxlite.ErrDNSBogon) {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if err.Error() != netxlite.FailureDNSBogonError {
|
|
t.Fatal("error not correctly wrapped")
|
|
}
|
|
if len(addrs) > 0 {
|
|
t.Fatal("expected no addresses here")
|
|
}
|
|
}
|