Skip to content

Commit 7e545fa

Browse files
committed
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. It also contains a separate fix to print the arguments parsing error instead of exiting with exit code 1 and no output.
1 parent 638288e commit 7e545fa

File tree

6 files changed

+67
-8
lines changed

6 files changed

+67
-8
lines changed

main.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ type Options struct {
5757
// Server listen ports
5858
ListenPorts []int `yaml:"listen-ports" short:"p" long:"port" description:"Listening ports. Zero value disables TCP and UDP listeners"`
5959

60+
// HTTP listen ports
61+
HTTPListenPorts []int `yaml:"http-port" short:"i" long:"http-port" description:"Listening ports for DNS-over-HTTP"`
62+
6063
// HTTPS listen ports
6164
HTTPSListenPorts []int `yaml:"https-port" short:"s" long:"https-port" description:"Listening ports for DNS-over-HTTPS"`
6265

@@ -245,9 +248,9 @@ func main() {
245248
if err != nil {
246249
if flagsErr, ok := err.(*goFlags.Error); ok && flagsErr.Type == goFlags.ErrHelp {
247250
os.Exit(0)
251+
} else {
252+
log.Fatalf("failed to parse args: %v", err)
248253
}
249-
250-
os.Exit(1)
251254
}
252255

253256
run(options)
@@ -621,6 +624,13 @@ func initListenAddrs(config *proxy.Config, options *Options) {
621624
}
622625
}
623626

627+
for _, port := range options.HTTPListenPorts {
628+
for _, ip := range listenIPs {
629+
a := net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port)))
630+
config.HTTPListenAddr = append(config.HTTPSListenAddr, a)
631+
}
632+
}
633+
624634
if config.DNSCryptResolverCert != nil && config.DNSCryptProviderName != "" {
625635
for _, port := range options.DNSCryptListenPorts {
626636
for _, ip := range listenIPs {

proxy/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type Config struct {
4949

5050
UDPListenAddr []*net.UDPAddr // if nil, then it does not listen for UDP
5151
TCPListenAddr []*net.TCPAddr // if nil, then it does not listen for TCP
52+
HTTPListenAddr []*net.TCPAddr // if nil, then it does not listen for HTTP (DoH)
5253
HTTPSListenAddr []*net.TCPAddr // if nil, then it does not listen for HTTPS (DoH)
5354
TLSListenAddr []*net.TCPAddr // if nil, then it does not listen for TLS (DoT)
5455
QUICListenAddr []*net.UDPAddr // if nil, then it does not listen for QUIC (DoQ)
@@ -332,6 +333,7 @@ func (p *Proxy) hasListenAddrs() bool {
332333
return p.UDPListenAddr != nil ||
333334
p.TCPListenAddr != nil ||
334335
p.TLSListenAddr != nil ||
336+
p.HTTPListenAddr != nil ||
335337
p.HTTPSListenAddr != nil ||
336338
p.QUICListenAddr != nil ||
337339
p.DNSCryptUDPListenAddr != nil ||

proxy/proxy.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ type Proxy struct {
8686
// quicListen are the listened QUIC connections.
8787
quicListen []*quic.EarlyListener
8888

89+
httpListen []net.Listener // HTTP listeners
90+
httpServer *http.Server // HTTP server instance
91+
8992
// httpsListen are the listened HTTPS connections.
9093
httpsListen []net.Listener
9194

proxy/proxy_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,7 @@ func createTestProxy(t *testing.T, tlsConfig *tls.Config) *Proxy {
12251225
} else {
12261226
p.UDPListenAddr = []*net.UDPAddr{{IP: ip, Port: 0}}
12271227
p.TCPListenAddr = []*net.TCPAddr{{IP: ip, Port: 0}}
1228+
p.HTTPListenAddr = []*net.TCPAddr{{Port: 0, IP: net.ParseIP(listenIP)}}
12281229
}
12291230
upstreams := make([]upstream.Upstream, 0)
12301231
dnsUpstream, err := upstream.AddressToUpstream(

proxy/server.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ func (p *Proxy) startListeners(ctx context.Context) error {
2323
return err
2424
}
2525

26+
err = p.createHTTPListeners()
27+
if err != nil {
28+
return err
29+
}
30+
2631
err = p.createTLSListeners()
2732
if err != nil {
2833
return err
@@ -55,6 +60,10 @@ func (p *Proxy) startListeners(ctx context.Context) error {
5560
go p.tcpPacketLoop(l, ProtoTLS, p.requestsSema)
5661
}
5762

63+
for _, l := range p.httpListen {
64+
go func(l net.Listener) { _ = p.httpServer.Serve(l) }(l)
65+
}
66+
5867
for _, l := range p.httpsListen {
5968
go func(l net.Listener) { _ = p.httpsServer.Serve(l) }(l)
6069
}

proxy/server_https.go

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ import (
1919
"golang.org/x/net/http2"
2020
)
2121

22-
// listenHTTP creates instances of TLS listeners that will be used to run an
22+
// listenHTTPS creates instances of TLS listeners that will be used to run an
2323
// H1/H2 server. Returns the address the listener actually listens to (useful
2424
// in the case if port 0 is specified).
25-
func (p *Proxy) listenHTTP(addr *net.TCPAddr) (laddr *net.TCPAddr, err error) {
25+
func (p *Proxy) listenHTTPS(addr *net.TCPAddr) (laddr *net.TCPAddr, err error) {
2626
tcpListen, err := net.ListenTCP("tcp", addr)
2727
if err != nil {
2828
return nil, fmt.Errorf("tcp listener: %w", err)
@@ -38,6 +38,21 @@ func (p *Proxy) listenHTTP(addr *net.TCPAddr) (laddr *net.TCPAddr, err error) {
3838
return tcpListen.Addr().(*net.TCPAddr), nil
3939
}
4040

41+
// listenHTTP creates instances of TCP listeners that will be used to run an
42+
// H1 server. Returns the address the listener actually listens to (useful
43+
// in the case if port 0 is specified).
44+
func (p *Proxy) listenHTTP(addr *net.TCPAddr) (laddr *net.TCPAddr, err error) {
45+
tcpListen, err := net.ListenTCP("tcp", addr)
46+
if err != nil {
47+
return nil, fmt.Errorf("tcp listener: %w", err)
48+
}
49+
log.Info("Listening to http://%s", tcpListen.Addr())
50+
51+
p.httpListen = append(p.httpListen, tcpListen)
52+
53+
return tcpListen.Addr().(*net.TCPAddr), nil
54+
}
55+
4156
// listenH3 creates instances of QUIC listeners that will be used for running
4257
// an HTTP/3 server.
4358
func (p *Proxy) listenH3(addr *net.UDPAddr) (err error) {
@@ -70,10 +85,9 @@ func (p *Proxy) createHTTPSListeners() (err error) {
7085

7186
for _, addr := range p.HTTPSListenAddr {
7287
log.Info("Creating an HTTPS server")
73-
74-
tcpAddr, lErr := p.listenHTTP(addr)
75-
if lErr != nil {
76-
return fmt.Errorf("failed to start HTTPS server on %s: %w", addr, lErr)
88+
tcpAddr, err := p.listenHTTPS(addr)
89+
if err != nil {
90+
return fmt.Errorf("failed to start HTTPS server on %s: %w", addr, err)
7791
}
7892

7993
if p.HTTP3 {
@@ -90,6 +104,26 @@ func (p *Proxy) createHTTPSListeners() (err error) {
90104
return nil
91105
}
92106

107+
// createHTTPListeners creates the cleartext HTTP listener for DNS-over-HTTPS (behind a proxy doing TLS termination).
108+
func (p *Proxy) createHTTPListeners() (err error) {
109+
p.httpServer = &http.Server{
110+
Handler: p,
111+
ReadHeaderTimeout: defaultTimeout,
112+
WriteTimeout: defaultTimeout,
113+
}
114+
115+
for _, addr := range p.HTTPListenAddr {
116+
log.Info("Creating an HTTP server")
117+
118+
_, err := p.listenHTTP(addr)
119+
if err != nil {
120+
return fmt.Errorf("failed to start HTTP server on %s: %w", addr, err)
121+
}
122+
}
123+
124+
return nil
125+
}
126+
93127
// ServeHTTP is the http.Handler implementation that handles DoH queries.
94128
// Here is what it returns:
95129
//

0 commit comments

Comments
 (0)