mirror of
https://github.com/coredns/coredns.git
synced 2026-04-05 11:45:33 -04:00
* core: Reject oversized GET dns query parameter of DoH The DoH POST path limits request size using http.MaxBytesReader(..., 65536), but the GET path passes the dns query value directly to base64ToMsg() with no equivalent bound. This PR adds length check. Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Fix Signed-off-by: Yong Tang <yong.tang.github@outlook.com> --------- Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
140 lines
3.1 KiB
Go
140 lines
3.1 KiB
Go
package doh
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// MimeType is the DoH mimetype that should be used.
|
|
const MimeType = "application/dns-message"
|
|
|
|
// Path is the URL path that should be used.
|
|
const Path = "/dns-query"
|
|
|
|
// NewRequest returns a new DoH request given a HTTP method, URL and dns.Msg.
|
|
//
|
|
// The URL should not have a path, so please exclude /dns-query. The URL will
|
|
// be prefixed with https:// by default, unless it's already prefixed with
|
|
// either http:// or https://.
|
|
func NewRequest(method, url string, m *dns.Msg) (*http.Request, error) {
|
|
buf, err := m.Pack()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
|
url = "https://" + url
|
|
}
|
|
|
|
switch method {
|
|
case http.MethodGet:
|
|
b64 := base64.RawURLEncoding.EncodeToString(buf)
|
|
|
|
req, err := http.NewRequest(
|
|
http.MethodGet,
|
|
fmt.Sprintf("%s%s?dns=%s", url, Path, b64),
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
return req, err
|
|
}
|
|
|
|
req.Header.Set("Content-Type", MimeType)
|
|
req.Header.Set("Accept", MimeType)
|
|
return req, nil
|
|
|
|
case http.MethodPost:
|
|
req, err := http.NewRequest(
|
|
http.MethodPost,
|
|
fmt.Sprintf("%s%s", url, Path),
|
|
bytes.NewReader(buf),
|
|
)
|
|
if err != nil {
|
|
return req, err
|
|
}
|
|
|
|
req.Header.Set("Content-Type", MimeType)
|
|
req.Header.Set("Accept", MimeType)
|
|
return req, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("method not allowed: %s", method)
|
|
}
|
|
}
|
|
|
|
// ResponseToMsg converts a http.Response to a dns message.
|
|
func ResponseToMsg(resp *http.Response) (*dns.Msg, error) {
|
|
defer resp.Body.Close()
|
|
|
|
return toMsg(resp.Body)
|
|
}
|
|
|
|
// RequestToMsg converts a http.Request to a dns message.
|
|
func RequestToMsg(req *http.Request) (*dns.Msg, error) {
|
|
switch req.Method {
|
|
case http.MethodGet:
|
|
return requestToMsgGet(req)
|
|
|
|
case http.MethodPost:
|
|
return requestToMsgPost(req)
|
|
|
|
default:
|
|
return nil, fmt.Errorf("method not allowed: %s", req.Method)
|
|
}
|
|
}
|
|
|
|
// requestToMsgPost extracts the dns message from the request body.
|
|
func requestToMsgPost(req *http.Request) (*dns.Msg, error) {
|
|
defer req.Body.Close()
|
|
return toMsg(req.Body)
|
|
}
|
|
|
|
const maxDNSQuerySize = 65536
|
|
const maxBase64Len = (maxDNSQuerySize*8 + 5) / 6
|
|
|
|
// requestToMsgGet extract the dns message from the GET request.
|
|
func requestToMsgGet(req *http.Request) (*dns.Msg, error) {
|
|
values := req.URL.Query()
|
|
b64, ok := values["dns"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("no 'dns' query parameter found")
|
|
}
|
|
if len(b64) != 1 {
|
|
return nil, fmt.Errorf("multiple 'dns' query values found")
|
|
}
|
|
if len(b64[0]) > maxBase64Len {
|
|
return nil, fmt.Errorf("dns query too large")
|
|
}
|
|
return base64ToMsg(b64[0])
|
|
}
|
|
|
|
func toMsg(r io.ReadCloser) (*dns.Msg, error) {
|
|
buf, err := io.ReadAll(http.MaxBytesReader(nil, r, maxDNSQuerySize))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m := new(dns.Msg)
|
|
err = m.Unpack(buf)
|
|
return m, err
|
|
}
|
|
|
|
func base64ToMsg(b64 string) (*dns.Msg, error) {
|
|
buf, err := b64Enc.DecodeString(b64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
m := new(dns.Msg)
|
|
err = m.Unpack(buf)
|
|
|
|
return m, err
|
|
}
|
|
|
|
var b64Enc = base64.RawURLEncoding
|