Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proxy: add support for HTTP-only listeners for DoH #302

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ type Options struct {
// Server listen ports
ListenPorts []int `yaml:"listen-ports" short:"p" long:"port" description:"Listening ports. Zero value disables TCP and UDP listeners"`

// HTTP listen ports
HTTPListenPorts []int `yaml:"http-port" short:"i" long:"http-port" description:"Listening ports for DNS-over-HTTP"`
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find a better letter here, -i.


// HTTPS listen ports
HTTPSListenPorts []int `yaml:"https-port" short:"s" long:"https-port" description:"Listening ports for DNS-over-HTTPS"`

Expand Down Expand Up @@ -245,9 +248,9 @@ func main() {
if err != nil {
if flagsErr, ok := err.(*goFlags.Error); ok && flagsErr.Type == goFlags.ErrHelp {
os.Exit(0)
} else {
log.Fatalf("failed to parse args: %v", err)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unrelated fix but handy to have.

}

os.Exit(1)
}

run(options)
Expand Down Expand Up @@ -621,6 +624,13 @@ func initListenAddrs(config *proxy.Config, options *Options) {
}
}

for _, port := range options.HTTPListenPorts {
for _, ip := range listenIPs {
a := net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16(port)))
config.HTTPListenAddr = append(config.HTTPSListenAddr, a)
}
}

if config.DNSCryptResolverCert != nil && config.DNSCryptProviderName != "" {
for _, port := range options.DNSCryptListenPorts {
for _, ip := range listenIPs {
Expand Down
2 changes: 2 additions & 0 deletions proxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Config struct {

UDPListenAddr []*net.UDPAddr // if nil, then it does not listen for UDP
TCPListenAddr []*net.TCPAddr // if nil, then it does not listen for TCP
HTTPListenAddr []*net.TCPAddr // if nil, then it does not listen for HTTP (DoH)
HTTPSListenAddr []*net.TCPAddr // if nil, then it does not listen for HTTPS (DoH)
TLSListenAddr []*net.TCPAddr // if nil, then it does not listen for TLS (DoT)
QUICListenAddr []*net.UDPAddr // if nil, then it does not listen for QUIC (DoQ)
Expand Down Expand Up @@ -332,6 +333,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 ||
Expand Down
3 changes: 3 additions & 0 deletions proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ type Proxy struct {
// quicListen are the listened QUIC connections.
quicListen []*quic.EarlyListener

httpListen []net.Listener // HTTP listeners
httpServer *http.Server // HTTP server instance

// httpsListen are the listened HTTPS connections.
httpsListen []net.Listener

Expand Down
1 change: 1 addition & 0 deletions proxy/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,7 @@ func createTestProxy(t *testing.T, tlsConfig *tls.Config) *Proxy {
} else {
p.UDPListenAddr = []*net.UDPAddr{{IP: ip, Port: 0}}
p.TCPListenAddr = []*net.TCPAddr{{IP: ip, Port: 0}}
p.HTTPListenAddr = []*net.TCPAddr{{IP: ip, Port: 0}}
}
upstreams := make([]upstream.Upstream, 0)
dnsUpstream, err := upstream.AddressToUpstream(
Expand Down
9 changes: 9 additions & 0 deletions proxy/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ func (p *Proxy) startListeners(ctx context.Context) error {
return err
}

err = p.createHTTPListeners()
if err != nil {
return err
}

err = p.createTLSListeners()
if err != nil {
return err
Expand Down Expand Up @@ -55,6 +60,10 @@ func (p *Proxy) startListeners(ctx context.Context) error {
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)
}
Expand Down
46 changes: 40 additions & 6 deletions proxy/server_https.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import (
"golang.org/x/net/http2"
)

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

// listenHTTP creates instances of TCP listeners that will be used to run an
// H1 server. Returns the address the listener actually listens to (useful
// in the case if port 0 is specified).
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result is not really used, since there is no H3 support.

func (p *Proxy) listenHTTP(addr *net.TCPAddr) (laddr *net.TCPAddr, err error) {
tcpListen, err := net.ListenTCP("tcp", addr)
if err != nil {
return nil, fmt.Errorf("tcp listener: %w", err)
}
log.Info("Listening to http://%s", tcpListen.Addr())

p.httpListen = append(p.httpListen, tcpListen)

return tcpListen.Addr().(*net.TCPAddr), nil
}

// listenH3 creates instances of QUIC listeners that will be used for running
// an HTTP/3 server.
func (p *Proxy) listenH3(addr *net.UDPAddr) (err error) {
Expand Down Expand Up @@ -70,10 +85,9 @@ func (p *Proxy) createHTTPSListeners() (err error) {

for _, addr := range p.HTTPSListenAddr {
log.Info("Creating an HTTPS server")

tcpAddr, lErr := p.listenHTTP(addr)
if lErr != nil {
return fmt.Errorf("failed to start HTTPS server on %s: %w", addr, lErr)
tcpAddr, err := p.listenHTTPS(addr)
if err != nil {
return fmt.Errorf("failed to start HTTPS server on %s: %w", addr, err)
}

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

// createHTTPListeners creates the cleartext HTTP listener for DNS-over-HTTPS (behind a proxy doing TLS termination).
func (p *Proxy) createHTTPListeners() (err error) {
p.httpServer = &http.Server{
Handler: p,
ReadHeaderTimeout: defaultTimeout,
WriteTimeout: defaultTimeout,
}

for _, addr := range p.HTTPListenAddr {
log.Info("Creating an HTTP server")

_, err := p.listenHTTP(addr)
if err != nil {
return fmt.Errorf("failed to start HTTP server on %s: %w", addr, err)
}
}

return nil
}

// ServeHTTP is the http.Handler implementation that handles DoH queries.
// Here is what it returns:
//
Expand Down