Skip to content

Commit

Permalink
Support: relay randomization (removed unncessary file. thanks @lifenj…
Browse files Browse the repository at this point in the history
  • Loading branch information
Jun Kurihara committed Feb 9, 2021
1 parent fcd9225 commit e81f1ca
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 38 deletions.
6 changes: 6 additions & 0 deletions dnscrypt-proxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ func newConfig() Config {
},
AnonymizedDNS: AnonymizedDNSConfig{
DirectCertFallback: true,
RelayRandomization: false,
},
}
}
Expand Down Expand Up @@ -232,6 +233,7 @@ type AnonymizedDNSConfig struct {
Routes []AnonymizedDNSRouteConfig `toml:"routes"`
SkipIncompatible bool `toml:"skip_incompatible"`
DirectCertFallback bool `toml:"direct_cert_fallback"`
RelayRandomization bool `toml:"relay_randomization"`
}

type BrokenImplementationsConfig struct {
Expand Down Expand Up @@ -614,6 +616,7 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
}
proxy.skipAnonIncompatibleResolvers = config.AnonymizedDNS.SkipIncompatible
proxy.anonDirectCertFallback = config.AnonymizedDNS.DirectCertFallback
proxy.anonRelayRandomization = config.AnonymizedDNS.RelayRandomization

if config.DoHClientX509AuthLegacy.Creds != nil {
return errors.New("[tls_client_auth] has been renamed to [doh_client_x509_auth] - Update your config file")
Expand Down Expand Up @@ -733,6 +736,9 @@ func ConfigLoad(proxy *Proxy, flags *ConfigFlags) error {
dlog.Noticef("Anonymized DNS: routing everything via %v", via)
}
}
if proxy.anonRelayRandomization {
dlog.Noticef("Anonymized DNS: relay randomization turned on")
}
}
if *flags.Check {
dlog.Notice("Configuration successfully checked")
Expand Down
25 changes: 20 additions & 5 deletions dnscrypt-proxy/dnsutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"encoding/binary"
"errors"
"math/rand"
"net"
"strings"
"time"
Expand Down Expand Up @@ -372,7 +373,7 @@ func DNSExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress strin
}
return nil, 0, false, err
}
dlog.Infof("Unable to get the public key for [%v] via relay [%v], retrying over a direct connection", *serverName, relay.RelayUDPAddr.IP)
dlog.Infof("Unable to get the public key for [%v] via relay [%v], retrying over a direct connection", *serverName, relay.RelayUDPAddrs)
relay = nil
}
}
Expand Down Expand Up @@ -403,9 +404,16 @@ func _dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress stri
return DNSExchangeResponse{err: err}
}
upstreamAddr := udpAddr
if relay != nil {
if relay != nil && len(relay.RelayUDPAddrs) > 0 {
var relayIdx int
if proxy.anonRelayRandomization {
relayIdx = rand.Intn(len(relay.RelayUDPAddrs))
} else {
relayIdx = 0
}
proxy.prepareForRelay(udpAddr.IP, udpAddr.Port, &binQuery)
upstreamAddr = relay.RelayUDPAddr
upstreamAddr = relay.RelayUDPAddrs[relayIdx]
dlog.Debugf("[%v] _dnsExchange: via relay [%v] (UDP)", serverAddress, relay.RelayUDPAddrs[relayIdx].IP)
}
now := time.Now()
pc, err := net.DialUDP("udp", nil, upstreamAddr)
Expand Down Expand Up @@ -436,9 +444,16 @@ func _dnsExchange(proxy *Proxy, proto string, query *dns.Msg, serverAddress stri
return DNSExchangeResponse{err: err}
}
upstreamAddr := tcpAddr
if relay != nil {
if relay != nil && len(relay.RelayTCPAddrs) > 0 {
var relayIdx int
if proxy.anonRelayRandomization {
relayIdx = rand.Intn(len(relay.RelayTCPAddrs))
} else {
relayIdx = 0
}
proxy.prepareForRelay(tcpAddr.IP, tcpAddr.Port, &binQuery)
upstreamAddr = relay.RelayTCPAddr
upstreamAddr = relay.RelayTCPAddrs[relayIdx]
dlog.Debugf("[%v] _dnsExchange: via relay [%v] (TCP)", serverAddress, relay.RelayTCPAddrs[relayIdx].IP)
}
now := time.Now()
var pc net.Conn
Expand Down
4 changes: 3 additions & 1 deletion dnscrypt-proxy/example-dnscrypt-proxy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,9 @@ skip_incompatible = false

# direct_cert_fallback = false


# If multiple relays are specified, one of them are randomly chosen when a query get issued.
# Default value is false (non-randomized).
# relay_randomization = true

###############################
# DNS64 #
Expand Down
20 changes: 18 additions & 2 deletions dnscrypt-proxy/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
crypto_rand "crypto/rand"
"encoding/binary"
"math/rand"
"net"
"os"
"runtime"
Expand Down Expand Up @@ -91,6 +92,7 @@ type Proxy struct {
certIgnoreTimestamp bool
skipAnonIncompatibleResolvers bool
anonDirectCertFallback bool
anonRelayRandomization bool
pluginBlockUndelegated bool
child bool
daemonize bool
Expand Down Expand Up @@ -477,7 +479,14 @@ func (proxy *Proxy) prepareForRelay(ip net.IP, port int, encryptedQuery *[]byte)
func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
upstreamAddr := serverInfo.UDPAddr
if serverInfo.Relay != nil && serverInfo.Relay.Dnscrypt != nil {
upstreamAddr = serverInfo.Relay.Dnscrypt.RelayUDPAddr
var relayIdx int
if proxy.anonRelayRandomization {
relayIdx = rand.Intn(len(serverInfo.Relay.Dnscrypt.RelayUDPAddrs))
} else {
relayIdx = 0
}
upstreamAddr = serverInfo.Relay.Dnscrypt.RelayUDPAddrs[relayIdx]
dlog.Debugf("[%v] exchangeWithUDPServer: via relay [%v]", serverInfo.Name, serverInfo.Relay.Dnscrypt.RelayUDPAddrs[relayIdx].IP)
}
var err error
var pc net.Conn
Expand Down Expand Up @@ -515,7 +524,14 @@ func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32
func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
upstreamAddr := serverInfo.TCPAddr
if serverInfo.Relay != nil && serverInfo.Relay.Dnscrypt != nil {
upstreamAddr = serverInfo.Relay.Dnscrypt.RelayTCPAddr
var relayIdx int
if proxy.anonRelayRandomization {
relayIdx = rand.Intn(len(serverInfo.Relay.Dnscrypt.RelayTCPAddrs))
} else {
relayIdx = 0
}
upstreamAddr = serverInfo.Relay.Dnscrypt.RelayTCPAddrs[relayIdx]
dlog.Debugf("[%v] exchangeWithTCPServer: via relay [%v]", serverInfo.Name, serverInfo.Relay.Dnscrypt.RelayTCPAddrs[relayIdx].IP)
}
var err error
var pc net.Conn
Expand Down
104 changes: 74 additions & 30 deletions dnscrypt-proxy/serversInfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ func (LBStrategyRandom) getCandidate(serversCount int) int {
var DefaultLBStrategy = LBStrategyP2{}

type DNSCryptRelay struct {
RelayUDPAddr *net.UDPAddr
RelayTCPAddr *net.TCPAddr
RelayUDPAddrs []*net.UDPAddr
RelayTCPAddrs []*net.TCPAddr
}

type ODoHRelay struct{}
Expand Down Expand Up @@ -281,7 +281,7 @@ func fetchServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp, isNew
return ServerInfo{}, fmt.Errorf("Unsupported protocol for [%s]: [%s]", name, stamp.Proto.String())
}

func findFarthestRoute(proxy *Proxy, name string, relayStamps []stamps.ServerStamp) *stamps.ServerStamp {
func findFarthestRoute(proxy *Proxy, name string, relayStamps []*stamps.ServerStamp) *stamps.ServerStamp {
serverIdx := -1
proxy.serversInfo.RLock()
for i, registeredServer := range proxy.serversInfo.registeredServers {
Expand All @@ -305,8 +305,8 @@ func findFarthestRoute(proxy *Proxy, name string, relayStamps []stamps.ServerSta
}
bestRelayIdxs := make([]int, 0)
bestRelaySamePrefixBits := 128
for relayIdx, relayStamp := range relayStamps {
relayAddrStr, _ := ExtractHostAndPort(relayStamp.ServerAddrStr, 443)
for relayIdx := range relayStamps {
relayAddrStr, _ := ExtractHostAndPort(relayStamps[relayIdx].ServerAddrStr, 443)
relayAddr := net.ParseIP(relayAddrStr)
if relayAddr == nil {
continue
Expand All @@ -332,10 +332,11 @@ func findFarthestRoute(proxy *Proxy, name string, relayStamps []stamps.ServerSta
bestRelayIdxs = append(bestRelayIdxs, relayIdx)
}
}
return &relayStamps[bestRelayIdxs[rand.Intn(len(bestRelayIdxs))]]
return relayStamps[bestRelayIdxs[rand.Intn(len(bestRelayIdxs))]]
}

func route(proxy *Proxy, name string) (*Relay, error) {
// Called only when DNSCrypt at this point (Jan. 14, 2021), ODoH needs to be handled by an arg of StampProtoType.
func route(proxy *Proxy, name string, serverProto stamps.StampProtoType) (*Relay, error) {
routes := proxy.routes
if routes == nil {
return nil, nil
Expand Down Expand Up @@ -378,40 +379,83 @@ func route(proxy *Proxy, name string) (*Relay, error) {
proxy.serversInfo.RUnlock()
}
}

if len(relayStamps) == 0 {
return nil, fmt.Errorf("Empty relay set for [%v]", name)
}
var relayCandidateStamp *stamps.ServerStamp
if !wildcard || len(relayStamps) == 1 {
relayCandidateStamp = &relayStamps[rand.Intn(len(relayStamps))]

dlog.Debugf(
"Choosing relay candidates for [%v] (%v) - wildcard: %v, relayRandomization: %v",
name, serverProto.String(), wildcard, proxy.anonRelayRandomization,
)

// filtering with serverProto
var filteredStamps [](*stamps.ServerStamp)
for i, stamp := range relayStamps {
if serverProto == stamp.Proto ||
(serverProto == stamps.StampProtoTypeDNSCrypt && stamp.Proto == stamps.StampProtoTypeDNSCryptRelay) ||
(serverProto == stamps.StampProtoTypeODoHTarget && stamp.Proto == stamps.StampProtoTypeODoHRelay) {
filteredStamps = append(filteredStamps, &relayStamps[i])
}
}
dlog.Debugf("Relay candidates supporting [%v]: %v", serverProto.String(), filteredStamps)

var relayCandidateStamps [](*stamps.ServerStamp)
if proxy.anonRelayRandomization {
for i := range filteredStamps {
relayCandidateStamps = append(relayCandidateStamps, filteredStamps[i])
}
} else if !wildcard || len(filteredStamps) == 1 {
relayCandidateStamps = append(relayCandidateStamps, filteredStamps[rand.Intn(len(filteredStamps))])
} else {
relayCandidateStamp = findFarthestRoute(proxy, name, relayStamps)
farthest := findFarthestRoute(proxy, name, filteredStamps)
relayCandidateStamps = append(relayCandidateStamps, farthest)
}
if relayCandidateStamp == nil {
if len(relayCandidateStamps) == 0 {
return nil, fmt.Errorf("No valid relay for server [%v]", name)
}
relayName := relayCandidateStamp.ServerAddrStr
dlog.Debugf("Stamps of chosen relay candidates for [%v]: %v", name, relayCandidateStamps)

/// maybe not required? only used for print.
var relayNamesForPrint []string
proxy.serversInfo.RLock()
for _, registeredServer := range proxy.serversInfo.registeredRelays {
if registeredServer.stamp.ServerAddrStr == relayCandidateStamp.ServerAddrStr {
relayName = registeredServer.name
break
for i := range relayCandidateStamps {
relayName := relayCandidateStamps[i].ServerAddrStr
for _, registeredServer := range proxy.serversInfo.registeredRelays {
if registeredServer.stamp.ServerAddrStr == relayCandidateStamps[i].ServerAddrStr {
relayName = registeredServer.name
break
}
}
relayNamesForPrint = append(relayNamesForPrint, relayName)
}

proxy.serversInfo.RUnlock()
switch relayCandidateStamp.Proto {
case stamps.StampProtoTypeDNSCrypt, stamps.StampProtoTypeDNSCryptRelay:
relayUDPAddr, err := net.ResolveUDPAddr("udp", relayCandidateStamp.ServerAddrStr)
if err != nil {
return nil, err
}
relayTCPAddr, err := net.ResolveTCPAddr("tcp", relayCandidateStamp.ServerAddrStr)
if err != nil {
return nil, err
switch serverProto {
case stamps.StampProtoTypeDNSCrypt:
var relayUDPAddrs [](*net.UDPAddr)
var relayTCPAddrs [](*net.TCPAddr)
for _, stamp := range relayCandidateStamps {
relayUDPAddr, err := net.ResolveUDPAddr("udp", stamp.ServerAddrStr)
if err != nil {
return nil, err
}
relayUDPAddrs = append(relayUDPAddrs, relayUDPAddr)
relayTCPAddr, err := net.ResolveTCPAddr("tcp", stamp.ServerAddrStr)
if err != nil {
return nil, err
}
relayTCPAddrs = append(relayTCPAddrs, relayTCPAddr)
}
dlog.Noticef("Anonymizing queries for [%v] via [%v]", name, relayName)
return &Relay{Proto: stamps.StampProtoTypeDNSCryptRelay, Dnscrypt: &DNSCryptRelay{RelayUDPAddr: relayUDPAddr, RelayTCPAddr: relayTCPAddr}}, nil
case stamps.StampProtoTypeODoHRelay:
dlog.Noticef("Anonymizing queries for [%v] via [%v]", name, relayNamesForPrint)
return &Relay{
Proto: stamps.StampProtoTypeDNSCryptRelay,
Dnscrypt: &DNSCryptRelay{
RelayUDPAddrs: relayUDPAddrs,
RelayTCPAddrs: relayTCPAddrs,
},
}, nil
case stamps.StampProtoTypeODoHTarget:
return &Relay{Proto: stamps.StampProtoTypeODoHRelay, ODoH: &ODoHRelay{}}, nil
}
return nil, fmt.Errorf("Invalid relay set for server [%v]", name)
Expand All @@ -434,7 +478,7 @@ func fetchDNSCryptServerInfo(proxy *Proxy, name string, stamp stamps.ServerStamp
break
}
}
relay, err := route(proxy, name)
relay, err := route(proxy, name, stamp.Proto)
if err != nil {
return ServerInfo{}, err
}
Expand Down

0 comments on commit e81f1ca

Please sign in to comment.