aboutsummaryrefslogtreecommitdiff
path: root/core/dnsserver/server-https.go
diff options
context:
space:
mode:
authorGravatar Miek Gieben <miek@miek.nl> 2018-05-21 19:40:46 +0100
committerGravatar GitHub <noreply@github.com> 2018-05-21 19:40:46 +0100
commit18b92e1117b67d27f98b14b46b19385c2dd23645 (patch)
tree6be110117ef16181a3a2d6a4703ac781037deabb /core/dnsserver/server-https.go
parent67c9075331b3c32f55e32836ab261d9364aefeb5 (diff)
downloadcoredns-18b92e1117b67d27f98b14b46b19385c2dd23645.tar.gz
coredns-18b92e1117b67d27f98b14b46b19385c2dd23645.tar.zst
coredns-18b92e1117b67d27f98b14b46b19385c2dd23645.zip
make CoreDNS DoH Server (#1619)
* WIP: make CoreDNS DoH Server * It works * Fix tests * Review from Tom - on diff. PR * correct mime type * Cleanups and use the pkg/nonwriter * rename and updates * implement get * implement GET * Code review comments * correct context * tweaks * code review
Diffstat (limited to 'core/dnsserver/server-https.go')
-rw-r--r--core/dnsserver/server-https.go149
1 files changed, 149 insertions, 0 deletions
diff --git a/core/dnsserver/server-https.go b/core/dnsserver/server-https.go
new file mode 100644
index 000000000..f460f0ff4
--- /dev/null
+++ b/core/dnsserver/server-https.go
@@ -0,0 +1,149 @@
+package dnsserver
+
+import (
+ "context"
+ "crypto/tls"
+ "fmt"
+ "net"
+ "net/http"
+ "strconv"
+
+ "github.com/coredns/coredns/plugin/pkg/nonwriter"
+ "github.com/miekg/dns"
+)
+
+// ServerHTTPS represents an instance of a DNS-over-HTTPS server.
+type ServerHTTPS struct {
+ *Server
+ httpsServer *http.Server
+ listenAddr net.Addr
+ tlsConfig *tls.Config
+}
+
+// NewServerHTTPS returns a new CoreDNS GRPC server and compiles all plugins in to it.
+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
+ // TLS configuration return an error: it can only be specified once.
+ var tlsConfig *tls.Config
+ for _, conf := range s.zones {
+ // Should we error if some configs *don't* have TLS?
+ tlsConfig = conf.TLSConfig
+ }
+
+ sh := &ServerHTTPS{Server: s, tlsConfig: tlsConfig, httpsServer: new(http.Server)}
+ sh.httpsServer.Handler = sh
+
+ return sh, nil
+}
+
+// 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) {
+
+ l, err := net.Listen("tcp", s.Addr[len(TransportHTTPS+"://"):])
+ 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
+ }
+
+ out := startUpZones(TransportHTTPS+"://", s.Addr, s.zones)
+ if out != "" {
+ fmt.Print(out)
+ }
+ return
+}
+
+// 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) {
+
+ msg := new(dns.Msg)
+ var err error
+
+ if r.URL.Path != pathDOH {
+ http.Error(w, "", http.StatusNotFound)
+ return
+ }
+
+ switch r.Method {
+ case http.MethodPost:
+ msg, err = postRequestToMsg(r)
+ case http.MethodGet:
+ msg, err = getRequestToMsg(r)
+ default:
+ http.Error(w, "", http.StatusMethodNotAllowed)
+ return
+ }
+
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ // Create a non-writer with the correct addresses in it.
+ dw := &nonwriter.Writer{Laddr: s.listenAddr}
+ h, p, _ := net.SplitHostPort(r.RemoteAddr)
+ po, _ := strconv.Atoi(p)
+ ip := net.ParseIP(h)
+ dw.Raddr = &net.TCPAddr{IP: ip, Port: po}
+
+ // 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.
+ s.ServeDNS(context.Background(), dw, msg)
+
+ buf, _ := dw.Msg.Pack()
+
+ w.Header().Set("Content-Type", mimeTypeDOH)
+ w.Header().Set("Cache-Control", "max-age=128") // TODO(issues/1823): implement proper fix.
+ w.Header().Set("Content-Length", strconv.Itoa(len(buf)))
+ w.WriteHeader(http.StatusOK)
+
+ w.Write(buf)
+}
+
+// Shutdown stops the server (non gracefully).
+func (s *ServerHTTPS) Shutdown() error {
+ if s.httpsServer != nil {
+ s.httpsServer.Shutdown(context.Background())
+ }
+ return nil
+}