package server import ( "encoding/base64" "codeberg.org/miekg/dns" "io" "log/slog" "net/http" ) func (s *Server) dohHandler(w http.ResponseWriter, r *http.Request) { var raw []byte switch r.Method { case http.MethodPost: ct := r.Header.Get("Content-Type") if ct != "application/dns-message" { http.Error(w, "unsupported content type", http.StatusUnsupportedMediaType) return } body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 65535)) if err != nil { http.Error(w, "read body", http.StatusBadRequest) return } raw = body case http.MethodGet: param := r.URL.Query().Get("dns") if param == "" { http.Error(w, "missing dns param", http.StatusBadRequest) return } decoded, err := base64.RawURLEncoding.DecodeString(param) if err != nil { http.Error(w, "invalid base64url", http.StatusBadRequest) return } raw = decoded default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } msg := new(dns.Msg) msg.Data = raw if err := msg.Unpack(); err != nil { http.Error(w, "invalid dns message", http.StatusBadRequest) return } if len(msg.Question) == 0 { http.Error(w, "no question", http.StatusBadRequest) return } resp, _ := s.buildResponse(msg) if err := resp.Pack(); err != nil { http.Error(w, "pack response", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/dns-message") w.Header().Set("Cache-Control", "no-cache, max-age=0") if _, err := w.Write(resp.Data); err != nil { slog.Error("doh write failed", "err", err) return } slog.Info("doh query served", "qname", msg.Question[0].Header().Name, "qtype", dns.TypeToString[dns.RRToType(msg.Question[0])], "rcode", dns.RcodeToString[resp.Rcode], "client", r.RemoteAddr, ) }