2019-11-24 22:45:43 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2019-11-28 23:08:23 +01:00
|
|
|
"crypto/tls"
|
2019-11-28 23:32:56 +01:00
|
|
|
"io"
|
2019-11-26 01:36:35 +01:00
|
|
|
"io/ioutil"
|
2019-11-24 22:45:43 +01:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2019-11-26 01:36:35 +01:00
|
|
|
"time"
|
2019-11-24 22:45:43 +01:00
|
|
|
|
|
|
|
"github.com/jedisct1/dlog"
|
2020-01-06 03:12:29 +01:00
|
|
|
"github.com/miekg/dns"
|
2019-11-24 22:45:43 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type localDoHHandler struct {
|
2019-11-26 01:36:35 +01:00
|
|
|
proxy *Proxy
|
2019-11-24 22:45:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (handler localDoHHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
2019-11-28 23:49:28 +01:00
|
|
|
proxy := handler.proxy
|
2019-12-03 13:04:58 +01:00
|
|
|
if !proxy.clientsCountInc() {
|
|
|
|
dlog.Warnf("Too many incoming connections (max=%d)", proxy.maxClients)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer proxy.clientsCountDec()
|
2019-11-24 22:45:43 +01:00
|
|
|
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
|
|
|
|
}
|
2019-11-24 22:45:43 +01:00
|
|
|
if request.Header.Get("Content-Type") != dataType {
|
2019-11-28 23:30:54 +01:00
|
|
|
writer.Header().Set("Content-Type", "text/plain")
|
2019-11-24 22:45:43 +01:00
|
|
|
writer.WriteHeader(400)
|
2019-11-28 23:30:54 +01:00
|
|
|
writer.Write([]byte("dnscrypt-proxy local DoH server\n"))
|
2019-11-24 22:45:43 +01:00
|
|
|
return
|
|
|
|
}
|
2019-11-26 01:36:35 +01:00
|
|
|
start := time.Now()
|
|
|
|
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)
|
2019-11-28 23:32:56 +01:00
|
|
|
packet, err := ioutil.ReadAll(io.LimitReader(request.Body, MaxHTTPBodyLength))
|
2019-11-26 01:36:35 +01:00
|
|
|
if err != nil {
|
|
|
|
dlog.Warnf("No body in a local DoH query")
|
|
|
|
return
|
|
|
|
}
|
2019-11-28 17:11:14 +01:00
|
|
|
response := proxy.processIncomingQuery(proxy.serversInfo.getOne(), "local_doh", proxy.mainProto, packet, &xClientAddr, nil, start)
|
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
|
|
|
|
}
|
2019-11-29 21:34:16 +01:00
|
|
|
padLen := 127 - (len(response)+127)&127
|
2020-01-06 03:12:29 +01:00
|
|
|
paddedResponse, err := addEDNS0PaddingIfNoneFound(&msg, response, padLen)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2019-11-28 23:33:34 +01:00
|
|
|
writer.Header().Set("Content-Type", dataType)
|
2019-11-24 22:45:43 +01:00
|
|
|
writer.WriteHeader(200)
|
2020-01-06 03:12:29 +01:00
|
|
|
writer.Write(paddedResponse)
|
2019-11-24 22:45:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (proxy *Proxy) localDoHListener(acceptPc *net.TCPListener) {
|
|
|
|
defer acceptPc.Close()
|
2020-01-06 01:02:57 +01:00
|
|
|
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-28 23:08:23 +01:00
|
|
|
noh2 := make(map[string]func(*http.Server, *tls.Conn, http.Handler))
|
2019-11-26 01:36:35 +01:00
|
|
|
httpServer := &http.Server{
|
|
|
|
ReadTimeout: proxy.timeout,
|
|
|
|
WriteTimeout: proxy.timeout,
|
2019-11-28 23:08:23 +01:00
|
|
|
TLSNextProto: noh2,
|
2019-11-26 01:36:35 +01:00
|
|
|
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 {
|
2019-11-24 22:45:43 +01:00
|
|
|
dlog.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|