| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | package dnsserver
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							|  |  |  | 	"context"
 | 
					
						
							|  |  |  | 	"crypto/tls"
 | 
					
						
							|  |  |  | 	"fmt"
 | 
					
						
							| 
									
										
										
										
											2022-06-23 21:46:42 +02:00
										 |  |  | 	stdlog "log"
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	"net"
 | 
					
						
							|  |  |  | 	"net/http"
 | 
					
						
							|  |  |  | 	"strconv"
 | 
					
						
							| 
									
										
										
										
											2018-06-27 21:12:27 +01:00
										 |  |  | 	"time"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 18:14:41 +02:00
										 |  |  | 	"github.com/coredns/caddy"
 | 
					
						
							| 
									
										
										
										
											2022-02-17 14:37:40 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/metrics/vars"
 | 
					
						
							| 
									
										
										
										
											2018-06-27 21:12:27 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/dnsutil"
 | 
					
						
							| 
									
										
										
										
											2018-07-07 08:22:07 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/doh"
 | 
					
						
							| 
									
										
										
										
											2022-06-23 21:46:42 +02:00
										 |  |  | 	clog "github.com/coredns/coredns/plugin/pkg/log"
 | 
					
						
							| 
									
										
										
										
											2018-06-27 21:12:27 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/response"
 | 
					
						
							| 
									
										
										
										
											2019-11-17 02:02:46 +00:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/reuseport"
 | 
					
						
							| 
									
										
										
										
											2018-09-19 07:29:37 +01:00
										 |  |  | 	"github.com/coredns/coredns/plugin/pkg/transport"
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServerHTTPS represents an instance of a DNS-over-HTTPS server.
 | 
					
						
							|  |  |  | type ServerHTTPS struct {
 | 
					
						
							|  |  |  | 	*Server
 | 
					
						
							| 
									
										
										
										
											2020-12-15 14:26:07 +01:00
										 |  |  | 	httpsServer  *http.Server
 | 
					
						
							|  |  |  | 	listenAddr   net.Addr
 | 
					
						
							|  |  |  | 	tlsConfig    *tls.Config
 | 
					
						
							|  |  |  | 	validRequest func(*http.Request) bool
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-23 21:46:42 +02:00
										 |  |  | // loggerAdapter is a simple adapter around CoreDNS logger made to implement io.Writer in order to log errors from HTTP server
 | 
					
						
							|  |  |  | type loggerAdapter struct {
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (l *loggerAdapter) Write(p []byte) (n int, err error) {
 | 
					
						
							|  |  |  | 	clog.Debug(string(p))
 | 
					
						
							|  |  |  | 	return len(p), nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-20 16:08:53 +02:00
										 |  |  | // HTTPRequestKey is the context key for the current processed HTTP request (if current processed request was done over DOH)
 | 
					
						
							|  |  |  | type HTTPRequestKey struct{}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 14:26:07 +01:00
										 |  |  | // NewServerHTTPS returns a new CoreDNS HTTPS server and compiles all plugins in to it.
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | func NewServerHTTPS(addr string, group []*Config) (*ServerHTTPS, error) {
 | 
					
						
							|  |  |  | 	s, err := NewServer(addr, group)
 | 
					
						
							|  |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return nil, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	// The *tls* plugin must make sure that multiple conflicting
 | 
					
						
							| 
									
										
										
										
											2020-09-01 15:10:45 +08:00
										 |  |  | 	// TLS configuration returns an error: it can only be specified once.
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	var tlsConfig *tls.Config
 | 
					
						
							|  |  |  | 	for _, conf := range s.zones {
 | 
					
						
							|  |  |  | 		// Should we error if some configs *don't* have TLS?
 | 
					
						
							|  |  |  | 		tlsConfig = conf.TLSConfig
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2021-11-23 14:03:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 15:38:12 +02:00
										 |  |  | 	// http/2 is recommended when using DoH. We need to specify it in next protos
 | 
					
						
							|  |  |  | 	// or the upgrade won't happen.
 | 
					
						
							| 
									
										
										
										
											2021-11-23 14:03:26 +01:00
										 |  |  | 	if tlsConfig != nil {
 | 
					
						
							|  |  |  | 		tlsConfig.NextProtos = []string{"h2", "http/1.1"}
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 14:26:07 +01:00
										 |  |  | 	// Use a custom request validation func or use the standard DoH path check.
 | 
					
						
							|  |  |  | 	var validator func(*http.Request) bool
 | 
					
						
							|  |  |  | 	for _, conf := range s.zones {
 | 
					
						
							|  |  |  | 		validator = conf.HTTPRequestValidateFunc
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	if validator == nil {
 | 
					
						
							|  |  |  | 		validator = func(r *http.Request) bool { return r.URL.Path == doh.Path }
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-02 16:07:29 +02:00
										 |  |  | 	srv := &http.Server{
 | 
					
						
							|  |  |  | 		ReadTimeout:  5 * time.Second,
 | 
					
						
							|  |  |  | 		WriteTimeout: 10 * time.Second,
 | 
					
						
							|  |  |  | 		IdleTimeout:  120 * time.Second,
 | 
					
						
							| 
									
										
										
										
											2022-06-23 21:46:42 +02:00
										 |  |  | 		ErrorLog:     stdlog.New(&loggerAdapter{}, "", 0),
 | 
					
						
							| 
									
										
										
										
											2020-07-02 16:07:29 +02:00
										 |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2020-12-15 14:26:07 +01:00
										 |  |  | 	sh := &ServerHTTPS{
 | 
					
						
							|  |  |  | 		Server: s, tlsConfig: tlsConfig, httpsServer: srv, validRequest: validator,
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	sh.httpsServer.Handler = sh
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return sh, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-06 16:25:07 +08:00
										 |  |  | // Compile-time check to ensure Server implements the caddy.GracefulServer interface
 | 
					
						
							|  |  |  | var _ caddy.GracefulServer = &Server{}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | // Serve implements caddy.TCPServer interface.
 | 
					
						
							|  |  |  | func (s *ServerHTTPS) Serve(l net.Listener) error {
 | 
					
						
							|  |  |  | 	s.m.Lock()
 | 
					
						
							|  |  |  | 	s.listenAddr = l.Addr()
 | 
					
						
							|  |  |  | 	s.m.Unlock()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if s.tlsConfig != nil {
 | 
					
						
							|  |  |  | 		l = tls.NewListener(l, s.tlsConfig)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return s.httpsServer.Serve(l)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServePacket implements caddy.UDPServer interface.
 | 
					
						
							|  |  |  | func (s *ServerHTTPS) ServePacket(p net.PacketConn) error { return nil }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Listen implements caddy.TCPServer interface.
 | 
					
						
							|  |  |  | func (s *ServerHTTPS) Listen() (net.Listener, error) {
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-17 02:02:46 +00:00
										 |  |  | 	l, err := reuseport.Listen("tcp", s.Addr[len(transport.HTTPS+"://"):])
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		return nil, err
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return l, nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ListenPacket implements caddy.UDPServer interface.
 | 
					
						
							|  |  |  | func (s *ServerHTTPS) ListenPacket() (net.PacketConn, error) { return nil, nil }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // OnStartupComplete lists the sites served by this server
 | 
					
						
							|  |  |  | // and any relevant information, assuming Quiet is false.
 | 
					
						
							|  |  |  | func (s *ServerHTTPS) OnStartupComplete() {
 | 
					
						
							|  |  |  | 	if Quiet {
 | 
					
						
							|  |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-19 07:29:37 +01:00
										 |  |  | 	out := startUpZones(transport.HTTPS+"://", s.Addr, s.zones)
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	if out != "" {
 | 
					
						
							|  |  |  | 		fmt.Print(out)
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stop stops the server. It blocks until the server is totally stopped.
 | 
					
						
							|  |  |  | func (s *ServerHTTPS) Stop() error {
 | 
					
						
							|  |  |  | 	s.m.Lock()
 | 
					
						
							|  |  |  | 	defer s.m.Unlock()
 | 
					
						
							|  |  |  | 	if s.httpsServer != nil {
 | 
					
						
							|  |  |  | 		s.httpsServer.Shutdown(context.Background())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return nil
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServeHTTP is the handler that gets the HTTP request and converts to the dns format, calls the plugin
 | 
					
						
							|  |  |  | // chain, converts it back and write it to the client.
 | 
					
						
							|  |  |  | func (s *ServerHTTPS) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 14:26:07 +01:00
										 |  |  | 	if !s.validRequest(r) {
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 		http.Error(w, "", http.StatusNotFound)
 | 
					
						
							| 
									
										
										
										
											2022-02-17 14:37:40 +01:00
										 |  |  | 		s.countResponse(http.StatusNotFound)
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-07 08:22:07 +01:00
										 |  |  | 	msg, err := doh.RequestToMsg(r)
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	if err != nil {
 | 
					
						
							|  |  |  | 		http.Error(w, err.Error(), http.StatusBadRequest)
 | 
					
						
							| 
									
										
										
										
											2022-02-17 14:37:40 +01:00
										 |  |  | 		s.countResponse(http.StatusBadRequest)
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 13:50:27 +01:00
										 |  |  | 	// Create a DoHWriter with the correct addresses in it.
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	h, p, _ := net.SplitHostPort(r.RemoteAddr)
 | 
					
						
							| 
									
										
										
										
											2018-05-23 13:50:27 +01:00
										 |  |  | 	port, _ := strconv.Atoi(p)
 | 
					
						
							| 
									
										
										
										
											2021-02-17 20:45:04 +01:00
										 |  |  | 	dw := &DoHWriter{
 | 
					
						
							|  |  |  | 		laddr:   s.listenAddr,
 | 
					
						
							|  |  |  | 		raddr:   &net.TCPAddr{IP: net.ParseIP(h), Port: port},
 | 
					
						
							|  |  |  | 		request: r,
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// We just call the normal chain handler - all error handling is done there.
 | 
					
						
							|  |  |  | 	// We should expect a packet to be returned that we can send to the client.
 | 
					
						
							| 
									
										
										
										
											2019-05-27 17:52:48 +03:00
										 |  |  | 	ctx := context.WithValue(context.Background(), Key{}, s.Server)
 | 
					
						
							| 
									
										
										
										
											2021-01-15 19:26:04 +01:00
										 |  |  | 	ctx = context.WithValue(ctx, LoopKey{}, 0)
 | 
					
						
							| 
									
										
										
										
											2022-06-20 16:08:53 +02:00
										 |  |  | 	ctx = context.WithValue(ctx, HTTPRequestKey{}, r)
 | 
					
						
							| 
									
										
										
										
											2019-05-27 17:52:48 +03:00
										 |  |  | 	s.ServeDNS(ctx, dw, msg)
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-25 19:16:17 +03:00
										 |  |  | 	// See section 4.2.1 of RFC 8484.
 | 
					
						
							|  |  |  | 	// We are using code 500 to indicate an unexpected situation when the chain
 | 
					
						
							|  |  |  | 	// handler has not provided any response message.
 | 
					
						
							|  |  |  | 	if dw.Msg == nil {
 | 
					
						
							|  |  |  | 		http.Error(w, "No response", http.StatusInternalServerError)
 | 
					
						
							| 
									
										
										
										
											2022-02-17 14:37:40 +01:00
										 |  |  | 		s.countResponse(http.StatusInternalServerError)
 | 
					
						
							| 
									
										
										
										
											2019-02-25 19:16:17 +03:00
										 |  |  | 		return
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	buf, _ := dw.Msg.Pack()
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-27 21:12:27 +01:00
										 |  |  | 	mt, _ := response.Typify(dw.Msg, time.Now().UTC())
 | 
					
						
							|  |  |  | 	age := dnsutil.MinimalTTL(dw.Msg, mt)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-07 08:22:07 +01:00
										 |  |  | 	w.Header().Set("Content-Type", doh.MimeType)
 | 
					
						
							| 
									
										
										
										
											2018-06-27 21:12:27 +01:00
										 |  |  | 	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%f", age.Seconds()))
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	w.Header().Set("Content-Length", strconv.Itoa(len(buf)))
 | 
					
						
							|  |  |  | 	w.WriteHeader(http.StatusOK)
 | 
					
						
							| 
									
										
										
										
											2022-02-17 14:37:40 +01:00
										 |  |  | 	s.countResponse(http.StatusOK)
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	w.Write(buf)
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 14:37:40 +01:00
										 |  |  | func (s *ServerHTTPS) countResponse(status int) {
 | 
					
						
							|  |  |  | 	vars.HTTPSResponsesCount.WithLabelValues(s.Addr, strconv.Itoa(status)).Inc()
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | // Shutdown stops the server (non gracefully).
 | 
					
						
							|  |  |  | func (s *ServerHTTPS) Shutdown() error {
 | 
					
						
							|  |  |  | 	if s.httpsServer != nil {
 | 
					
						
							|  |  |  | 		s.httpsServer.Shutdown(context.Background())
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 	return nil
 | 
					
						
							|  |  |  | }
 |