{"record":{"id":"79d2302066b91ba6","repo":"ginuerzh/gost","slug":"bad-request-79d230","errorCode":null,"errorMessage":"Bad Request","messagePattern":"Bad Request","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"dns.go","lineNumber":260,"sourceCode":"\tb, err := m.Pack()\n\tif err != nil {\n\t\tlog.Logf(\"[dns] %s: %v\", l.addr, err)\n\t\treturn\n\t}\n\tif err := l.serve(w, b); err != nil {\n\t\tlog.Logf(\"[dns] %s: %v\", l.addr, err)\n\t}\n}\n\n// Based on https://github.com/semihalev/sdns\nfunc (l *dnsListener) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tvar buf []byte\n\tvar err error\n\tswitch r.Method {\n\tcase http.MethodGet:\n\t\tbuf, err = base64.RawURLEncoding.DecodeString(r.URL.Query().Get(\"dns\"))\n\t\tif len(buf) == 0 || err != nil {\n\t\t\thttp.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\tcase http.MethodPost:\n\t\tif r.Header.Get(\"Content-Type\") != \"application/dns-message\" {\n\t\t\thttp.Error(w, http.StatusText(http.StatusUnsupportedMediaType), http.StatusUnsupportedMediaType)\n\t\t\treturn\n\t\t}\n\n\t\tbuf, err = io.ReadAll(r.Body)\n\t\tif err != nil {\n\t\t\thttp.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\tdefault:\n\t\thttp.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/dns.go#L242-L278","documentation":"The DoH handler returns HTTP 400 Bad Request when a GET request's 'dns' query parameter is missing, empty, or is not valid base64url (RFC 4648 raw, no padding) encoded DNS wire-format data. The library requires every DoH GET request to carry a validly encoded DNS message in the 'dns' parameter before it will attempt to unpack it.","triggerScenarios":"GET /dns-query with no ?dns= parameter; GET with ?dns= containing standard base64 with padding characters; GET with ?dns= containing characters outside the base64url alphabet; GET with ?dns= that decodes to zero bytes.","commonSituations":"Clients hand-crafting DoH URLs and forgetting to base64url-encode the query; using base64.StdEncoding instead of RawURLEncoding; shell scripts leaving the parameter unencoded so '+'/'/' appear; truncating the URL so the dns parameter is dropped.","solutions":["base64url-encode (RFC 4648, unpadded) the DNS wire-format message and place it in the ?dns= query parameter","Verify the client uses RawURLEncoding semantics: replace '+' with '-', '/' with '_', and strip '=' padding","Confirm the decoded payload is a complete DNS message (at least the 12-byte header) before sending","Test the URL with a known-good DoH client (e.g. curl with a pre-encoded dns param) to rule out URL mangling"],"exampleFix":"// before (standard base64 with padding)\nq := base64.StdEncoding.EncodeToString(wire)\nurl := \"https://doh.example/dns-query?dns=\" + q\n// after (raw base64url, no padding)\nq := base64.RawURLEncoding.EncodeToString(wire)\nurl := \"https://doh.example/dns-query?dns=\" + q","handlingStrategy":"validation","validationCode":"q := r.URL.Query().Get(\"dns\")\nbuf, err := base64.RawURLEncoding.DecodeString(q)\nif err != nil || len(buf) < 12 {\n\treturn fmt.Errorf(\"invalid dns query param: %v\", err)\n}","typeGuard":"func validDoHGetParam(q string) bool {\n\tbuf, err := base64.RawURLEncoding.DecodeString(q)\n\treturn err == nil && len(buf) >= 12\n}","tryCatchPattern":null,"preventionTips":["Always use RawURLEncoding (base64url, unpadded) for the ?dns= parameter","URL-encode the parameter so the client library does not mangle '-' and '_'","Sanity-check that the decoded payload is at least 12 bytes before sending"],"tags":["http","doh","bad-request","base64url"],"backgroundTag":"http-400-bad-request","analyzedSha":"a33fdbf4c98034f4bfeeaea9868909822b9c526d","analyzedAt":"2026-09-02T22:15:54.506Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}