diff options
| author | radhitya <alif@radhitya.org> | 2026-06-13 12:46:38 +0700 |
|---|---|---|
| committer | radhitya <alif@radhitya.org> | 2026-06-13 12:46:38 +0700 |
| commit | 01e05e8df5f56d605dfd75456a424527e76a2955 (patch) | |
| tree | 3de06990292982a7c13f9a6edc9fe07e6127ff00 /internal/server/doh.go | |
| parent | 1d1a15075b93815a2d006167d433c03d1abef419 (diff) | |
dns codec, udp server (reuseport, 4096 buffers), tcp server, doh listener post get without tls, concurrent, ends0, fuzz)
Diffstat (limited to 'internal/server/doh.go')
| -rw-r--r-- | internal/server/doh.go | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/internal/server/doh.go b/internal/server/doh.go new file mode 100644 index 0000000..e9cf466 --- /dev/null +++ b/internal/server/doh.go @@ -0,0 +1,75 @@ +package server + +import ( + "encoding/base64" + "github.com/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) + if err := msg.Unpack(raw); 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 := buildResponse(msg) + packed, err := resp.Pack() + if 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(packed); err != nil { + slog.Error("doh write failed", "err", err) + return + } + + slog.Info("doh query served", + "qname", msg.Question[0].Name, + "qtype", dns.TypeToString[msg.Question[0].Qtype], + "rcode", dns.RcodeToString[resp.Rcode], + "client", r.RemoteAddr, + ) +} |
