Skip to content

Commit 19619c2

Browse files
tomberganbradfitz
authored andcommitted
net, net/http: don't trace DNS dials
This fixes change https://go-review.googlesource.com/#/c/23069/, which assumes all DNS requests are UDP. This is not true -- DNS requests can be TCP in some cases. See: https://tip.golang.org/src/net/dnsclient_unix.go#L154 https://en.wikipedia.org/wiki/Domain_Name_System#Protocol_transport Also, the test code added by the above change doesn't actually test anything because the test uses a faked DNS resolver that doesn't actually make any DNS queries. I fixed that by adding another test that uses the system DNS resolver. Updates #12580 Change-Id: I6c24c03ebab84d437d3ac610fd6eb5353753c490 Reviewed-on: https://go-review.googlesource.com/23101 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
1 parent 0cc710d commit 19619c2

File tree

3 files changed

+63
-18
lines changed

3 files changed

+63
-18
lines changed

src/internal/nettrace/nettrace.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ type Trace struct {
3232
// actually be for circular dependency reasons.
3333
DNSDone func(netIPs []interface{}, coalesced bool, err error)
3434

35-
// ConnectStart is called before a TCPAddr or UnixAddr
36-
// Dial. In the case of DualStack (Happy Eyeballs) dialing,
37-
// this may be called multiple times, from multiple
35+
// ConnectStart is called before a Dial, excluding Dials made
36+
// during DNS lookups. In the case of DualStack (Happy Eyeballs)
37+
// dialing, this may be called multiple times, from multiple
3838
// goroutines.
3939
ConnectStart func(network, addr string)
4040

41-
// ConnectStart is called after a TCPAddr or UnixAddr Dial
42-
// with the results. It may also be called multiple times,
43-
// like ConnectStart.
41+
// ConnectStart is called after a Dial with the results, excluding
42+
// Dials made during DNS lookups. It may also be called multiple
43+
// times, like ConnectStart.
4444
ConnectDone func(network, addr string, err error)
4545
}

src/net/dial.go

+11-12
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,16 @@ func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn
317317
ctx = subCtx
318318
}
319319

320-
addrs, err := resolveAddrList(ctx, "dial", network, address, d.LocalAddr)
320+
// Shadow the nettrace (if any) during resolve so Connect events don't fire for DNS lookups.
321+
resolveCtx := ctx
322+
if trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace); trace != nil {
323+
shadow := *trace
324+
shadow.ConnectStart = nil
325+
shadow.ConnectDone = nil
326+
resolveCtx = context.WithValue(resolveCtx, nettrace.TraceKey{}, &shadow)
327+
}
328+
329+
addrs, err := resolveAddrList(resolveCtx, "dial", network, address, d.LocalAddr)
321330
if err != nil {
322331
return nil, &OpError{Op: "dial", Net: network, Source: nil, Addr: nil, Err: err}
323332
}
@@ -472,21 +481,11 @@ func dialSerial(ctx context.Context, dp *dialParam, ras addrList) (Conn, error)
472481
return nil, firstErr
473482
}
474483

475-
// traceDialType reports whether ra is an address type for which
476-
// nettrace.Trace should trace.
477-
func traceDialType(ra Addr) bool {
478-
switch ra.(type) {
479-
case *TCPAddr, *UnixAddr:
480-
return true
481-
}
482-
return false
483-
}
484-
485484
// dialSingle attempts to establish and returns a single connection to
486485
// the destination address.
487486
func dialSingle(ctx context.Context, dp *dialParam, ra Addr) (c Conn, err error) {
488487
trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
489-
if trace != nil && traceDialType(ra) {
488+
if trace != nil {
490489
raStr := ra.String()
491490
if trace.ConnectStart != nil {
492491
trace.ConnectStart(dp.network, raStr)

src/net/http/transport_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -3308,6 +3308,52 @@ func testTransportEventTrace(t *testing.T, noHooks bool) {
33083308
}
33093309
}
33103310

3311+
func TestTransportEventTraceRealDNS(t *testing.T) {
3312+
defer afterTest(t)
3313+
tr := &Transport{}
3314+
defer tr.CloseIdleConnections()
3315+
c := &Client{Transport: tr}
3316+
3317+
var mu sync.Mutex
3318+
var buf bytes.Buffer
3319+
logf := func(format string, args ...interface{}) {
3320+
mu.Lock()
3321+
defer mu.Unlock()
3322+
fmt.Fprintf(&buf, format, args...)
3323+
buf.WriteByte('\n')
3324+
}
3325+
3326+
req, _ := NewRequest("GET", "http://dns-should-not-resolve.golang:80", nil)
3327+
trace := &httptrace.ClientTrace{
3328+
DNSStart: func(e httptrace.DNSStartInfo) { logf("DNSStart: %+v", e) },
3329+
DNSDone: func(e httptrace.DNSDoneInfo) { logf("DNSDone: %+v", e) },
3330+
ConnectStart: func(network, addr string) { logf("ConnectStart: %s %s", network, addr) },
3331+
ConnectDone: func(network, addr string, err error) { logf("ConnectDone: %s %s %v", network, addr, err) },
3332+
}
3333+
req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace))
3334+
3335+
resp, err := c.Do(req)
3336+
if err == nil {
3337+
resp.Body.Close()
3338+
t.Fatal("expected error during DNS lookup")
3339+
}
3340+
3341+
got := buf.String()
3342+
wantSub := func(sub string) {
3343+
if !strings.Contains(got, sub) {
3344+
t.Errorf("expected substring %q in output.", sub)
3345+
}
3346+
}
3347+
wantSub("DNSStart: {Host:dns-should-not-resolve.golang}")
3348+
wantSub("DNSDone: {Addrs:[] Err:")
3349+
if strings.Contains(got, "ConnectStart") || strings.Contains(got, "ConnectDone") {
3350+
t.Errorf("should not see Connect events")
3351+
}
3352+
if t.Failed() {
3353+
t.Errorf("Output:\n%s", got)
3354+
}
3355+
}
3356+
33113357
func TestTransportMaxIdleConns(t *testing.T) {
33123358
defer afterTest(t)
33133359
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {

0 commit comments

Comments
 (0)