fix(netxlite/dns): more stricly mirror stdlib error strings (#513)

This diff attempts to modify the errors reported by our custom
resolver by matching more strings from the stdlib.

Part of https://github.com/ooni/probe/issues/1733 and diff has been
extracted from https://github.com/ooni/probe-cli/pull/506.
This commit is contained in:
Simone Basso 2021-09-27 16:48:46 +02:00 committed by GitHub
parent 201f602a40
commit 24b230fd38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 177 additions and 52 deletions

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// 2021-09-07 12:37:42.667616 +0200 CEST m=+1.355122543 // 2021-09-27 15:57:57.877144 +0200 CEST m=+1.353309960
// https://curl.haxx.se/ca/cacert.pem // https://curl.haxx.se/ca/cacert.pem
package netxlite package netxlite

View File

@ -1,34 +1,42 @@
package dnsx package dnsx
import ( import (
"errors"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/ooni/probe-cli/v3/internal/netxlite/errorsx"
) )
// The Decoder decodes a DNS reply into A or AAAA entries. It will use the // The Decoder decodes DNS replies.
// provided qtype and only look for mathing entries. It will return error if
// there are no entries for the requested qtype inside the reply.
type Decoder interface { type Decoder interface {
Decode(qtype uint16, data []byte) ([]string, error) // DecodeLookupHost decodes an A or AAAA reply.
DecodeLookupHost(qtype uint16, data []byte) ([]string, error)
} }
// MiekgDecoder uses github.com/miekg/dns to implement the Decoder. // MiekgDecoder uses github.com/miekg/dns to implement the Decoder.
type MiekgDecoder struct{} type MiekgDecoder struct{}
// Decode implements Decoder.Decode. func (d *MiekgDecoder) parseReply(data []byte) (*dns.Msg, error) {
func (d *MiekgDecoder) Decode(qtype uint16, data []byte) ([]string, error) {
reply := new(dns.Msg) reply := new(dns.Msg)
if err := reply.Unpack(data); err != nil { if err := reply.Unpack(data); err != nil {
return nil, err return nil, err
} }
// TODO(bassosimone): map more errors to net.DNSError names // TODO(bassosimone): map more errors to net.DNSError names
// TODO(bassosimone): add support for lame referral.
switch reply.Rcode { switch reply.Rcode {
case dns.RcodeSuccess: case dns.RcodeSuccess:
return reply, nil
case dns.RcodeNameError: case dns.RcodeNameError:
return nil, errors.New("ooniresolver: no such host") return nil, errorsx.ErrOODNSNoSuchHost
case dns.RcodeRefused:
return nil, errorsx.ErrOODNSRefused
default: default:
return nil, errors.New("ooniresolver: query failed") return nil, errorsx.ErrOODNSMisbehaving
}
}
func (d *MiekgDecoder) DecodeLookupHost(qtype uint16, data []byte) ([]string, error) {
reply, err := d.parseReply(data)
if err != nil {
return nil, err
} }
var addrs []string var addrs []string
for _, answer := range reply.Answer { for _, answer := range reply.Answer {
@ -46,7 +54,7 @@ func (d *MiekgDecoder) Decode(qtype uint16, data []byte) ([]string, error) {
} }
} }
if len(addrs) <= 0 { if len(addrs) <= 0 {
return nil, errors.New("ooniresolver: no response returned") return nil, errorsx.ErrOODNSNoAnswer
} }
return addrs, nil return addrs, nil
} }

View File

@ -1,16 +1,18 @@
package dnsx package dnsx
import ( import (
"errors"
"net" "net"
"strings" "strings"
"testing" "testing"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/ooni/probe-cli/v3/internal/netxlite/errorsx"
) )
func TestDecoderUnpackError(t *testing.T) { func TestDecoderUnpackError(t *testing.T) {
d := &MiekgDecoder{} d := &MiekgDecoder{}
data, err := d.Decode(dns.TypeA, nil) data, err := d.DecodeLookupHost(dns.TypeA, nil)
if err == nil { if err == nil {
t.Fatal("expected an error here") t.Fatal("expected an error here")
} }
@ -21,20 +23,20 @@ func TestDecoderUnpackError(t *testing.T) {
func TestDecoderNXDOMAIN(t *testing.T) { func TestDecoderNXDOMAIN(t *testing.T) {
d := &MiekgDecoder{} d := &MiekgDecoder{}
data, err := d.Decode(dns.TypeA, genReplyError(t, dns.RcodeNameError)) data, err := d.DecodeLookupHost(dns.TypeA, genReplyError(t, dns.RcodeNameError))
if err == nil || !strings.HasSuffix(err.Error(), "no such host") { if err == nil || !strings.HasSuffix(err.Error(), "no such host") {
t.Fatal("not the error we expected") t.Fatal("not the error we expected", err)
} }
if data != nil { if data != nil {
t.Fatal("expected nil data here") t.Fatal("expected nil data here")
} }
} }
func TestDecoderOtherError(t *testing.T) { func TestDecoderRefusedError(t *testing.T) {
d := &MiekgDecoder{} d := &MiekgDecoder{}
data, err := d.Decode(dns.TypeA, genReplyError(t, dns.RcodeRefused)) data, err := d.DecodeLookupHost(dns.TypeA, genReplyError(t, dns.RcodeRefused))
if err == nil || !strings.HasSuffix(err.Error(), "query failed") { if !errors.Is(err, errorsx.ErrOODNSRefused) {
t.Fatal("not the error we expected") t.Fatal("not the error we expected", err)
} }
if data != nil { if data != nil {
t.Fatal("expected nil data here") t.Fatal("expected nil data here")
@ -43,9 +45,9 @@ func TestDecoderOtherError(t *testing.T) {
func TestDecoderNoAddress(t *testing.T) { func TestDecoderNoAddress(t *testing.T) {
d := &MiekgDecoder{} d := &MiekgDecoder{}
data, err := d.Decode(dns.TypeA, genReplySuccess(t, dns.TypeA)) data, err := d.DecodeLookupHost(dns.TypeA, genReplySuccess(t, dns.TypeA))
if err == nil || !strings.HasSuffix(err.Error(), "no response returned") { if !errors.Is(err, errorsx.ErrOODNSNoAnswer) {
t.Fatal("not the error we expected") t.Fatal("not the error we expected", err)
} }
if data != nil { if data != nil {
t.Fatal("expected nil data here") t.Fatal("expected nil data here")
@ -54,7 +56,7 @@ func TestDecoderNoAddress(t *testing.T) {
func TestDecoderDecodeA(t *testing.T) { func TestDecoderDecodeA(t *testing.T) {
d := &MiekgDecoder{} d := &MiekgDecoder{}
data, err := d.Decode( data, err := d.DecodeLookupHost(
dns.TypeA, genReplySuccess(t, dns.TypeA, "1.1.1.1", "8.8.8.8")) dns.TypeA, genReplySuccess(t, dns.TypeA, "1.1.1.1", "8.8.8.8"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -72,7 +74,7 @@ func TestDecoderDecodeA(t *testing.T) {
func TestDecoderDecodeAAAA(t *testing.T) { func TestDecoderDecodeAAAA(t *testing.T) {
d := &MiekgDecoder{} d := &MiekgDecoder{}
data, err := d.Decode( data, err := d.DecodeLookupHost(
dns.TypeAAAA, genReplySuccess(t, dns.TypeAAAA, "::1", "fe80::1")) dns.TypeAAAA, genReplySuccess(t, dns.TypeAAAA, "::1", "fe80::1"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -90,10 +92,10 @@ func TestDecoderDecodeAAAA(t *testing.T) {
func TestDecoderUnexpectedAReply(t *testing.T) { func TestDecoderUnexpectedAReply(t *testing.T) {
d := &MiekgDecoder{} d := &MiekgDecoder{}
data, err := d.Decode( data, err := d.DecodeLookupHost(
dns.TypeA, genReplySuccess(t, dns.TypeAAAA, "::1", "fe80::1")) dns.TypeA, genReplySuccess(t, dns.TypeAAAA, "::1", "fe80::1"))
if err == nil || !strings.HasSuffix(err.Error(), "no response returned") { if !errors.Is(err, errorsx.ErrOODNSNoAnswer) {
t.Fatal("not the error we expected") t.Fatal("not the error we expected", err)
} }
if data != nil { if data != nil {
t.Fatal("expected nil data here") t.Fatal("expected nil data here")
@ -102,10 +104,10 @@ func TestDecoderUnexpectedAReply(t *testing.T) {
func TestDecoderUnexpectedAAAAReply(t *testing.T) { func TestDecoderUnexpectedAAAAReply(t *testing.T) {
d := &MiekgDecoder{} d := &MiekgDecoder{}
data, err := d.Decode( data, err := d.DecodeLookupHost(
dns.TypeAAAA, genReplySuccess(t, dns.TypeA, "1.1.1.1", "8.8.4.4.")) dns.TypeAAAA, genReplySuccess(t, dns.TypeA, "1.1.1.1", "8.8.4.4."))
if err == nil || !strings.HasSuffix(err.Error(), "no response returned") { if !errors.Is(err, errorsx.ErrOODNSNoAnswer) {
t.Fatal("not the error we expected") t.Fatal("not the error we expected", err)
} }
if data != nil { if data != nil {
t.Fatal("expected nil data here") t.Fatal("expected nil data here")
@ -179,3 +181,20 @@ func genReplySuccess(t *testing.T, qtype uint16, ips ...string) []byte {
} }
return data return data
} }
func TestParseReply(t *testing.T) {
d := &MiekgDecoder{}
msg := &dns.Msg{}
msg.Rcode = dns.RcodeFormatError // an rcode we don't handle
data, err := msg.Pack()
if err != nil {
t.Fatal(err)
}
reply, err := d.parseReply(data)
if !errors.Is(err, errorsx.ErrOODNSMisbehaving) { // catch all error
t.Fatal("not the error we expected", err)
}
if reply != nil {
t.Fatal("expected nil reply")
}
}

View File

@ -27,14 +27,14 @@ type DNSOverHTTPS struct {
// NewDNSOverHTTPS creates a new DNSOverHTTP instance from the // NewDNSOverHTTPS creates a new DNSOverHTTP instance from the
// specified http.Client and URL, as a convenience. // specified http.Client and URL, as a convenience.
func NewDNSOverHTTPS(client *http.Client, URL string) *DNSOverHTTPS { func NewDNSOverHTTPS(client HTTPClient, URL string) *DNSOverHTTPS {
return NewDNSOverHTTPSWithHostOverride(client, URL, "") return NewDNSOverHTTPSWithHostOverride(client, URL, "")
} }
// NewDNSOverHTTPSWithHostOverride is like NewDNSOverHTTPS except that // NewDNSOverHTTPSWithHostOverride is like NewDNSOverHTTPS except that
// it's creating a resolver where we use the specified host. // it's creating a resolver where we use the specified host.
func NewDNSOverHTTPSWithHostOverride( func NewDNSOverHTTPSWithHostOverride(
client *http.Client, URL, hostOverride string) *DNSOverHTTPS { client HTTPClient, URL, hostOverride string) *DNSOverHTTPS {
return &DNSOverHTTPS{Client: client, URL: URL, HostOverride: hostOverride} return &DNSOverHTTPS{Client: client, URL: URL, HostOverride: hostOverride}
} }

View File

@ -175,3 +175,18 @@ func TestDNSOverHTTPSHostOverride(t *testing.T) {
t.Fatal("did not see correct host override") t.Fatal("did not see correct host override")
} }
} }
func TestDNSOverHTTPSCloseIdleConnections(t *testing.T) {
var called bool
doh := &DNSOverHTTPS{
Client: &mocks.HTTPClient{
MockCloseIdleConnections: func() {
called = true
},
},
}
doh.CloseIdleConnections()
if !called {
t.Fatal("not called")
}
}

View File

@ -51,8 +51,8 @@ func (r *SerialResolver) CloseIdleConnections() {
// LookupHost implements Resolver.LookupHost. // LookupHost implements Resolver.LookupHost.
func (r *SerialResolver) LookupHost(ctx context.Context, hostname string) ([]string, error) { func (r *SerialResolver) LookupHost(ctx context.Context, hostname string) ([]string, error) {
var addrs []string var addrs []string
addrsA, errA := r.roundTripWithRetry(ctx, hostname, dns.TypeA) addrsA, errA := r.lookupHostWithRetry(ctx, hostname, dns.TypeA)
addrsAAAA, errAAAA := r.roundTripWithRetry(ctx, hostname, dns.TypeAAAA) addrsAAAA, errAAAA := r.lookupHostWithRetry(ctx, hostname, dns.TypeAAAA)
if errA != nil && errAAAA != nil { if errA != nil && errAAAA != nil {
return nil, errA return nil, errA
} }
@ -61,11 +61,11 @@ func (r *SerialResolver) LookupHost(ctx context.Context, hostname string) ([]str
return addrs, nil return addrs, nil
} }
func (r *SerialResolver) roundTripWithRetry( func (r *SerialResolver) lookupHostWithRetry(
ctx context.Context, hostname string, qtype uint16) ([]string, error) { ctx context.Context, hostname string, qtype uint16) ([]string, error) {
var errorslist []error var errorslist []error
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
replies, err := r.roundTrip(ctx, hostname, qtype) replies, err := r.lookupHostWithoutRetry(ctx, hostname, qtype)
if err == nil { if err == nil {
return replies, nil return replies, nil
} }
@ -87,7 +87,9 @@ func (r *SerialResolver) roundTripWithRetry(
return nil, errorslist[0] return nil, errorslist[0]
} }
func (r *SerialResolver) roundTrip( // lookupHostWithoutRetry issues a lookup host query for the specified
// qtype (dns.A or dns.AAAA) without retrying on failure.
func (r *SerialResolver) lookupHostWithoutRetry(
ctx context.Context, hostname string, qtype uint16) ([]string, error) { ctx context.Context, hostname string, qtype uint16) ([]string, error) {
querydata, err := r.Encoder.Encode(hostname, qtype, r.Txp.RequiresPadding()) querydata, err := r.Encoder.Encode(hostname, qtype, r.Txp.RequiresPadding())
if err != nil { if err != nil {
@ -97,5 +99,5 @@ func (r *SerialResolver) roundTrip(
if err != nil { if err != nil {
return nil, err return nil, err
} }
return r.Decoder.Decode(qtype, replydata) return r.Decoder.DecodeLookupHost(qtype, replydata)
} }

View File

@ -5,7 +5,6 @@ import (
"crypto/tls" "crypto/tls"
"errors" "errors"
"net" "net"
"strings"
"testing" "testing"
"github.com/miekg/dns" "github.com/miekg/dns"
@ -79,8 +78,8 @@ func TestOONIWithEmptyReply(t *testing.T) {
} }
r := NewSerialResolver(txp) r := NewSerialResolver(txp)
addrs, err := r.LookupHost(context.Background(), "www.gogle.com") addrs, err := r.LookupHost(context.Background(), "www.gogle.com")
if err == nil || !strings.HasSuffix(err.Error(), "no response returned") { if !errors.Is(err, errorsx.ErrOODNSNoAnswer) {
t.Fatal("not the error we expected") t.Fatal("not the error we expected", err)
} }
if addrs != nil { if addrs != nil {
t.Fatal("expected nil address here") t.Fatal("expected nil address here")
@ -146,3 +145,18 @@ func TestOONIWithTimeout(t *testing.T) {
t.Fatal("we didn't actually take the timeouts") t.Fatal("we didn't actually take the timeouts")
} }
} }
func TestSerialResolverCloseIdleConnections(t *testing.T) {
var called bool
r := &SerialResolver{
Txp: &mocks.RoundTripper{
MockCloseIdleConnections: func() {
called = true
},
},
}
r.CloseIdleConnections()
if !called {
t.Fatal("not called")
}
}

View File

@ -84,12 +84,18 @@ func classifyWithStringSuffix(err error) string {
if strings.HasSuffix(s, "TLS handshake timeout") { if strings.HasSuffix(s, "TLS handshake timeout") {
return FailureGenericTimeoutError return FailureGenericTimeoutError
} }
if strings.HasSuffix(s, "no such host") { if strings.HasSuffix(s, DNSNoSuchHostSuffix) {
// This is dns_lookup_error in MK but such error is used as a // This is dns_lookup_error in MK but such error is used as a
// generic "hey, the lookup failed" error. Instead, this error // generic "hey, the lookup failed" error. Instead, this error
// that we return here is significantly more specific. // that we return here is significantly more specific.
return FailureDNSNXDOMAINError return FailureDNSNXDOMAINError
} }
if strings.HasSuffix(s, DNSServerMisbehavingSuffix) {
return FailureDNSServerMisbehaving
}
if strings.HasSuffix(s, DNSNoAnswerSuffix) {
return FailureDNSNoAnswer
}
if strings.HasSuffix(s, "use of closed network connection") { if strings.HasSuffix(s, "use of closed network connection") {
return FailureConnectionAlreadyClosed return FailureConnectionAlreadyClosed
} }
@ -223,6 +229,21 @@ func quicIsCertificateError(alert uint8) bool {
// filters for DNS bogons MUST use this error. // filters for DNS bogons MUST use this error.
var ErrDNSBogon = errors.New("dns: detected bogon address") var ErrDNSBogon = errors.New("dns: detected bogon address")
// These strings are same as the standard library.
const (
DNSNoSuchHostSuffix = "no such host"
DNSServerMisbehavingSuffix = "server misbehaving"
DNSNoAnswerSuffix = "no answer from DNS server"
)
// These errors are returned by the decoder and/or the serial resolver.
var (
ErrOODNSNoSuchHost = fmt.Errorf("ooniresolver: %s", DNSNoSuchHostSuffix)
ErrOODNSRefused = errors.New("ooniresolver: refused")
ErrOODNSMisbehaving = fmt.Errorf("ooniresolver: %s", DNSServerMisbehavingSuffix)
ErrOODNSNoAnswer = fmt.Errorf("ooniresolver: %s", DNSNoAnswerSuffix)
)
// ClassifyResolverError maps an error occurred during a domain name // ClassifyResolverError maps an error occurred during a domain name
// resolution to the corresponding OONI failure string. // resolution to the corresponding OONI failure string.
// //
@ -239,6 +260,11 @@ func ClassifyResolverError(err error) string {
if errors.Is(err, ErrDNSBogon) { if errors.Is(err, ErrDNSBogon) {
return FailureDNSBogonError // not in MK return FailureDNSBogonError // not in MK
} }
// Implementation note: we match errors that share the same
// string of the stdlib in the generic classifier.
if errors.Is(err, ErrOODNSRefused) {
return FailureDNSRefusedError // not in MK
}
return ClassifyGenericError(err) return ClassifyGenericError(err)
} }

View File

@ -77,6 +77,18 @@ func TestClassifyGenericError(t *testing.T) {
} }
}) })
t.Run("for dns server misbehaving", func(t *testing.T) {
if ClassifyGenericError(errors.New("dns server misbehaving")) != FailureDNSServerMisbehaving {
t.Fatal("unexpected results")
}
})
t.Run("for no answer from DNS server", func(t *testing.T) {
if ClassifyGenericError(errors.New("no answer from DNS server")) != FailureDNSNoAnswer {
t.Fatal("unexpected results")
}
})
t.Run("for use of closed network connection", func(t *testing.T) { t.Run("for use of closed network connection", func(t *testing.T) {
err := errors.New("read tcp 10.0.2.15:56948->93.184.216.34:443: use of closed network connection") err := errors.New("read tcp 10.0.2.15:56948->93.184.216.34:443: use of closed network connection")
if ClassifyGenericError(err) != FailureConnectionAlreadyClosed { if ClassifyGenericError(err) != FailureConnectionAlreadyClosed {
@ -251,6 +263,12 @@ func TestClassifyResolverError(t *testing.T) {
} }
}) })
t.Run("for refused", func(t *testing.T) {
if ClassifyResolverError(ErrOODNSRefused) != FailureDNSRefusedError {
t.Fatal("unexpected result")
}
})
t.Run("for another kind of error", func(t *testing.T) { t.Run("for another kind of error", func(t *testing.T) {
if ClassifyResolverError(io.EOF) != FailureEOFError { if ClassifyResolverError(io.EOF) != FailureEOFError {
t.Fatal("unexpected result") t.Fatal("unexpected result")

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-27 14:20:26.411929 +0200 CEST m=+0.233208042 // Generated: 2021-09-27 15:57:58.500515 +0200 CEST m=+0.304199251
package errorsx package errorsx
@ -50,6 +50,9 @@ const (
// //
FailureDNSBogonError = "dns_bogon_error" FailureDNSBogonError = "dns_bogon_error"
FailureDNSNXDOMAINError = "dns_nxdomain_error" FailureDNSNXDOMAINError = "dns_nxdomain_error"
FailureDNSRefusedError = "dns_refused_error"
FailureDNSServerMisbehaving = "dns_server_misbehaving"
FailureDNSNoAnswer = "dns_no_answer"
FailureEOFError = "eof_error" FailureEOFError = "eof_error"
FailureGenericTimeoutError = "generic_timeout_error" FailureGenericTimeoutError = "generic_timeout_error"
FailureQUICIncompatibleVersion = "quic_incompatible_version" FailureQUICIncompatibleVersion = "quic_incompatible_version"
@ -94,6 +97,9 @@ var failuresMap = map[string]string{
"wrong_protocol_type": "wrong_protocol_type", "wrong_protocol_type": "wrong_protocol_type",
"dns_bogon_error": "dns_bogon_error", "dns_bogon_error": "dns_bogon_error",
"dns_nxdomain_error": "dns_nxdomain_error", "dns_nxdomain_error": "dns_nxdomain_error",
"dns_refused_error": "dns_refused_error",
"dns_server_misbehaving": "dns_server_misbehaving",
"dns_no_answer": "dns_no_answer",
"eof_error": "eof_error", "eof_error": "eof_error",
"generic_timeout_error": "generic_timeout_error", "generic_timeout_error": "generic_timeout_error",
"quic_incompatible_version": "quic_incompatible_version", "quic_incompatible_version": "quic_incompatible_version",

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-27 14:20:26.459041 +0200 CEST m=+0.280321626 // Generated: 2021-09-27 15:57:58.553101 +0200 CEST m=+0.356786917
package errorsx package errorsx

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-27 14:20:26.179327 +0200 CEST m=+0.000601459 // Generated: 2021-09-27 15:57:58.197535 +0200 CEST m=+0.001212709
package errorsx package errorsx

View File

@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT. // Code generated by go generate; DO NOT EDIT.
// Generated: 2021-09-27 14:20:26.380511 +0200 CEST m=+0.201789292 // Generated: 2021-09-27 15:57:58.455744 +0200 CEST m=+0.259427834
package errorsx package errorsx

View File

@ -93,6 +93,9 @@ var Specs = []*ErrorSpec{
// we must write "DNS" rather than writing "dns". // we must write "DNS" rather than writing "dns".
NewLibraryError("DNS_bogon_error"), NewLibraryError("DNS_bogon_error"),
NewLibraryError("DNS_NXDOMAIN_error"), NewLibraryError("DNS_NXDOMAIN_error"),
NewLibraryError("DNS_refused_error"),
NewLibraryError("DNS_server_misbehaving"),
NewLibraryError("DNS_no_answer"),
NewLibraryError("EOF_error"), NewLibraryError("EOF_error"),
NewLibraryError("generic_timeout_error"), NewLibraryError("generic_timeout_error"),
NewLibraryError("QUIC_incompatible_version"), NewLibraryError("QUIC_incompatible_version"),

View File

@ -3,6 +3,7 @@ package netxlite
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"net" "net"
"time" "time"
@ -128,15 +129,16 @@ type resolverLogger struct {
var _ Resolver = &resolverLogger{} var _ Resolver = &resolverLogger{}
func (r *resolverLogger) LookupHost(ctx context.Context, hostname string) ([]string, error) { func (r *resolverLogger) LookupHost(ctx context.Context, hostname string) ([]string, error) {
r.Logger.Debugf("resolve %s...", hostname) prefix := fmt.Sprintf("resolve[A,AAAA] %s with %s (%s)", hostname, r.Network(), r.Address())
r.Logger.Debugf("%s...", prefix)
start := time.Now() start := time.Now()
addrs, err := r.Resolver.LookupHost(ctx, hostname) addrs, err := r.Resolver.LookupHost(ctx, hostname)
elapsed := time.Since(start) elapsed := time.Since(start)
if err != nil { if err != nil {
r.Logger.Debugf("resolve %s... %s in %s", hostname, err, elapsed) r.Logger.Debugf("%s... %s in %s", prefix, err, elapsed)
return nil, err return nil, err
} }
r.Logger.Debugf("resolve %s... %+v in %s", hostname, addrs, elapsed) r.Logger.Debugf("%s... %+v in %s", prefix, addrs, elapsed)
return addrs, nil return addrs, nil
} }

View File

@ -146,12 +146,18 @@ func TestResolverLogger(t *testing.T) {
}, },
} }
expected := []string{"1.1.1.1"} expected := []string{"1.1.1.1"}
r := resolverLogger{ r := &resolverLogger{
Logger: lo, Logger: lo,
Resolver: &mocks.Resolver{ Resolver: &mocks.Resolver{
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) { MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
return expected, nil return expected, nil
}, },
MockNetwork: func() string {
return "system"
},
MockAddress: func() string {
return ""
},
}, },
} }
addrs, err := r.LookupHost(context.Background(), "dns.google") addrs, err := r.LookupHost(context.Background(), "dns.google")
@ -174,12 +180,18 @@ func TestResolverLogger(t *testing.T) {
}, },
} }
expected := errors.New("mocked error") expected := errors.New("mocked error")
r := resolverLogger{ r := &resolverLogger{
Logger: lo, Logger: lo,
Resolver: &mocks.Resolver{ Resolver: &mocks.Resolver{
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) { MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
return nil, expected return nil, expected
}, },
MockNetwork: func() string {
return "system"
},
MockAddress: func() string {
return ""
},
}, },
} }
addrs, err := r.LookupHost(context.Background(), "dns.google") addrs, err := r.LookupHost(context.Background(), "dns.google")

View File

@ -121,7 +121,7 @@ type TLSHandshaker interface {
// //
// QUIRK: The returned connection will always implement the TLSConn interface // QUIRK: The returned connection will always implement the TLSConn interface
// exposed by this package. A future version of this interface will instead // exposed by this package. A future version of this interface will instead
// return directly a TLSConn and remove the ConnectionState param. // return directly a TLSConn to avoid unconditional castings.
Handshake(ctx context.Context, conn net.Conn, config *tls.Config) ( Handshake(ctx context.Context, conn net.Conn, config *tls.Config) (
net.Conn, tls.ConnectionState, error) net.Conn, tls.ConnectionState, error)
} }