From b9dbc81ea73160d5019f64a74b04bd6e61a45e7d Mon Sep 17 00:00:00 2001 From: gdm85 Date: Tue, 9 Dec 2025 22:27:54 +0100 Subject: [PATCH 1/2] proxy: add support for HTTP-only listeners for DoH It is desirable to use the DoH endpoint even without TLS termination, for example in case of an external proxy (nginx) taking care of termination. This change allows to specify independent --http-port ports which use simple TCP listeners. --- internal/cmd/args.go | 8 ++++++++ internal/cmd/config.go | 3 +++ internal/cmd/proxy.go | 7 +++++++ main.go | 1 + proxy/config.go | 3 +++ proxy/proxy.go | 11 +++++++++++ proxy/proxy_test.go | 1 + proxy/server.go | 9 +++++++++ proxy/serverhttps.go | 40 ++++++++++++++++++++++++++++++++++------ 9 files changed, 77 insertions(+), 6 deletions(-) diff --git a/internal/cmd/args.go b/internal/cmd/args.go index c4984503e..ba428c47c 100644 --- a/internal/cmd/args.go +++ b/internal/cmd/args.go @@ -27,6 +27,7 @@ const ( upstreamModeIdx listenAddrsIdx listenPortsIdx + httpListenPortsIdx httpsListenPortsIdx tlsListenPortsIdx quicListenPortsIdx @@ -151,6 +152,12 @@ var commandLineOptions = []*commandLineOption{ short: "p", valueType: "port", }, + httpListenPortsIdx: { + description: "Listening ports for DNS-over-HTTP.", + long: "http-port", + short: "i", + valueType: "port", + }, httpsListenPortsIdx: { description: "Listening ports for DNS-over-HTTPS.", long: "https-port", @@ -424,6 +431,7 @@ func parseCmdLineOptions(conf *configuration) (err error) { upstreamModeIdx: &conf.UpstreamMode, listenAddrsIdx: &conf.ListenAddrs, listenPortsIdx: &conf.ListenPorts, + httpListenPortsIdx: &conf.HTTPListenPorts, httpsListenPortsIdx: &conf.HTTPSListenPorts, tlsListenPortsIdx: &conf.TLSListenPorts, quicListenPortsIdx: &conf.QUICListenPorts, diff --git a/internal/cmd/config.go b/internal/cmd/config.go index 8a246c926..5a9e9f61b 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -49,6 +49,9 @@ type configuration struct { // ListenPorts are the ports server listens on. ListenPorts []int `yaml:"listen-ports"` + // HTTP listen ports are the ports server listens on for DNS-over-HTTP. + HTTPListenPorts []int `yaml:"http-port"` + // HTTPSListenPorts are the ports server listens on for DNS-over-HTTPS. HTTPSListenPorts []int `yaml:"https-port"` diff --git a/internal/cmd/proxy.go b/internal/cmd/proxy.go index 9e39c8a3d..5bed42352 100644 --- a/internal/cmd/proxy.go +++ b/internal/cmd/proxy.go @@ -382,6 +382,13 @@ func (conf *configuration) initListenAddrs(config *proxy.Config) (err error) { } } + for _, ip := range addrs { + for _, port := range conf.HTTPListenPorts { + a := net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port))) + config.HTTPListenAddr = append(config.HTTPListenAddr, a) + } + } + initTLSListenAddrs(config, conf, addrs) initDNSCryptListenAddrs(config, conf, addrs) diff --git a/main.go b/main.go index 6181dadbf..88ca2b836 100644 --- a/main.go +++ b/main.go @@ -7,3 +7,4 @@ import ( func main() { cmd.Main() } + diff --git a/proxy/config.go b/proxy/config.go index 2a8282eda..3458fc51f 100644 --- a/proxy/config.go +++ b/proxy/config.go @@ -143,6 +143,8 @@ type Config struct { // requests. HTTPSListenAddr []*net.TCPAddr + HTTPListenAddr []*net.TCPAddr // if nil, then it does not listen for HTTP (DoH) + // TLSListenAddr is the set of TCP addresses to listen for DNS-over-TLS // requests. TLSListenAddr []*net.TCPAddr @@ -448,6 +450,7 @@ func (p *Proxy) hasListenAddrs() bool { return p.UDPListenAddr != nil || p.TCPListenAddr != nil || p.TLSListenAddr != nil || + p.HTTPListenAddr != nil || p.HTTPSListenAddr != nil || p.QUICListenAddr != nil || p.DNSCryptUDPListenAddr != nil || diff --git a/proxy/proxy.go b/proxy/proxy.go index 44ca67ca2..2d3b40187 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -146,6 +146,9 @@ type Proxy struct { // them. quicTransports []*quic.Transport + httpListen []net.Listener // HTTP listeners + httpServer *http.Server // HTTP server instance + // httpsListen are the listened HTTPS connections. httpsListen []net.Listener @@ -444,6 +447,14 @@ func (p *Proxy) closeListeners(errs []error) (res []error) { p.httpsListen = nil } + if p.httpServer != nil { + res = closeAll(res, p.httpServer) + p.httpServer = nil + + // No need to close these since they're closed by httpsServer.Close(). + p.httpListen = nil + } + if p.h3Server != nil { res = closeAll(res, p.h3Server) p.h3Server = nil diff --git a/proxy/proxy_test.go b/proxy/proxy_test.go index b38eb0e80..4416ea42d 100644 --- a/proxy/proxy_test.go +++ b/proxy/proxy_test.go @@ -182,3 +182,4 @@ func TestProxy_Start_closeOnFail(t *testing.T) { servicetest.RequireRun(t, p, testTimeout) })) } + p.HTTPListenAddr = []*net.TCPAddr{{IP: ip, Port: 0}} diff --git a/proxy/server.go b/proxy/server.go index f2c51ef24..2cb2582ea 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -35,6 +35,11 @@ func (p *Proxy) startListeners(ctx context.Context) (err error) { return err } + err = p.initHTTPListeners(ctx) + if err != nil { + return err + } + err = p.initHTTPSListeners(ctx) if err != nil { return err @@ -67,6 +72,10 @@ func (p *Proxy) serveListeners() { go p.tcpPacketLoop(l, ProtoTLS, p.requestsSema) } + for _, l := range p.httpListen { + go func(l net.Listener) { _ = p.httpServer.Serve(l) }(l) + } + for _, l := range p.httpsListen { go func(l net.Listener) { _ = p.httpsServer.Serve(l) }(l) } diff --git a/proxy/serverhttps.go b/proxy/serverhttps.go index 36677cd08..f64106ea0 100644 --- a/proxy/serverhttps.go +++ b/proxy/serverhttps.go @@ -28,6 +28,7 @@ import ( func (p *Proxy) listenHTTP( ctx context.Context, addr *net.TCPAddr, + withTLS bool, ) (ln net.Listener, tcpAddr *net.TCPAddr, err error) { var tcpListen *net.TCPListener err = p.bindWithRetry(ctx, func() (listenErr error) { @@ -45,14 +46,20 @@ func (p *Proxy) listenHTTP( return nil, nil, fmt.Errorf("bad listener address type: %T", laddr) } - p.logger.InfoContext(ctx, "listening to https", "addr", tcpAddr) + if withTLS { + p.logger.InfoContext(ctx, "listening to https", "addr", tcpAddr) - tlsConfig := p.TLSConfig.Clone() - tlsConfig.NextProtos = []string{http2.NextProtoTLS, "http/1.1"} + tlsConfig := p.TLSConfig.Clone() + tlsConfig.NextProtos = []string{http2.NextProtoTLS, "http/1.1"} + + tlsListen := tls.NewListener(tcpListen, tlsConfig) - tlsListen := tls.NewListener(tcpListen, tlsConfig) + return tlsListen, tcpAddr, nil + } - return tlsListen, tcpAddr, nil + p.logger.InfoContext(ctx, "listening to http", "addr", tcpAddr) + + return tcpListen, tcpAddr, nil } // listenH3 creates instances of QUIC listeners that will be used for running @@ -73,6 +80,27 @@ func (p *Proxy) listenH3( return quicListen, nil } +func (p *Proxy) initHTTPListeners(ctx context.Context) (err error) { + p.httpServer = &http.Server{ + Handler: p, + ReadHeaderTimeout: defaultTimeout, + WriteTimeout: defaultTimeout, + } + + for _, addr := range p.HTTPListenAddr { + p.logger.InfoContext(ctx, "creating an http server") + + ln, _, lErr := p.listenHTTP(ctx, addr, false) + if lErr != nil { + return fmt.Errorf("failed to start HTTPS server on %s: %w", addr, lErr) + } + + p.httpListen = append(p.httpListen, ln) + } + + return nil +} + // initHTTPSListeners creates TCP/UDP listeners and HTTP/H3 servers. func (p *Proxy) initHTTPSListeners(ctx context.Context) (err error) { p.httpsServer = &http.Server{ @@ -90,7 +118,7 @@ func (p *Proxy) initHTTPSListeners(ctx context.Context) (err error) { for _, addr := range p.HTTPSListenAddr { p.logger.InfoContext(ctx, "creating an https server") - ln, tcpAddr, lErr := p.listenHTTP(ctx, addr) + ln, tcpAddr, lErr := p.listenHTTP(ctx, addr, true) if lErr != nil { return fmt.Errorf("failed to start HTTPS server on %s: %w", addr, lErr) } From efcc90be48f2387cb08d06bf2adfb1a4eb8f468a Mon Sep 17 00:00:00 2001 From: gdm85 Date: Wed, 4 Feb 2026 21:47:07 +0100 Subject: [PATCH 2/2] fix: use Go 1.25.7 --- Makefile | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4cbcebd83..9c0c3992f 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ GOAMD64 = v1 GOPROXY = https://proxy.golang.org|direct GOTELEMETRY = off OUT = dnsproxy -GOTOOLCHAIN = go1.25.5 +GOTOOLCHAIN = go1.25.7 RACE = 0 REVISION = $${REVISION:-$$(git rev-parse --short HEAD)} VERSION = 0 diff --git a/go.mod b/go.mod index cfd587471..a3263b0a8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/AdguardTeam/dnsproxy -go 1.25.5 +go 1.25.7 require ( github.com/AdguardTeam/golibs v0.35.2