refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
package netxlite
|
|
|
|
|
2022-05-15 19:25:27 +02:00
|
|
|
//
|
|
|
|
// DNS resolver
|
|
|
|
//
|
|
|
|
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
import (
|
|
|
|
"context"
|
2021-09-05 20:41:46 +02:00
|
|
|
"errors"
|
2021-09-27 16:48:46 +02:00
|
|
|
"fmt"
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
"net"
|
2022-06-02 22:25:37 +02:00
|
|
|
"net/http"
|
2022-05-13 15:32:47 +02:00
|
|
|
"strings"
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
"time"
|
2021-08-17 10:29:06 +02:00
|
|
|
|
2022-06-01 09:59:44 +02:00
|
|
|
"github.com/miekg/dns"
|
2022-01-03 13:53:23 +01:00
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
2021-08-17 10:29:06 +02:00
|
|
|
"golang.org/x/net/idna"
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
)
|
|
|
|
|
2021-09-29 20:21:25 +02:00
|
|
|
// ErrNoDNSTransport is the error returned when you attempt to perform
|
2022-05-14 17:38:31 +02:00
|
|
|
// a DNS operation that requires a custom DNSTransport (e.g., DNSOverHTTPSTransport)
|
2022-08-27 15:47:48 +02:00
|
|
|
// but you are using the "stdlib" resolver instead.
|
2021-09-27 23:09:41 +02:00
|
|
|
var ErrNoDNSTransport = errors.New("operation requires a DNS transport")
|
|
|
|
|
2022-06-06 14:46:44 +02:00
|
|
|
// NewStdlibResolver creates a new Resolver by combining WrapResolver
|
2022-08-27 15:47:48 +02:00
|
|
|
// with an internal "stdlib" resolver type. The list of optional wrappers
|
2022-06-01 11:10:08 +02:00
|
|
|
// allow to wrap the underlying getaddrinfo transport. Any nil wrapper
|
|
|
|
// will be silently ignored by the code that performs the wrapping.
|
2022-06-06 14:46:44 +02:00
|
|
|
func NewStdlibResolver(logger model.DebugLogger, wrappers ...model.DNSTransportWrapper) model.Resolver {
|
|
|
|
return WrapResolver(logger, NewUnwrappedStdlibResolver(wrappers...))
|
2022-06-01 09:59:44 +02:00
|
|
|
}
|
|
|
|
|
2022-06-02 22:25:37 +02:00
|
|
|
// NewParallelDNSOverHTTPSResolver creates a new DNS over HTTPS resolver
|
|
|
|
// that uses the standard library for all operations. This function constructs
|
|
|
|
// all the building blocks and calls WrapResolver on the returned resolver.
|
|
|
|
func NewParallelDNSOverHTTPSResolver(logger model.DebugLogger, URL string) model.Resolver {
|
|
|
|
client := &http.Client{Transport: NewHTTPTransportStdlib(logger)}
|
|
|
|
txp := WrapDNSTransport(NewUnwrappedDNSOverHTTPSTransport(client, URL))
|
|
|
|
return WrapResolver(logger, NewUnwrappedParallelResolver(txp))
|
|
|
|
}
|
|
|
|
|
2022-06-06 14:46:44 +02:00
|
|
|
// NewUnwrappedStdlibResolver returns a new, unwrapped resolver using the standard
|
|
|
|
// library (i.e., getaddrinfo if possible and &net.Resolver{} otherwise). As the name
|
|
|
|
// implies, this function returns an unwrapped resolver.
|
|
|
|
func NewUnwrappedStdlibResolver(wrappers ...model.DNSTransportWrapper) model.Resolver {
|
2022-06-01 09:59:44 +02:00
|
|
|
return &resolverSystem{
|
2022-08-27 15:47:48 +02:00
|
|
|
t: WrapDNSTransport(NewDNSOverGetaddrinfoTransport(), wrappers...),
|
2022-06-01 09:59:44 +02:00
|
|
|
}
|
2021-09-27 12:00:43 +02:00
|
|
|
}
|
|
|
|
|
2022-07-08 19:42:24 +02:00
|
|
|
// NewSerialUDPResolver creates a new Resolver using DNS-over-UDP
|
2022-06-01 11:10:08 +02:00
|
|
|
// that performs serial A/AAAA lookups during LookupHost.
|
|
|
|
//
|
|
|
|
// Deprecated: use NewParallelResolverUDP.
|
|
|
|
//
|
|
|
|
// Arguments:
|
|
|
|
//
|
|
|
|
// - logger is the logger to use
|
|
|
|
//
|
|
|
|
// - dialer is the dialer to create and connect UDP conns
|
|
|
|
//
|
|
|
|
// - address is the server address (e.g., 1.1.1.1:53)
|
|
|
|
//
|
|
|
|
// - wrappers is the optional list of wrappers to wrap the underlying
|
|
|
|
// transport. Any nil wrapper will be silently ignored.
|
2022-07-08 19:42:24 +02:00
|
|
|
func NewSerialUDPResolver(logger model.DebugLogger, dialer model.Dialer,
|
2022-06-01 11:10:08 +02:00
|
|
|
address string, wrappers ...model.DNSTransportWrapper) model.Resolver {
|
|
|
|
return WrapResolver(logger, NewUnwrappedSerialResolver(
|
|
|
|
WrapDNSTransport(NewUnwrappedDNSOverUDPTransport(dialer, address), wrappers...),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2022-07-08 19:42:24 +02:00
|
|
|
// NewParallelUDPResolver creates a new Resolver using DNS-over-UDP
|
2022-06-01 11:10:08 +02:00
|
|
|
// that performs parallel A/AAAA lookups during LookupHost.
|
2021-09-29 20:21:25 +02:00
|
|
|
//
|
|
|
|
// Arguments:
|
|
|
|
//
|
|
|
|
// - logger is the logger to use
|
|
|
|
//
|
|
|
|
// - dialer is the dialer to create and connect UDP conns
|
|
|
|
//
|
|
|
|
// - address is the server address (e.g., 1.1.1.1:53)
|
2022-06-01 11:10:08 +02:00
|
|
|
//
|
|
|
|
// - wrappers is the optional list of wrappers to wrap the underlying
|
|
|
|
// transport. Any nil wrapper will be silently ignored.
|
2022-07-08 19:42:24 +02:00
|
|
|
func NewParallelUDPResolver(logger model.DebugLogger, dialer model.Dialer,
|
2022-06-01 11:10:08 +02:00
|
|
|
address string, wrappers ...model.DNSTransportWrapper) model.Resolver {
|
|
|
|
return WrapResolver(logger, NewUnwrappedParallelResolver(
|
|
|
|
WrapDNSTransport(NewUnwrappedDNSOverUDPTransport(dialer, address), wrappers...),
|
2021-09-28 18:15:38 +02:00
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2021-09-27 12:00:43 +02:00
|
|
|
// WrapResolver creates a new resolver that wraps an
|
|
|
|
// existing resolver to add these properties:
|
2021-09-08 22:48:10 +02:00
|
|
|
//
|
|
|
|
// 1. handles IDNA;
|
|
|
|
//
|
|
|
|
// 2. performs logging;
|
|
|
|
//
|
|
|
|
// 3. short-circuits IP addresses like getaddrinfo does (i.e.,
|
|
|
|
// resolving "1.1.1.1" yields []string{"1.1.1.1"};
|
|
|
|
//
|
|
|
|
// 4. wraps errors;
|
|
|
|
//
|
|
|
|
// 5. enforces reasonable timeouts (
|
|
|
|
// see https://github.com/ooni/probe/issues/1726).
|
2021-09-29 20:21:25 +02:00
|
|
|
//
|
|
|
|
// This is a low-level factory. Use only if out of alternatives.
|
2022-01-03 13:53:23 +01:00
|
|
|
func WrapResolver(logger model.DebugLogger, resolver model.Resolver) model.Resolver {
|
2021-09-05 18:03:50 +02:00
|
|
|
return &resolverIDNA{
|
|
|
|
Resolver: &resolverLogger{
|
2021-09-05 20:12:05 +02:00
|
|
|
Resolver: &resolverShortCircuitIPAddr{
|
2021-09-07 19:56:42 +02:00
|
|
|
Resolver: &resolverErrWrapper{
|
2021-09-27 12:00:43 +02:00
|
|
|
Resolver: resolver,
|
2021-09-07 19:56:42 +02:00
|
|
|
},
|
2021-09-05 20:12:05 +02:00
|
|
|
},
|
2021-09-05 20:41:46 +02:00
|
|
|
Logger: logger,
|
2021-09-05 18:03:50 +02:00
|
|
|
},
|
|
|
|
}
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
// resolverSystem is the system resolver.
|
2021-09-05 18:50:05 +02:00
|
|
|
type resolverSystem struct {
|
2022-06-01 09:59:44 +02:00
|
|
|
t model.DNSTransport
|
2021-09-05 18:50:05 +02:00
|
|
|
}
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.Resolver = &resolverSystem{}
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
func (r *resolverSystem) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
2022-06-01 09:59:44 +02:00
|
|
|
encoder := &DNSEncoderMiekg{}
|
|
|
|
query := encoder.Encode(hostname, dns.TypeANY, false)
|
2022-08-11 10:20:28 +02:00
|
|
|
trace := ContextTraceOrDefault(ctx)
|
|
|
|
start := trace.TimeNow()
|
2022-06-01 09:59:44 +02:00
|
|
|
resp, err := r.t.RoundTrip(ctx, query)
|
2022-08-11 10:20:28 +02:00
|
|
|
end := trace.TimeNow()
|
2022-06-01 09:59:44 +02:00
|
|
|
if err != nil {
|
2022-08-11 10:20:28 +02:00
|
|
|
trace.OnDNSRoundTripForLookupHost(start, r, query, resp, []string{}, err, end)
|
|
|
|
return []string{}, err
|
2021-09-05 18:50:05 +02:00
|
|
|
}
|
2022-08-11 10:20:28 +02:00
|
|
|
addrs, err := resp.DecodeLookupHost()
|
|
|
|
trace.OnDNSRoundTripForLookupHost(start, r, query, resp, addrs, err, end)
|
|
|
|
return addrs, err
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
func (r *resolverSystem) Network() string {
|
2022-06-01 09:59:44 +02:00
|
|
|
return r.t.Network()
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
func (r *resolverSystem) Address() string {
|
2022-06-01 09:59:44 +02:00
|
|
|
return r.t.Address()
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 18:03:50 +02:00
|
|
|
func (r *resolverSystem) CloseIdleConnections() {
|
2022-06-01 09:59:44 +02:00
|
|
|
r.t.CloseIdleConnections()
|
2021-09-05 18:03:50 +02:00
|
|
|
}
|
|
|
|
|
2021-09-27 23:09:41 +02:00
|
|
|
func (r *resolverSystem) LookupHTTPS(
|
2022-01-03 13:53:23 +01:00
|
|
|
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
2021-09-27 23:09:41 +02:00
|
|
|
return nil, ErrNoDNSTransport
|
|
|
|
}
|
|
|
|
|
2022-05-16 10:46:53 +02:00
|
|
|
func (r *resolverSystem) LookupNS(
|
|
|
|
ctx context.Context, domain string) ([]*net.NS, error) {
|
|
|
|
return nil, ErrNoDNSTransport
|
|
|
|
}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
// resolverLogger is a resolver that emits events
|
|
|
|
type resolverLogger struct {
|
2022-05-15 19:25:27 +02:00
|
|
|
Resolver model.Resolver
|
|
|
|
Logger model.DebugLogger
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
}
|
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.Resolver = &resolverLogger{}
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
func (r *resolverLogger) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
2021-09-27 16:48:46 +02:00
|
|
|
prefix := fmt.Sprintf("resolve[A,AAAA] %s with %s (%s)", hostname, r.Network(), r.Address())
|
|
|
|
r.Logger.Debugf("%s...", prefix)
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
start := time.Now()
|
|
|
|
addrs, err := r.Resolver.LookupHost(ctx, hostname)
|
2021-06-23 17:00:44 +02:00
|
|
|
elapsed := time.Since(start)
|
|
|
|
if err != nil {
|
2021-09-27 16:48:46 +02:00
|
|
|
r.Logger.Debugf("%s... %s in %s", prefix, err, elapsed)
|
2021-06-23 17:00:44 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-09-27 16:48:46 +02:00
|
|
|
r.Logger.Debugf("%s... %+v in %s", prefix, addrs, elapsed)
|
2021-06-23 17:00:44 +02:00
|
|
|
return addrs, nil
|
refactor: start pivoting netx (#396)
What do I mean by pivoting? Netx is currently organized by row:
```
| dialer | quicdialer | resolver | ...
saving | | | | ...
errorwrapping | | | | ...
logging | | | | ...
mocking/sys | | | | ...
```
Every row needs to implement saving, errorwrapping, logging, mocking (or
adapting to the system or to some underlying library).
This causes cross package dependencies and, in turn, complexity. For
example, we need the `trace` package for supporting saving.
And `dialer`, `quickdialer`, et al. need to depend on such a package.
The same goes for errorwrapping.
This arrangement further complicates testing. For example, I am
currently working on https://github.com/ooni/probe/issues/1505 and
I realize it need to repeat integration tests in multiple places.
Let's say instead we pivot the above matrix as follows:
```
| saving | errorwrapping | logging | ...
dialer | | | | ...
quicdialer | | | | ...
logging | | | | ...
mocking/sys | | | | ...
...
```
In this way, now every row contains everything related to a specific
action to perform. We can now share code without relying on extra
support packages. What's more, we can write tests and, judding from
the way in which things are made, it seems we only need integration
testing in `errorwrapping` because it's where data quality matters
whereas, in all other cases, unit testing is fine.
I am going, therefore, to proceed with these changes and "pivot"
`netx`. Hopefully, it won't be too painful.
2021-06-23 15:53:12 +02:00
|
|
|
}
|
|
|
|
|
2021-09-27 23:09:41 +02:00
|
|
|
func (r *resolverLogger) LookupHTTPS(
|
2022-01-03 13:53:23 +01:00
|
|
|
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
2021-09-27 23:09:41 +02:00
|
|
|
prefix := fmt.Sprintf("resolve[HTTPS] %s with %s (%s)", domain, r.Network(), r.Address())
|
|
|
|
r.Logger.Debugf("%s...", prefix)
|
|
|
|
start := time.Now()
|
|
|
|
https, err := r.Resolver.LookupHTTPS(ctx, domain)
|
|
|
|
elapsed := time.Since(start)
|
|
|
|
if err != nil {
|
|
|
|
r.Logger.Debugf("%s... %s in %s", prefix, err, elapsed)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
alpn := https.ALPN
|
|
|
|
a := https.IPv4
|
|
|
|
aaaa := https.IPv6
|
|
|
|
r.Logger.Debugf("%s... %+v %+v %+v in %s", prefix, alpn, a, aaaa, elapsed)
|
|
|
|
return https, nil
|
|
|
|
}
|
|
|
|
|
2022-05-15 19:25:27 +02:00
|
|
|
func (r *resolverLogger) Address() string {
|
|
|
|
return r.Resolver.Address()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *resolverLogger) Network() string {
|
|
|
|
return r.Resolver.Network()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *resolverLogger) CloseIdleConnections() {
|
|
|
|
r.Resolver.CloseIdleConnections()
|
|
|
|
}
|
|
|
|
|
2022-05-16 10:46:53 +02:00
|
|
|
func (r *resolverLogger) LookupNS(
|
|
|
|
ctx context.Context, domain string) ([]*net.NS, error) {
|
|
|
|
prefix := fmt.Sprintf("resolve[NS] %s with %s (%s)", domain, r.Network(), r.Address())
|
|
|
|
r.Logger.Debugf("%s...", prefix)
|
|
|
|
start := time.Now()
|
|
|
|
ns, err := r.Resolver.LookupNS(ctx, domain)
|
|
|
|
elapsed := time.Since(start)
|
|
|
|
if err != nil {
|
|
|
|
r.Logger.Debugf("%s... %s in %s", prefix, err, elapsed)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
r.Logger.Debugf("%s... %+v in %s", prefix, ns, elapsed)
|
|
|
|
return ns, nil
|
|
|
|
}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
// resolverIDNA supports resolving Internationalized Domain Names.
|
2021-08-17 11:02:12 +02:00
|
|
|
//
|
2021-08-17 10:29:06 +02:00
|
|
|
// See RFC3492 for more information.
|
2021-09-05 14:49:38 +02:00
|
|
|
type resolverIDNA struct {
|
2022-05-15 19:25:27 +02:00
|
|
|
Resolver model.Resolver
|
2021-08-17 10:29:06 +02:00
|
|
|
}
|
|
|
|
|
2022-05-15 19:25:27 +02:00
|
|
|
var _ model.Resolver = &resolverIDNA{}
|
|
|
|
|
2021-09-05 14:49:38 +02:00
|
|
|
func (r *resolverIDNA) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
2021-08-17 10:29:06 +02:00
|
|
|
host, err := idna.ToASCII(hostname)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return r.Resolver.LookupHost(ctx, host)
|
|
|
|
}
|
2021-09-05 20:12:05 +02:00
|
|
|
|
2021-09-27 23:09:41 +02:00
|
|
|
func (r *resolverIDNA) LookupHTTPS(
|
2022-01-03 13:53:23 +01:00
|
|
|
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
2021-09-27 23:09:41 +02:00
|
|
|
host, err := idna.ToASCII(domain)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return r.Resolver.LookupHTTPS(ctx, host)
|
|
|
|
}
|
|
|
|
|
2022-05-15 19:25:27 +02:00
|
|
|
func (r *resolverIDNA) Network() string {
|
|
|
|
return r.Resolver.Network()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *resolverIDNA) Address() string {
|
|
|
|
return r.Resolver.Address()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *resolverIDNA) CloseIdleConnections() {
|
|
|
|
r.Resolver.CloseIdleConnections()
|
|
|
|
}
|
|
|
|
|
2022-05-16 10:46:53 +02:00
|
|
|
func (r *resolverIDNA) LookupNS(
|
|
|
|
ctx context.Context, domain string) ([]*net.NS, error) {
|
|
|
|
host, err := idna.ToASCII(domain)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return r.Resolver.LookupNS(ctx, host)
|
|
|
|
}
|
|
|
|
|
2021-09-05 20:12:05 +02:00
|
|
|
// resolverShortCircuitIPAddr recognizes when the input hostname is an
|
|
|
|
// IP address and returns it immediately to the caller.
|
|
|
|
type resolverShortCircuitIPAddr struct {
|
2022-05-15 19:25:27 +02:00
|
|
|
Resolver model.Resolver
|
2021-09-05 20:12:05 +02:00
|
|
|
}
|
|
|
|
|
2022-05-15 19:25:27 +02:00
|
|
|
var _ model.Resolver = &resolverShortCircuitIPAddr{}
|
|
|
|
|
2021-09-05 20:12:05 +02:00
|
|
|
func (r *resolverShortCircuitIPAddr) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
|
|
|
if net.ParseIP(hostname) != nil {
|
|
|
|
return []string{hostname}, nil
|
|
|
|
}
|
|
|
|
return r.Resolver.LookupHost(ctx, hostname)
|
|
|
|
}
|
2021-09-05 20:41:46 +02:00
|
|
|
|
2022-05-13 18:26:15 +02:00
|
|
|
func (r *resolverShortCircuitIPAddr) LookupHTTPS(ctx context.Context, hostname string) (*model.HTTPSSvc, error) {
|
|
|
|
if net.ParseIP(hostname) != nil {
|
|
|
|
https := &model.HTTPSSvc{}
|
|
|
|
if isIPv6(hostname) {
|
|
|
|
https.IPv6 = append(https.IPv6, hostname)
|
|
|
|
} else {
|
|
|
|
https.IPv4 = append(https.IPv4, hostname)
|
|
|
|
}
|
|
|
|
return https, nil
|
|
|
|
}
|
|
|
|
return r.Resolver.LookupHTTPS(ctx, hostname)
|
|
|
|
}
|
|
|
|
|
2022-05-15 19:25:27 +02:00
|
|
|
func (r *resolverShortCircuitIPAddr) Network() string {
|
|
|
|
return r.Resolver.Network()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *resolverShortCircuitIPAddr) Address() string {
|
|
|
|
return r.Resolver.Address()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *resolverShortCircuitIPAddr) CloseIdleConnections() {
|
|
|
|
r.Resolver.CloseIdleConnections()
|
|
|
|
}
|
|
|
|
|
2022-05-16 10:46:53 +02:00
|
|
|
// ErrDNSIPAddress indicates that you passed an IP address to a DNS
|
|
|
|
// function that only works with domain names.
|
|
|
|
var ErrDNSIPAddress = errors.New("ooresolver: expected domain, found IP address")
|
|
|
|
|
|
|
|
func (r *resolverShortCircuitIPAddr) LookupNS(
|
|
|
|
ctx context.Context, hostname string) ([]*net.NS, error) {
|
|
|
|
if net.ParseIP(hostname) != nil {
|
|
|
|
return nil, ErrDNSIPAddress
|
|
|
|
}
|
|
|
|
return r.Resolver.LookupNS(ctx, hostname)
|
|
|
|
}
|
|
|
|
|
2022-05-13 15:32:47 +02:00
|
|
|
// IsIPv6 returns true if the given candidate is a valid IP address
|
|
|
|
// representation and such representation is IPv6.
|
|
|
|
func IsIPv6(candidate string) (bool, error) {
|
|
|
|
if net.ParseIP(candidate) == nil {
|
|
|
|
return false, ErrInvalidIP
|
|
|
|
}
|
|
|
|
return isIPv6(candidate), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// isIPv6 returns true if the given IP address is IPv6.
|
|
|
|
func isIPv6(candidate string) bool {
|
|
|
|
// This check for identifying IPv6 is discussed
|
|
|
|
// at https://stackoverflow.com/questions/22751035
|
|
|
|
// and seems good-enough for our purposes.
|
|
|
|
return strings.Contains(candidate, ":")
|
|
|
|
}
|
|
|
|
|
2021-09-29 20:21:25 +02:00
|
|
|
// ErrNoResolver is the type of error returned by "without resolver"
|
|
|
|
// dialer when asked to dial for and endpoint containing a domain name,
|
|
|
|
// since they can only dial for endpoints containing IP addresses.
|
2021-09-05 20:41:46 +02:00
|
|
|
var ErrNoResolver = errors.New("no configured resolver")
|
|
|
|
|
2022-05-31 20:02:11 +02:00
|
|
|
// NullResolver is a resolver that is not capable of resolving
|
2021-09-05 20:41:46 +02:00
|
|
|
// domain names to IP addresses and always returns ErrNoResolver.
|
2022-05-31 20:02:11 +02:00
|
|
|
type NullResolver struct{}
|
2021-09-05 20:41:46 +02:00
|
|
|
|
2022-05-31 20:02:11 +02:00
|
|
|
func (r *NullResolver) LookupHost(ctx context.Context, hostname string) (addrs []string, err error) {
|
2021-09-05 20:41:46 +02:00
|
|
|
return nil, ErrNoResolver
|
|
|
|
}
|
|
|
|
|
2022-05-31 20:02:11 +02:00
|
|
|
func (r *NullResolver) Network() string {
|
2021-09-05 20:41:46 +02:00
|
|
|
return "null"
|
|
|
|
}
|
|
|
|
|
2022-05-31 20:02:11 +02:00
|
|
|
func (r *NullResolver) Address() string {
|
2021-09-05 20:41:46 +02:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2022-05-31 20:02:11 +02:00
|
|
|
func (r *NullResolver) CloseIdleConnections() {
|
2021-09-08 22:48:10 +02:00
|
|
|
// nothing to do
|
2021-09-05 20:41:46 +02:00
|
|
|
}
|
2021-09-07 19:56:42 +02:00
|
|
|
|
2022-05-31 20:02:11 +02:00
|
|
|
func (r *NullResolver) LookupHTTPS(
|
2022-01-03 13:53:23 +01:00
|
|
|
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
2021-09-27 23:09:41 +02:00
|
|
|
return nil, ErrNoResolver
|
|
|
|
}
|
|
|
|
|
2022-05-31 20:02:11 +02:00
|
|
|
func (r *NullResolver) LookupNS(
|
2022-05-16 10:46:53 +02:00
|
|
|
ctx context.Context, domain string) ([]*net.NS, error) {
|
|
|
|
return nil, ErrNoResolver
|
|
|
|
}
|
|
|
|
|
2021-09-07 19:56:42 +02:00
|
|
|
// resolverErrWrapper is a Resolver that knows about wrapping errors.
|
|
|
|
type resolverErrWrapper struct {
|
2022-05-15 19:25:27 +02:00
|
|
|
Resolver model.Resolver
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|
|
|
|
|
2022-01-03 13:53:23 +01:00
|
|
|
var _ model.Resolver = &resolverErrWrapper{}
|
2021-09-07 19:56:42 +02:00
|
|
|
|
|
|
|
func (r *resolverErrWrapper) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
|
|
|
addrs, err := r.Resolver.LookupHost(ctx, hostname)
|
|
|
|
if err != nil {
|
2022-07-01 12:22:22 +02:00
|
|
|
return nil, NewErrWrapper(ClassifyResolverError, ResolveOperation, err)
|
2021-09-07 19:56:42 +02:00
|
|
|
}
|
|
|
|
return addrs, nil
|
|
|
|
}
|
2021-09-27 23:09:41 +02:00
|
|
|
|
|
|
|
func (r *resolverErrWrapper) LookupHTTPS(
|
2022-01-03 13:53:23 +01:00
|
|
|
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
2021-09-27 23:09:41 +02:00
|
|
|
out, err := r.Resolver.LookupHTTPS(ctx, domain)
|
|
|
|
if err != nil {
|
2022-07-01 12:22:22 +02:00
|
|
|
return nil, NewErrWrapper(ClassifyResolverError, ResolveOperation, err)
|
2021-09-27 23:09:41 +02:00
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
2022-05-15 19:25:27 +02:00
|
|
|
|
|
|
|
func (r *resolverErrWrapper) Network() string {
|
|
|
|
return r.Resolver.Network()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *resolverErrWrapper) Address() string {
|
|
|
|
return r.Resolver.Address()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *resolverErrWrapper) CloseIdleConnections() {
|
|
|
|
r.Resolver.CloseIdleConnections()
|
|
|
|
}
|
2022-05-16 10:46:53 +02:00
|
|
|
|
|
|
|
func (r *resolverErrWrapper) LookupNS(
|
|
|
|
ctx context.Context, domain string) ([]*net.NS, error) {
|
|
|
|
out, err := r.Resolver.LookupNS(ctx, domain)
|
|
|
|
if err != nil {
|
2022-07-01 12:22:22 +02:00
|
|
|
return nil, NewErrWrapper(ClassifyResolverError, ResolveOperation, err)
|
2022-05-16 10:46:53 +02:00
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|