Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cleanup(sessionresolver): don't depend on netx #1068

Merged
merged 9 commits into from
Feb 1, 2023
6 changes: 3 additions & 3 deletions internal/bytecounter/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
// MaybeWrapHTTPTransport takes in input an HTTPTransport and either wraps it
// to perform byte counting, if this counter is not nil, or just returns to the
// caller the original transport, when the counter is nil.
func (c *Counter) MaybeWrapHTTPTransport(txp model.HTTPTransport) model.HTTPTransport {
if c != nil {
txp = WrapHTTPTransport(txp, c)
func MaybeWrapHTTPTransport(txp model.HTTPTransport, counter *Counter) model.HTTPTransport {
if counter != nil {
txp = WrapHTTPTransport(txp, counter)
}
return txp
}
Expand Down
4 changes: 2 additions & 2 deletions internal/bytecounter/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestMaybeWrapHTTPTransport(t *testing.T) {
t.Run("when counter is not nil", func(t *testing.T) {
underlying := &mocks.HTTPTransport{}
counter := &Counter{}
txp := counter.MaybeWrapHTTPTransport(underlying)
txp := MaybeWrapHTTPTransport(underlying, counter)
realTxp := txp.(*httpTransport)
if realTxp.HTTPTransport != underlying {
t.Fatal("did not wrap correctly")
Expand All @@ -26,7 +26,7 @@ func TestMaybeWrapHTTPTransport(t *testing.T) {
t.Run("when counter is nil", func(t *testing.T) {
underlying := &mocks.HTTPTransport{}
var counter *Counter
txp := counter.MaybeWrapHTTPTransport(underlying)
txp := MaybeWrapHTTPTransport(underlying, counter)
if txp != underlying {
t.Fatal("unexpected result")
}
Expand Down
110 changes: 110 additions & 0 deletions internal/bytecounter/resolver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package bytecounter

import (
"context"
"net"

"github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/netxlite"
)

// MaybeWrapSystemResolver takes in input a Resolver and either wraps it
// to perform byte counting, if this counter is not nil, or just returns to the
// caller the original resolver, when the counter is nil.
//
// # Bug
//
// The returned resolver will only approximately estimate the bytes
// sent and received by this resolver if this resolver is the system
// resolver. For more accurate counting when using DNS over HTTPS,
// you should instead count at the HTTP transport level. If you are
// using DNS over TCP, DNS over TLS, or DNS over UDP, you should instead
// count the bytes by just wrapping the connections you're using.
func MaybeWrapSystemResolver(reso model.Resolver, counter *Counter) model.Resolver {
if counter != nil {
reso = WrapSystemResolver(reso, counter)
}
return reso
}

// WrapSystemResolver creates a new byte-counting-aware resolver. This function
// returns a resolver with the same bugs of [MaybeWrapSystemResolver].
func WrapSystemResolver(reso model.Resolver, counter *Counter) model.Resolver {
return &resolver{
Resolver: reso,
Counter: counter,
}
}

// resolver is the type returned by WrapResolver.
type resolver struct {
Resolver model.Resolver
Counter *Counter
}

// Address implements model.Resolver
func (r *resolver) Address() string {
return r.Resolver.Address()
}

// CloseIdleConnections implements model.Resolver
func (r *resolver) CloseIdleConnections() {
r.Resolver.CloseIdleConnections()
}

// LookupHTTPS implements model.Resolver
func (r *resolver) LookupHTTPS(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
r.updateCounterBytesSent(domain, 1)
out, err := r.Resolver.LookupHTTPS(ctx, domain)
r.updateCounterBytesRecv(err)
return out, err
}

// LookupHost implements model.Resolver
func (r *resolver) LookupHost(ctx context.Context, hostname string) (addrs []string, err error) {
r.updateCounterBytesSent(hostname, 2)
out, err := r.Resolver.LookupHost(ctx, hostname)
r.updateCounterBytesRecv(err)
return out, err
}

// LookupNS implements model.Resolver
func (r *resolver) LookupNS(ctx context.Context, domain string) ([]*net.NS, error) {
r.updateCounterBytesSent(domain, 1)
out, err := r.Resolver.LookupNS(ctx, domain)
r.updateCounterBytesRecv(err)
return out, err
}

// Network implements model.Resolver
func (r *resolver) Network() string {
return r.Resolver.Network()
}

// updateCounterBytesSent estimates the bytes sent.
func (r *resolver) updateCounterBytesSent(domain string, n int) {
// Assume we are sending N queries for the given domain, which is the
// correct byte counting strategy when using the system resolver
r.Counter.Sent.Add(int64(n * len(domain)))
}

// updateCounterBytesRecv estimates the bytes recv.
func (r *resolver) updateCounterBytesRecv(err error) {
if err != nil {
switch err.Error() {
case netxlite.FailureDNSNXDOMAINError,
netxlite.FailureDNSNoAnswer,
netxlite.FailureDNSRefusedError,
netxlite.FailureDNSNonRecoverableFailure,
netxlite.FailureDNSServfailError:
// In case it seems we received a message, let us
// pretend overall it was 128 bytes
r.Counter.Received.Add(128)
default:
// In this case we assume we did not receive any byte
}
return
}
// On success we assume we received 256 bytes
r.Counter.Received.Add(256)
}
Loading