| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2022-09-08 14:56:27 -04:00
										 |  |  | 	for _, z := range s.zones { | 
					
						
							|  |  |  | 		for _, conf := range z { | 
					
						
							|  |  |  | 			// Should we error if some configs *don't* have TLS? | 
					
						
							|  |  |  | 			tlsConfig = conf.TLSConfig | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-05-21 19:40:46 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2022-09-08 14:56:27 -04:00
										 |  |  | 	for _, z := range s.zones { | 
					
						
							|  |  |  | 		for _, conf := range z { | 
					
						
							|  |  |  | 			validator = conf.HTTPRequestValidateFunc | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-12-15 14:26:07 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	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{ | 
					
						
							| 
									
										
										
										
											2022-12-28 11:14:16 +00:00
										 |  |  | 		ReadTimeout:  s.readTimeout, | 
					
						
							|  |  |  | 		WriteTimeout: s.writeTimeout, | 
					
						
							|  |  |  | 		IdleTimeout:  s.idleTimeout, | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-05 11:34:20 -07:00
										 |  |  | // Compile-time check to ensure ServerHTTPS implements the caddy.GracefulServer interface | 
					
						
							|  |  |  | var _ caddy.GracefulServer = &ServerHTTPS{} | 
					
						
							| 
									
										
										
										
											2020-03-06 16:25:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2024-10-24 21:20:19 +02:00
										 |  |  | 	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", uint32(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 | 
					
						
							|  |  |  | } |