Skip to content
This repository has been archived by the owner on Mar 6, 2020. It is now read-only.

Commit

Permalink
Merge nervousresolver chaining with stable code (#106)
Browse files Browse the repository at this point in the history
Now, rather than using nervousresolver as an opt-in that is not
so easy to use, you can chain as many resolvers as you wish.

Xref: ooni/probe-engine#88
  • Loading branch information
bassosimone authored Oct 31, 2019
1 parent d7d1d58 commit c29f7ee
Show file tree
Hide file tree
Showing 15 changed files with 344 additions and 323 deletions.
16 changes: 13 additions & 3 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| Author | Simone Basso |
|--------------|--------------|
| Last-Updated | 2019-10-29 |
| Last-Updated | 2019-10-30 |
| Status | approved |

## Introduction
Expand Down Expand Up @@ -215,7 +215,7 @@ The `ConfigureDNS` method will behave exactly like the
`ConfigureDNS` method of `netx.Resolver` (see below).

```Go
func (c *Client) SetResolver(r model.DNSResolver) error
func (c *Client) SetResolver(r model.DNSResolver)
```

Also `SetResolver` is described below.
Expand Down Expand Up @@ -324,7 +324,7 @@ func (d *Dialer) ForceSpecificSNI(sni string) error
```

```Go
func (d *Dialer) SetResolver(r model.DNSResolver) error
func (d *Dialer) SetResolver(r model.DNSResolver)
```

`SetCABundle` and `ForceSpecificSNI` behave exactly like the same
Expand Down Expand Up @@ -372,6 +372,16 @@ The arguments have the same meaning of `ConfigureDNS` and
the will return an interface replacement for `net.Resolver`
as described below.

The `ChainResolvers` function allows to chain a fallback
resolver to a primary resolver, so that the former is used
whenever the latter fails:

```Go
func ChainResolvers(
primary, fallback model.DNSResolver
) model.DNSResolver
```

### The github.com/ooni/netx/x package

This is for experimental stuff.
4 changes: 2 additions & 2 deletions httpx/httpx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package httpx_test

import (
"io/ioutil"
"net"
"testing"

"github.com/ooni/netx/handlers"
"github.com/ooni/netx/httpx"
"github.com/ooni/netx/x/nervousresolver"
)

func TestIntegration(t *testing.T) {
Expand All @@ -30,7 +30,7 @@ func TestIntegration(t *testing.T) {
func TestIntegrationSetResolver(t *testing.T) {
client := httpx.NewClient(handlers.NoHandler)
defer client.Transport.CloseIdleConnections()
client.SetResolver(nervousresolver.Default)
client.SetResolver(new(net.Resolver))
resp, err := client.HTTPClient.Get("https://www.google.com")
if err != nil {
t.Fatal(err)
Expand Down
7 changes: 7 additions & 0 deletions internal/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ooni/netx/internal/dialer"
"github.com/ooni/netx/internal/httptransport"
"github.com/ooni/netx/internal/resolver"
"github.com/ooni/netx/internal/resolver/chainresolver"
"github.com/ooni/netx/model"
"golang.org/x/net/http2"
)
Expand Down Expand Up @@ -293,3 +294,9 @@ func (t *HTTPTransport) CloseIdleConnections() {
tr.CloseIdleConnections()
}
}

// ChainResolvers chains a primary and a secondary resolver such that
// we can fallback to the secondary if primary is broken.
func ChainResolvers(primary, secondary model.DNSResolver) model.DNSResolver {
return chainresolver.New(primary, secondary)
}
16 changes: 16 additions & 0 deletions internal/internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"time"

"github.com/ooni/netx/handlers"
"github.com/ooni/netx/internal/resolver/brokenresolver"
"github.com/ooni/netx/internal/resolver/systemresolver"
)

func TestIntegrationDial(t *testing.T) {
Expand Down Expand Up @@ -263,6 +265,20 @@ func TestIntegration(t *testing.T) {
client.CloseIdleConnections()
}

func TestIntegrationChainResolvers(t *testing.T) {
dialer := NewDialer(time.Now(), handlers.NoHandler)
resolver := ChainResolvers(
brokenresolver.New(),
systemresolver.New(new(net.Resolver)),
)
dialer.SetResolver(resolver)
conn, err := dialer.Dial("tcp", "www.google.com:80")
if err != nil {
t.Fatal(err)
}
defer conn.Close()
}

func TestIntegrationFailure(t *testing.T) {
client := &http.Client{
Transport: NewHTTPTransport(
Expand Down
44 changes: 44 additions & 0 deletions internal/resolver/brokenresolver/brokenresolver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Package brokenresolver is a broken resolver
package brokenresolver

import (
"context"
"net"
)

// Resolver is a broken resolver.
type Resolver struct{}

// New creates a new broken Resolver instance.
func New() *Resolver {
return &Resolver{}
}

var errNotFound = &net.DNSError{
Err: "no such host",
}

// LookupAddr returns the name of the provided IP address
func (c *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) {
return nil, errNotFound
}

// LookupCNAME returns the canonical name of a host
func (c *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) {
return "", errNotFound
}

// LookupHost returns the IP addresses of a host
func (c *Resolver) LookupHost(ctx context.Context, hostname string) ([]string, error) {
return nil, errNotFound
}

// LookupMX returns the MX records of a specific name
func (c *Resolver) LookupMX(ctx context.Context, name string) ([]*net.MX, error) {
return nil, errNotFound
}

// LookupNS returns the NS records of a specific name
func (c *Resolver) LookupNS(ctx context.Context, name string) ([]*net.NS, error) {
return nil, errNotFound
}
61 changes: 61 additions & 0 deletions internal/resolver/brokenresolver/brokenresolver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package brokenresolver

import (
"context"
"testing"
)

func TestLookupAddr(t *testing.T) {
client := New()
names, err := client.LookupAddr(context.Background(), "8.8.8.8")
if err == nil {
t.Fatal("expected an error here")
}
if names != nil {
t.Fatal("expected nil here")
}
}

func TestLookupCNAME(t *testing.T) {
client := New()
cname, err := client.LookupCNAME(context.Background(), "www.ooni.io")
if err == nil {
t.Fatal("expected an error here")
}
if cname != "" {
t.Fatal("expected empty string here")
}
}

func TestLookupHost(t *testing.T) {
client := New()
addrs, err := client.LookupHost(context.Background(), "www.google.com")
if err == nil {
t.Fatal("expected an error here")
}
if addrs != nil {
t.Fatal("expected nil here")
}
}

func TestLookupMX(t *testing.T) {
client := New()
records, err := client.LookupMX(context.Background(), "ooni.io")
if err == nil {
t.Fatal("expected an error here")
}
if records != nil {
t.Fatal("expected nil here")
}
}

func TestLookupNS(t *testing.T) {
client := New()
records, err := client.LookupNS(context.Background(), "ooni.io")
if err == nil {
t.Fatal("expected an error here")
}
if records != nil {
t.Fatal("expected nil here")
}
}
68 changes: 68 additions & 0 deletions internal/resolver/chainresolver/chainresolver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Package chainresolver allows to chain two resolvers
package chainresolver

import (
"context"
"net"

"github.com/ooni/netx/model"
)

// Resolver is a chain resolver.
type Resolver struct {
primary model.DNSResolver
secondary model.DNSResolver
}

// New creates a new chain Resolver instance.
func New(primary, secondary model.DNSResolver) *Resolver {
return &Resolver{
primary: primary,
secondary: secondary,
}
}

// LookupAddr returns the name of the provided IP address
func (c *Resolver) LookupAddr(ctx context.Context, addr string) ([]string, error) {
names, err := c.primary.LookupAddr(ctx, addr)
if err != nil {
names, err = c.secondary.LookupAddr(ctx, addr)
}
return names, err
}

// LookupCNAME returns the canonical name of a host
func (c *Resolver) LookupCNAME(ctx context.Context, host string) (string, error) {
cname, err := c.primary.LookupCNAME(ctx, host)
if err != nil {
cname, err = c.secondary.LookupCNAME(ctx, host)
}
return cname, err
}

// LookupHost returns the IP addresses of a host
func (c *Resolver) LookupHost(ctx context.Context, hostname string) ([]string, error) {
addrs, err := c.primary.LookupHost(ctx, hostname)
if err != nil {
addrs, err = c.secondary.LookupHost(ctx, hostname)
}
return addrs, err
}

// LookupMX returns the MX records of a specific name
func (c *Resolver) LookupMX(ctx context.Context, name string) ([]*net.MX, error) {
records, err := c.primary.LookupMX(ctx, name)
if err != nil {
records, err = c.secondary.LookupMX(ctx, name)
}
return records, err
}

// LookupNS returns the NS records of a specific name
func (c *Resolver) LookupNS(ctx context.Context, name string) ([]*net.NS, error) {
records, err := c.primary.LookupNS(ctx, name)
if err != nil {
records, err = c.secondary.LookupNS(ctx, name)
}
return records, err
}
64 changes: 64 additions & 0 deletions internal/resolver/chainresolver/chainresolver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package chainresolver

import (
"context"
"net"
"testing"

"github.com/ooni/netx/internal/resolver/brokenresolver"
)

func TestLookupAddr(t *testing.T) {
client := New(brokenresolver.New(), new(net.Resolver))
names, err := client.LookupAddr(context.Background(), "8.8.8.8")
if err != nil {
t.Fatal(err)
}
if names == nil {
t.Fatal("expect non nil return value here")
}
}

func TestLookupCNAME(t *testing.T) {
client := New(brokenresolver.New(), new(net.Resolver))
cname, err := client.LookupCNAME(context.Background(), "www.ooni.io")
if err != nil {
t.Fatal(err)
}
if cname == "" {
t.Fatal("expect non empty return value here")
}
}

func TestLookupHost(t *testing.T) {
client := New(brokenresolver.New(), new(net.Resolver))
addrs, err := client.LookupHost(context.Background(), "www.google.com")
if err != nil {
t.Fatal(err)
}
if addrs == nil {
t.Fatal("expect non nil return value here")
}
}

func TestLookupMX(t *testing.T) {
client := New(brokenresolver.New(), new(net.Resolver))
records, err := client.LookupMX(context.Background(), "ooni.io")
if err != nil {
t.Fatal(err)
}
if records == nil {
t.Fatal("expect non nil return value here")
}
}

func TestLookupNS(t *testing.T) {
client := New(brokenresolver.New(), new(net.Resolver))
records, err := client.LookupNS(context.Background(), "ooni.io")
if err != nil {
t.Fatal(err)
}
if records == nil {
t.Fatal("expect non nil return value here")
}
}
Loading

0 comments on commit c29f7ee

Please sign in to comment.