From 3718f0cc817a8dd3127cb6b22c2e437d8ae0dc4c Mon Sep 17 00:00:00 2001 From: Cedric Wang Date: Fri, 5 Jun 2026 18:31:53 -0700 Subject: [PATCH] dnsserver: use http.LocalAddrContextKey for DoH local address (#8149) The DoH server resolved the per-connection local address in a custom http.Server.ConnContext callback. ConnContext runs synchronously in the http.Server accept loop, so calling c.LocalAddr() there is a problem when the listener is proxyproto-wrapped: LocalAddr() triggers the PROXY-header read, which blocks the accept loop until the header arrives and head-of-line-blocks acceptance of every other connection. net/http and http2 already populate http.LocalAddrContextKey from the connection in the per-connection serving goroutine (net/http server.go, http2 server_common.go / h2_bundle.go), resolved through the same tls.Conn -> proxyproto.Conn chain. For a proxyproto connection that value is the PROXY header's destination address -- byte-identical to what the custom callback produced -- and it is set off the accept loop on both the HTTP/1.1 and HTTP/2 paths. Drop the custom ConnContext callback and the connKey type, and read http.LocalAddrContextKey in localAddr() instead. The client address is unaffected: it arrives via r.RemoteAddr, which the framework populates natively. Signed-off-by: zongqi-wang --- core/dnsserver/server_https.go | 10 ++-------- core/dnsserver/server_https_test.go | 4 ++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/core/dnsserver/server_https.go b/core/dnsserver/server_https.go index 2f0746b0d..9e866adc3 100644 --- a/core/dnsserver/server_https.go +++ b/core/dnsserver/server_https.go @@ -52,9 +52,6 @@ func (l *loggerAdapter) Write(p []byte) (n int, err error) { // Plugins can access the original HTTP request to retrieve headers, client IP, and metadata. type HTTPRequestKey struct{} -// connAddrKey is the context key for the per-connection local address set by ConnContext. -type connAddrKey struct{} - // NewServerHTTPS returns a new CoreDNS HTTPS server and compiles all plugins in to it. func NewServerHTTPS(addr string, group []*Config) (*ServerHTTPS, error) { s, err := NewServer(addr, group) @@ -93,9 +90,6 @@ func NewServerHTTPS(addr string, group []*Config) (*ServerHTTPS, error) { WriteTimeout: s.WriteTimeout, IdleTimeout: s.IdleTimeout, ErrorLog: stdlog.New(&loggerAdapter{}, "", 0), - ConnContext: func(ctx context.Context, c net.Conn) context.Context { - return context.WithValue(ctx, connAddrKey{}, c.LocalAddr()) - }, } maxConnections := DefaultHTTPSMaxConnections if len(group) > 0 && group[0] != nil && group[0].MaxHTTPSConnections != nil { @@ -176,9 +170,9 @@ func (s *ServerHTTPS) Stop() error { return nil } -// localAddr returns the per-connection local address from context, or s.listenAddr as fallback. +// localAddr returns the per-connection local address, or s.listenAddr as fallback. func (s *ServerHTTPS) localAddr(r *http.Request) net.Addr { - if addr, ok := r.Context().Value(connAddrKey{}).(net.Addr); ok { + if addr, ok := r.Context().Value(http.LocalAddrContextKey).(net.Addr); ok { return addr } return s.listenAddr diff --git a/core/dnsserver/server_https_test.go b/core/dnsserver/server_https_test.go index e36c3d1d1..525348aa9 100644 --- a/core/dnsserver/server_https_test.go +++ b/core/dnsserver/server_https_test.go @@ -196,7 +196,7 @@ func TestDoHWriterLaddrFromConnContext(t *testing.T) { ppDst := &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 443} r := httptest.NewRequest(http.MethodPost, "/dns-query", io.NopCloser(bytes.NewReader(buf))) - ctx := context.WithValue(r.Context(), connAddrKey{}, ppDst) + ctx := context.WithValue(r.Context(), http.LocalAddrContextKey, ppDst) r = r.WithContext(ctx) w := httptest.NewRecorder() @@ -230,7 +230,7 @@ func TestDoHWriterLaddrFallback(t *testing.T) { t.Fatal(err) } - // No connAddrKey in context; should fall back to s.listenAddr. + // No LocalAddrContextKey in context; should fall back to s.listenAddr. r := httptest.NewRequest(http.MethodPost, "/dns-query", io.NopCloser(bytes.NewReader(buf))) w := httptest.NewRecorder()