From 0166f21b27c9a22479cc3e302db6947a4de05637 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 6 Jun 2018 15:54:51 +0200 Subject: [PATCH] Add built-in support for Tor --- dnscrypt-proxy/common.go | 2 +- dnscrypt-proxy/config.go | 16 +++++++++++ dnscrypt-proxy/example-dnscrypt-proxy.toml | 8 ++++++ dnscrypt-proxy/proxy.go | 33 +++++++++++++--------- dnscrypt-proxy/xtransport.go | 10 +++++-- 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/dnscrypt-proxy/common.go b/dnscrypt-proxy/common.go index e4b2850c..ab9b1111 100644 --- a/dnscrypt-proxy/common.go +++ b/dnscrypt-proxy/common.go @@ -47,7 +47,7 @@ func PrefixWithSize(packet []byte) ([]byte, error) { return packet, nil } -func ReadPrefixed(conn *net.TCPConn) ([]byte, error) { +func ReadPrefixed(conn *net.Conn) ([]byte, error) { buf := make([]byte, 2+MaxDNSPacketSize) packetLength, pos := -1, 0 for { diff --git a/dnscrypt-proxy/config.go b/dnscrypt-proxy/config.go index 6e3f19d1..443e49eb 100644 --- a/dnscrypt-proxy/config.go +++ b/dnscrypt-proxy/config.go @@ -6,6 +6,7 @@ import ( "flag" "fmt" "net" + "net/url" "os" "path" "path/filepath" @@ -15,6 +16,7 @@ import ( "github.com/BurntSushi/toml" "github.com/jedisct1/dlog" stamps "github.com/jedisct1/go-dnsstamps" + netproxy "golang.org/x/net/proxy" ) type Config struct { @@ -27,6 +29,7 @@ type Config struct { ForceTCP bool `toml:"force_tcp"` Timeout int `toml:"timeout"` KeepAlive int `toml:"keepalive"` + Proxy string `toml:"proxy"` CertRefreshDelay int `toml:"cert_refresh_delay"` CertIgnoreTimestamp bool `toml:"cert_ignore_timestamp"` EphemeralKeys bool `toml:"dnscrypt_ephemeral_keys"` @@ -237,6 +240,19 @@ func ConfigLoad(proxy *Proxy, svcFlag *string) error { proxy.xTransport.useIPv4 = config.SourceIPv4 proxy.xTransport.useIPv6 = config.SourceIPv6 proxy.xTransport.keepAlive = time.Duration(config.KeepAlive) * time.Second + + if len(config.Proxy) > 0 { + proxyDialerURL, err := url.Parse(config.Proxy) + if err != nil { + dlog.Fatalf("Unable to parse proxy url [%v]", config.Proxy) + } + proxyDialer, err := netproxy.FromURL(proxyDialerURL, netproxy.Direct) + if err != nil { + dlog.Fatalf("Unable to use the proxy: [%v]", err) + } + proxy.xTransport.proxyDialer = &proxyDialer + } + proxy.xTransport.rebuildTransport() proxy.timeout = time.Duration(config.Timeout) * time.Millisecond diff --git a/dnscrypt-proxy/example-dnscrypt-proxy.toml b/dnscrypt-proxy/example-dnscrypt-proxy.toml index c0a092b2..124109ef 100644 --- a/dnscrypt-proxy/example-dnscrypt-proxy.toml +++ b/dnscrypt-proxy/example-dnscrypt-proxy.toml @@ -77,6 +77,14 @@ require_nofilter = true force_tcp = false +## HTTP / SOCKS proxy +## Uncomment the following line to route all TCP connections to a local Tor node +## Tor doesn't support UDP, so set `force_tcp` to `true` as well. + +# proxy = "socks5://127.0.0.1:9050" + + + ## How long a DNS query will wait for a response, in milliseconds timeout = 2500 diff --git a/dnscrypt-proxy/proxy.go b/dnscrypt-proxy/proxy.go index 6bf00f29..525dc60d 100644 --- a/dnscrypt-proxy/proxy.go +++ b/dnscrypt-proxy/proxy.go @@ -176,7 +176,7 @@ func (proxy *Proxy) tcpListener(acceptPc *net.TCPListener) { } defer proxy.clientsCountDec() clientPc.SetDeadline(time.Now().Add(proxy.timeout)) - packet, err := ReadPrefixed(clientPc.(*net.TCPConn)) + packet, err := ReadPrefixed(&clientPc) if err != nil || len(packet) < MinDNSPacketSize { return } @@ -214,22 +214,29 @@ func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32 } func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) { - pc, err := net.DialTCP("tcp", nil, serverInfo.TCPAddr) + proxyDialer := proxy.xTransport.proxyDialer + var pc *net.Conn + if proxyDialer == nil { + pcx, err := net.DialTCP("tcp", nil, serverInfo.TCPAddr) + if err != nil { + return nil, err + } + pc = interface{}(pcx).(*net.Conn) + } else { + pcx, err := (*proxyDialer).Dial("tcp", serverInfo.TCPAddr.String()) + if err != nil { + return nil, err + } + pc = &pcx + } + (*pc).SetDeadline(time.Now().Add(serverInfo.Timeout)) + encryptedQuery, err := PrefixWithSize(encryptedQuery) if err != nil { return nil, err } - pc.SetDeadline(time.Now().Add(serverInfo.Timeout)) - encryptedQuery, err = PrefixWithSize(encryptedQuery) - if err != nil { - return nil, err - } - pc.Write(encryptedQuery) - + (*pc).Write(encryptedQuery) encryptedResponse, err := ReadPrefixed(pc) - pc.Close() - if err != nil { - return nil, err - } + (*pc).Close() return proxy.Decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce) } diff --git a/dnscrypt-proxy/xtransport.go b/dnscrypt-proxy/xtransport.go index e0645b13..7f5fc9cb 100644 --- a/dnscrypt-proxy/xtransport.go +++ b/dnscrypt-proxy/xtransport.go @@ -21,6 +21,7 @@ import ( stamps "github.com/jedisct1/go-dnsstamps" "github.com/miekg/dns" "golang.org/x/net/http2" + netproxy "golang.org/x/net/proxy" ) const DefaultFallbackResolver = "9.9.9.9:53" @@ -41,6 +42,7 @@ type XTransport struct { useIPv6 bool tlsDisableSessionTickets bool tlsCipherSuite []uint16 + proxyDialer *netproxy.Dialer } var DefaultKeepAlive = 5 * time.Second @@ -74,7 +76,6 @@ func (xTransport *XTransport) rebuildTransport() { (*xTransport.transport).CloseIdleConnections() } timeout := xTransport.timeout - dialer := &net.Dialer{Timeout: timeout, KeepAlive: timeout, DualStack: true} transport := &http.Transport{ DisableKeepAlives: false, DisableCompression: true, @@ -95,7 +96,12 @@ func (xTransport *XTransport) rebuildTransport() { dlog.Debugf("[%s] IP address was not cached", host) } addrStr = ipOnly + ":" + strconv.Itoa(port) - return dialer.DialContext(ctx, network, addrStr) + if xTransport.proxyDialer == nil { + dialer := &net.Dialer{Timeout: timeout, KeepAlive: timeout, DualStack: true} + return dialer.DialContext(ctx, network, addrStr) + } else { + return (*xTransport.proxyDialer).Dial(network, addrStr) + } }, } if xTransport.tlsDisableSessionTickets || xTransport.tlsCipherSuite != nil {