Skip to content

Commit

Permalink
feat(netxlite): add dialer factory, simplify resolver factory (ooni#459)
Browse files Browse the repository at this point in the history
  • Loading branch information
bassosimone authored Sep 5, 2021
1 parent 61a1809 commit 208f3ef
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 19 deletions.
20 changes: 20 additions & 0 deletions internal/netxlite/dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ type Dialer interface {
CloseIdleConnections()
}

// NewDialerWithResolver creates a dialer using the given resolver and logger.
func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
return &dialerLogger{
Dialer: &dialerResolver{
Dialer: &dialerLogger{
Dialer: &dialerSystem{},
Logger: logger,
},
Resolver: resolver,
},
Logger: logger,
}
}

// NewDialerWithoutResolver creates a dialer that uses the given
// logger and fails with ErrNoResolver when it is passed a domain name.
func NewDialerWithoutResolver(logger Logger) Dialer {
return NewDialerWithResolver(logger, &nullResolver{})
}

// underlyingDialer is the Dialer we use by default.
var underlyingDialer = &net.Dialer{
Timeout: 15 * time.Second,
Expand Down
28 changes: 28 additions & 0 deletions internal/netxlite/dialer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,31 @@ func TestUnderlyingDialerHasTimeout(t *testing.T) {
t.Fatal("unexpected timeout value")
}
}

func TestNewDialerWithoutResolverChain(t *testing.T) {
dlr := NewDialerWithoutResolver(log.Log)
dlog, okay := dlr.(*dialerLogger)
if !okay {
t.Fatal("invalid type")
}
if dlog.Logger != log.Log {
t.Fatal("invalid logger")
}
dreso, okay := dlog.Dialer.(*dialerResolver)
if !okay {
t.Fatal("invalid type")
}
if _, okay := dreso.Resolver.(*nullResolver); !okay {
t.Fatal("invalid Resolver type")
}
dlog, okay = dreso.Dialer.(*dialerLogger)
if !okay {
t.Fatal("invalid type")
}
if dlog.Logger != log.Log {
t.Fatal("invalid logger")
}
if _, okay := dlog.Dialer.(*dialerSystem); !okay {
t.Fatal("invalid type")
}
}
2 changes: 1 addition & 1 deletion internal/netxlite/http3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestHTTP3TransportWorks(t *testing.T) {
Dialer: &quicDialerQUICGo{
QUICListener: &quicListenerStdlib{},
},
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
}
txp := NewHTTP3Transport(d, &tls.Config{})
client := &http.Client{Transport: txp}
Expand Down
4 changes: 2 additions & 2 deletions internal/netxlite/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func TestHTTPTransportLoggerCloseIdleConnections(t *testing.T) {
func TestHTTPTransportWorks(t *testing.T) {
d := &dialerResolver{
Dialer: defaultDialer,
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
}
th := &tlsHandshakerConfigurable{}
txp := NewHTTPTransport(d, &tls.Config{}, th)
Expand All @@ -134,7 +134,7 @@ func TestHTTPTransportWithFailingDialer(t *testing.T) {
return nil, expected
},
},
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
}
th := &tlsHandshakerConfigurable{}
txp := NewHTTPTransport(d, &tls.Config{}, th)
Expand Down
8 changes: 4 additions & 4 deletions internal/netxlite/quic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func TestQUICDialerQUICGoTLSDefaultsForDoQ(t *testing.T) {
func TestQUICDialerResolverSuccess(t *testing.T) {
tlsConfig := &tls.Config{}
dialer := &quicDialerResolver{
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
Dialer: &quicDialerQUICGo{
QUICListener: &quicListenerStdlib{},
}}
Expand All @@ -234,7 +234,7 @@ func TestQUICDialerResolverSuccess(t *testing.T) {
func TestQUICDialerResolverNoPort(t *testing.T) {
tlsConfig := &tls.Config{}
dialer := &quicDialerResolver{
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
Dialer: &quicDialerQUICGo{}}
sess, err := dialer.DialContext(
context.Background(), "udp", "www.google.com",
Expand Down Expand Up @@ -288,7 +288,7 @@ func TestQUICDialerResolverInvalidPort(t *testing.T) {
// to establish a connection leads to a failure
tlsConf := &tls.Config{}
dialer := &quicDialerResolver{
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
Dialer: &quicDialerQUICGo{
QUICListener: &quicListenerStdlib{},
}}
Expand All @@ -312,7 +312,7 @@ func TestQUICDialerResolverApplyTLSDefaults(t *testing.T) {
var gotTLSConfig *tls.Config
tlsConfig := &tls.Config{}
dialer := &quicDialerResolver{
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
Dialer: &mocks.QUICContextDialer{
MockDialContext: func(ctx context.Context, network, address string,
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
Expand Down
41 changes: 32 additions & 9 deletions internal/netxlite/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package netxlite

import (
"context"
"errors"
"net"
"time"

Expand All @@ -23,20 +24,15 @@ type Resolver interface {
CloseIdleConnections()
}

// ResolverConfig contains config for creating a resolver.
type ResolverConfig struct {
// Logger is the MANDATORY logger to use.
Logger Logger
}

// NewResolver creates a new resolver.
func NewResolver(config *ResolverConfig) Resolver {
// NewResolverSystem creates a new resolver using system
// facilities for resolving domain names (e.g., getaddrinfo).
func NewResolverSystem(logger Logger) Resolver {
return &resolverIDNA{
Resolver: &resolverLogger{
Resolver: &resolverShortCircuitIPAddr{
Resolver: &resolverSystem{},
},
Logger: config.Logger,
Logger: logger,
},
}
}
Expand Down Expand Up @@ -159,3 +155,30 @@ func (r *resolverShortCircuitIPAddr) LookupHost(ctx context.Context, hostname st
}
return r.Resolver.LookupHost(ctx, hostname)
}

// ErrNoResolver indicates you are using a dialer without a resolver.
var ErrNoResolver = errors.New("no configured resolver")

// nullResolver is a resolver that is not capable of resolving
// domain names to IP addresses and always returns ErrNoResolver.
type nullResolver struct{}

// LookupHost implements Resolver.LookupHost.
func (r *nullResolver) LookupHost(ctx context.Context, hostname string) (addrs []string, err error) {
return nil, ErrNoResolver
}

// Network implements Resolver.Network.
func (r *nullResolver) Network() string {
return "null"
}

// Address implements Resolver.Address.
func (r *nullResolver) Address() string {
return ""
}

// CloseIdleConnections implements Resolver.CloseIdleConnections.
func (r *nullResolver) CloseIdleConnections() {
// nothing
}
23 changes: 20 additions & 3 deletions internal/netxlite/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,7 @@ func TestResolverIDNAWithInvalidPunycode(t *testing.T) {
}

func TestNewResolverTypeChain(t *testing.T) {
r := NewResolver(&ResolverConfig{
Logger: log.Log,
})
r := NewResolverSystem(log.Log)
ridna, ok := r.(*resolverIDNA)
if !ok {
t.Fatal("invalid resolver")
Expand Down Expand Up @@ -238,3 +236,22 @@ func TestResolverShortCircuitIPAddrWithDomain(t *testing.T) {
t.Fatal("invalid result")
}
}

func TestNullResolverWorksAsIntended(t *testing.T) {
r := &nullResolver{}
ctx := context.Background()
addrs, err := r.LookupHost(ctx, "dns.google")
if !errors.Is(err, ErrNoResolver) {
t.Fatal("not the error we expected", err)
}
if addrs != nil {
t.Fatal("expected nil addr")
}
if r.Network() != "null" {
t.Fatal("invalid network")
}
if r.Address() != "" {
t.Fatal("invalid address")
}
r.CloseIdleConnections() // should not crash
}

0 comments on commit 208f3ef

Please sign in to comment.