Add built-in support for Tor

This commit is contained in:
Frank Denis 2018-06-06 15:54:51 +02:00
parent 977dcad826
commit 0166f21b27
5 changed files with 53 additions and 16 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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 {