First bits towards providing access over DoH in addition to DNS

Mainly to deal with the Firefox+ESNI situation
This commit is contained in:
Frank Denis 2019-11-24 22:45:43 +01:00
parent 30b5507bf4
commit f249813cc5
3 changed files with 94 additions and 4 deletions

View File

@ -33,6 +33,7 @@ type Config struct {
ServerNames []string `toml:"server_names"`
DisabledServerNames []string `toml:"disabled_server_names"`
ListenAddresses []string `toml:"listen_addresses"`
LocalDoHListenAddresses []string `toml:"local_doh_listen_addresses"`
Daemonize bool
UserName string `toml:"user_name"`
ForceTCP bool `toml:"force_tcp"`
@ -94,6 +95,7 @@ func newConfig() Config {
return Config{
LogLevel: int(dlog.LogLevel()),
ListenAddresses: []string{"127.0.0.1:53"},
LocalDoHListenAddresses: []string{"127.0.0.1:443"},
Timeout: 5000,
KeepAlive: 5,
CertRefreshDelay: 240,
@ -325,7 +327,7 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
proxy.certRefreshDelayAfterFailure = time.Duration(10 * time.Second)
proxy.certIgnoreTimestamp = config.CertIgnoreTimestamp
proxy.ephemeralKeys = config.EphemeralKeys
if len(config.ListenAddresses) == 0 {
if len(config.ListenAddresses) == 0 && len(config.LocalDoHListenAddresses) == 0 {
dlog.Debug("No local IP/port configured")
}
@ -349,6 +351,7 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
proxy.serversInfo.lbEstimator = config.LBEstimator
proxy.listenAddresses = config.ListenAddresses
proxy.localDoHListenAddresses = config.LocalDoHListenAddresses
proxy.daemonize = config.Daemonize
proxy.pluginBlockIPv6 = config.BlockIPv6
proxy.cache = config.Cache

View File

@ -0,0 +1,31 @@
package main
import (
"net"
"net/http"
"github.com/jedisct1/dlog"
)
type localDoHHandler struct {
}
func (handler localDoHHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
dataType := "application/dns-message"
if request.Header.Get("Content-Type") != dataType {
writer.WriteHeader(400)
return
}
writer.WriteHeader(200)
writer.Header().Add("Server", "dnscrypt-proxy")
writer.Header().Add("Content-Type", "application/dns-message")
writer.Write([]byte("OK\n"))
}
func (proxy *Proxy) localDoHListener(acceptPc *net.TCPListener) {
defer acceptPc.Close()
httpServer := &http.Server{ReadTimeout: proxy.timeout, WriteTimeout: proxy.timeout, Handler: localDoHHandler{}}
if err := httpServer.Serve(acceptPc); err != nil {
dlog.Fatal(err)
}
}

View File

@ -31,6 +31,7 @@ type Proxy struct {
certIgnoreTimestamp bool
mainProto string
listenAddresses []string
localDoHListenAddresses []string
daemonize bool
registeredServers []RegisteredServer
registeredRelays []RegisteredServer
@ -75,7 +76,7 @@ type Proxy struct {
showCerts bool
}
func (proxy *Proxy) addListener(listenAddrStr string) {
func (proxy *Proxy) addDNSListener(listenAddrStr string) {
listenUDPAddr, err := net.ResolveUDPAddr("udp", listenAddrStr)
if err != nil {
dlog.Fatal(err)
@ -121,7 +122,6 @@ func (proxy *Proxy) addListener(listenAddrStr string) {
FileDescriptors = append(FileDescriptors, fdUDP)
FileDescriptors = append(FileDescriptors, fdTCP)
return
}
// child
@ -144,6 +144,48 @@ func (proxy *Proxy) addListener(listenAddrStr string) {
go proxy.tcpListener(listenerTCP.(*net.TCPListener))
}
func (proxy *Proxy) addLocalDoHListener(listenAddrStr string) {
listenTCPAddr, err := net.ResolveTCPAddr("tcp", listenAddrStr)
if err != nil {
dlog.Fatal(err)
}
// if 'userName' is not set, continue as before
if len(proxy.userName) <= 0 {
if err := proxy.localDoHListenerFromAddr(listenTCPAddr); err != nil {
dlog.Fatal(err)
}
return
}
// if 'userName' is set and we are the parent process
if !proxy.child {
// parent
listenerTCP, err := net.ListenTCP("tcp", listenTCPAddr)
if err != nil {
dlog.Fatal(err)
}
fdTCP, err := listenerTCP.File() // On Windows, the File method of TCPListener is not implemented.
if err != nil {
dlog.Fatalf("Unable to switch to a different user: %v", err)
}
defer listenerTCP.Close()
FileDescriptors = append(FileDescriptors, fdTCP)
return
}
// child
listenerTCP, err := net.FileListener(os.NewFile(uintptr(3+FileDescriptorNum), "listenerTCP"))
if err != nil {
dlog.Fatalf("Unable to switch to a different user: %v", err)
}
FileDescriptorNum++
dlog.Noticef("Now listening to %v [HTTP]", listenAddrStr)
go proxy.localDoHListener(listenerTCP.(*net.TCPListener))
}
func (proxy *Proxy) StartProxy() {
proxy.questionSizeEstimator = NewQuestionSizeEstimator()
if _, err := crypto_rand.Read(proxy.proxySecretKey[:]); err != nil {
@ -154,8 +196,12 @@ func (proxy *Proxy) StartProxy() {
proxy.serversInfo.registerServer(registeredServer.name, registeredServer.stamp)
}
for _, listenAddrStr := range proxy.listenAddresses {
proxy.addListener(listenAddrStr)
proxy.addDNSListener(listenAddrStr)
}
for _, listenAddrStr := range proxy.localDoHListenAddresses {
proxy.addLocalDoHListener(listenAddrStr)
}
// if 'userName' is set and we are the parent process drop privilege and exit
if len(proxy.userName) > 0 && !proxy.child {
proxy.dropPrivilege(proxy.userName, FileDescriptors)
@ -270,6 +316,16 @@ func (proxy *Proxy) tcpListenerFromAddr(listenAddr *net.TCPAddr) error {
return nil
}
func (proxy *Proxy) localDoHListenerFromAddr(listenAddr *net.TCPAddr) error {
acceptPc, err := net.ListenTCP("tcp", listenAddr)
if err != nil {
return err
}
dlog.Noticef("Now listening to %v [HTTP]", listenAddr)
go proxy.localDoHListener(acceptPc)
return nil
}
func (proxy *Proxy) prepareForRelay(ip net.IP, port int, encryptedQuery *[]byte) {
anonymizedDNSHeader := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00}
relayedQuery := append(anonymizedDNSHeader, ip.To16()...)