dnscrypt-proxy/dnscrypt-proxy/local-doh.go

142 lines
3.4 KiB
Go
Raw Normal View History

package main
import (
"encoding/base64"
"fmt"
2019-11-28 23:32:56 +01:00
"io"
"net"
"net/http"
"strings"
2019-11-26 01:36:35 +01:00
"time"
"github.com/jedisct1/dlog"
2020-01-06 03:12:29 +01:00
"github.com/miekg/dns"
)
type localDoHHandler struct {
2019-11-26 01:36:35 +01:00
proxy *Proxy
}
func (handler localDoHHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
2019-11-28 23:49:28 +01:00
proxy := handler.proxy
if !proxy.clientsCountInc() {
dlog.Warnf("Too many incoming connections (max=%d)", proxy.maxClients)
return
}
defer proxy.clientsCountDec()
dataType := "application/dns-message"
2019-11-28 16:46:25 +01:00
writer.Header().Set("Server", "dnscrypt-proxy")
2019-11-28 23:49:28 +01:00
if request.URL.Path != proxy.localDoHPath {
writer.WriteHeader(404)
return
}
packet := []byte{}
var err error
start := time.Now()
if request.Method == "POST" &&
request.Header.Get("Content-Type") == dataType {
2023-02-02 19:38:24 +01:00
packet, err = io.ReadAll(io.LimitReader(request.Body, int64(MaxDNSPacketSize)))
if err != nil {
dlog.Warnf("No body in a local DoH query")
return
}
} else if request.Method == "GET" && request.Header.Get("Accept") == dataType {
encodedPacket := request.URL.Query().Get("dns")
if len(encodedPacket) >= MinDNSPacketSize*4/3 && len(encodedPacket) <= MaxDNSPacketSize*4/3 {
packet, err = base64.RawURLEncoding.DecodeString(encodedPacket)
if err != nil {
dlog.Warnf("Invalid base64 in a local DoH query")
return
}
}
}
if len(packet) < MinDNSPacketSize {
writer.Header().Set("Content-Type", "text/plain")
writer.WriteHeader(400)
writer.Write([]byte("dnscrypt-proxy local DoH server\n"))
return
}
2019-11-26 01:36:35 +01:00
clientAddr, err := net.ResolveTCPAddr("tcp", request.RemoteAddr)
if err != nil {
dlog.Errorf("Unable to get the client address: [%v]", err)
return
}
xClientAddr := net.Addr(clientAddr)
hasEDNS0Padding, err := hasEDNS0Padding(packet)
if err != nil {
writer.WriteHeader(400)
return
}
response := proxy.processIncomingQuery("local_doh", proxy.mainProto, packet, &xClientAddr, nil, start, false)
2019-11-26 01:36:35 +01:00
if len(response) == 0 {
writer.WriteHeader(500)
return
}
2020-01-06 03:12:29 +01:00
msg := dns.Msg{}
if err := msg.Unpack(packet); err != nil {
writer.WriteHeader(500)
return
}
responseLen := len(response)
paddedLen := dohPaddedLen(responseLen)
padLen := paddedLen - responseLen
if hasEDNS0Padding {
response, err = addEDNS0PaddingIfNoneFound(&msg, response, padLen)
if err != nil {
dlog.Critical(err)
return
}
} else {
pad := strings.Repeat("X", padLen)
writer.Header().Set("X-Pad", pad)
2020-01-06 03:12:29 +01:00
}
2019-11-28 23:33:34 +01:00
writer.Header().Set("Content-Type", dataType)
writer.Header().Set("Content-Length", fmt.Sprint(len(response)))
writer.WriteHeader(200)
writer.Write(response)
}
func (proxy *Proxy) localDoHListener(acceptPc *net.TCPListener) {
defer acceptPc.Close()
if len(proxy.localDoHCertFile) == 0 || len(proxy.localDoHCertKeyFile) == 0 {
dlog.Fatal("A certificate and a key are required to start a local DoH service")
}
2019-11-26 01:36:35 +01:00
httpServer := &http.Server{
ReadTimeout: proxy.timeout,
WriteTimeout: proxy.timeout,
Handler: localDoHHandler{proxy: proxy},
}
2019-11-28 23:08:23 +01:00
httpServer.SetKeepAlivesEnabled(true)
2019-11-28 16:46:25 +01:00
if err := httpServer.ServeTLS(acceptPc, proxy.localDoHCertFile, proxy.localDoHCertKeyFile); err != nil {
dlog.Fatal(err)
}
}
func dohPaddedLen(unpaddedLen int) int {
boundaries := [...]int{
64,
128,
192,
256,
320,
384,
512,
704,
768,
896,
960,
1024,
1088,
1152,
2688,
4080,
MaxDNSPacketSize,
}
for _, boundary := range boundaries {
if boundary >= unpaddedLen {
return boundary
}
}
return unpaddedLen
}