Skip to content

Commit af9815c

Browse files
committed
dnscrypt: multiple anonmyized relays per server
1 parent cf58bfe commit af9815c

File tree

3 files changed

+88
-80
lines changed

3 files changed

+88
-80
lines changed

intra/dnscrypt/crypto.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func encrypt(
9797
var paddedLength int
9898
if useudp { // using udp
9999
paddedLength = xdns.MaxDNSUDPSafePacketSize
100-
} else if serverInfo.RelayTCPAddr != nil { // tcp, with relay
100+
} else if len(serverInfo.RelayTCPAddrs) > 0 { // tcp, with relay
101101
paddedLength = xdns.MaxDNSPacketSize
102102
} else { // tcp, without relay
103103
minQuestionSize := QueryOverhead + len(packet)

intra/dnscrypt/multiserver.go

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"encoding/binary"
2121
"errors"
2222
"fmt"
23+
"math/rand"
2324
"net"
2425
"strings"
2526
"sync"
@@ -81,10 +82,16 @@ var (
8182
errNoConn = errors.New("dnscrypt: no connection")
8283
)
8384

84-
func udpExchange(pid string, serverInfo *serverinfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
85+
func chooseAny[t net.Addr](s []t) t {
86+
return s[rand.Intn(len(s))]
87+
}
88+
89+
func udpExchange(pid string, serverInfo *serverinfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) (res []byte, relay net.Addr, err error) {
8590
upstreamAddr := serverInfo.UDPAddr
86-
if serverInfo.RelayUDPAddr != nil {
87-
upstreamAddr = serverInfo.RelayUDPAddr
91+
userelay := len(serverInfo.RelayUDPAddrs) > 0
92+
if userelay {
93+
upstreamAddr = chooseAny(serverInfo.RelayUDPAddrs)
94+
relay = upstreamAddr
8895
}
8996

9097
pc, err := serverInfo.dialudp(pid, upstreamAddr)
@@ -93,14 +100,14 @@ func udpExchange(pid string, serverInfo *serverinfo, sharedKey *[32]byte, encryp
93100
err = errNoConn
94101
}
95102
log.E("dnscrypt: udp: dialing %s; hasConn? %s(%t); err: %v", serverInfo, pid, pc != nil, err)
96-
return nil, err
103+
return
97104
}
98105

99106
defer pc.Close()
100107
if err = pc.SetDeadline(time.Now().Add(timeout8s)); err != nil {
101-
return nil, err
108+
return
102109
}
103-
if serverInfo.RelayUDPAddr != nil {
110+
if userelay {
104111
prepareForRelay(serverInfo.UDPAddr.IP, serverInfo.UDPAddr.Port, &encryptedQuery)
105112
}
106113
// TODO: use a pool
@@ -111,27 +118,31 @@ func udpExchange(pid string, serverInfo *serverinfo, sharedKey *[32]byte, encryp
111118
core.Recycle(bptr)
112119
}()
113120
for tries := 2; tries > 0; tries-- {
114-
if _, err := pc.Write(encryptedQuery); err != nil {
121+
if _, err = pc.Write(encryptedQuery); err != nil {
115122
log.E("dnscrypt: udp: [%s] write err; [%v]", serverInfo.Name, err)
116-
return nil, err
123+
return
117124
}
118-
length, err := pc.Read(encryptedResponse)
125+
var length int
126+
length, err = pc.Read(encryptedResponse)
119127
if err == nil {
120128
encryptedResponse = encryptedResponse[:length]
121129
break
122130
} else if tries <= 0 {
123131
log.E("dnscrypt: udp: [%s] read err; quit [%v]", serverInfo.Name, err)
124-
return nil, err
132+
return
125133
}
126134
log.D("dnscrypt: udp: [%s] read err; retry [%v]", serverInfo.Name, err)
127135
}
128-
return decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce)
136+
res, err = decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce)
137+
return
129138
}
130139

131-
func tcpExchange(pid string, serverInfo *serverinfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
140+
func tcpExchange(pid string, serverInfo *serverinfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) (res []byte, relay net.Addr, err error) {
132141
upstreamAddr := serverInfo.TCPAddr
133-
if serverInfo.RelayTCPAddr != nil {
134-
upstreamAddr = serverInfo.RelayTCPAddr
142+
userelay := len(serverInfo.RelayTCPAddrs) > 0
143+
if userelay {
144+
upstreamAddr = chooseAny(serverInfo.RelayTCPAddrs)
145+
relay = upstreamAddr
135146
}
136147

137148
pc, err := serverInfo.dialtcp(pid, upstreamAddr)
@@ -140,31 +151,32 @@ func tcpExchange(pid string, serverInfo *serverinfo, sharedKey *[32]byte, encryp
140151
err = errNoConn
141152
}
142153
log.E("dnscrypt: tcp: dialing %s; hasConn? %s(%t); err: %v", serverInfo, pid, pc != nil, err)
143-
return nil, err
154+
return
144155
}
145156
defer pc.Close()
146-
if derr := pc.SetDeadline(time.Now().Add(timeout8s)); derr != nil {
147-
log.E("dnscrypt: tcp: err deadline: %v", derr)
148-
return nil, derr
157+
if err = pc.SetDeadline(time.Now().Add(timeout8s)); err != nil {
158+
log.E("dnscrypt: tcp: err deadline: %v", err)
159+
return
149160
}
150-
if serverInfo.RelayTCPAddr != nil {
161+
if userelay {
151162
prepareForRelay(serverInfo.TCPAddr.IP, serverInfo.TCPAddr.Port, &encryptedQuery)
152163
}
153164
encryptedQuery, err = xdns.PrefixWithSize(encryptedQuery)
154165
if err != nil {
155166
log.E("dnscrypt: tcp: prefix(q) %s err: %v", serverInfo, err)
156-
return nil, err
167+
return
157168
}
158-
if _, werr := pc.Write(encryptedQuery); werr != nil {
159-
log.E("dnscrypt: tcp: err write: %v", serverInfo, werr)
160-
return nil, werr
169+
if _, err = pc.Write(encryptedQuery); err != nil {
170+
log.E("dnscrypt: tcp: err write: %v", serverInfo, err)
171+
return
161172
}
162173
encryptedResponse, err := xdns.ReadPrefixed(&pc)
163174
if err != nil {
164175
log.E("dnscrypt: tcp: read(enc) %s err %v", serverInfo, err)
165-
return nil, err
176+
return
166177
}
167-
return decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce)
178+
res, err = decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce)
179+
return
168180
}
169181

170182
func prepareForRelay(ip net.IP, port int, eq *[]byte) {
@@ -177,7 +189,7 @@ func prepareForRelay(ip net.IP, port int, eq *[]byte) {
177189
*eq = relayedQuery
178190
}
179191

180-
func query(pid string, packet []byte, serverInfo *serverinfo, useudp bool) (response []byte, qerr *dnsx.QueryError) {
192+
func query(pid string, packet []byte, serverInfo *serverinfo, useudp bool) (response []byte, relay net.Addr, qerr *dnsx.QueryError) {
181193
if len(packet) < xdns.MinDNSPacketSize {
182194
qerr = dnsx.NewBadQueryError(errQueryTooShort)
183195
return
@@ -235,7 +247,7 @@ func query(pid string, packet []byte, serverInfo *serverinfo, useudp bool) (resp
235247
}
236248

237249
if useudp {
238-
response, err = udpExchange(pid, serverInfo, sharedKey, encryptedQuery, clientNonce)
250+
response, relay, err = udpExchange(pid, serverInfo, sharedKey, encryptedQuery, clientNonce)
239251
}
240252
tcpfallback := useudp && err != nil
241253
if tcpfallback {
@@ -244,7 +256,7 @@ func query(pid string, packet []byte, serverInfo *serverinfo, useudp bool) (resp
244256
// if udp errored out, try over tcp; or use tcp if udp is disabled
245257
if tcpfallback || !useudp {
246258
useudp = false // switched to tcp
247-
response, err = tcpExchange(pid, serverInfo, sharedKey, encryptedQuery, clientNonce)
259+
response, relay, err = tcpExchange(pid, serverInfo, sharedKey, encryptedQuery, clientNonce)
248260
}
249261

250262
if err != nil {
@@ -291,14 +303,15 @@ func query(pid string, packet []byte, serverInfo *serverinfo, useudp bool) (resp
291303
// resolve resolves incoming DNS query, data
292304
func resolve(network string, data []byte, si *serverinfo, smm *x.DNSSummary) (response []byte, err error) {
293305
var qerr *dnsx.QueryError
306+
var anonrelayaddr net.Addr
294307

295308
before := time.Now()
296309

297310
proto, pid := xdns.Net2ProxyID(network)
298311
useudp := proto == dnsx.NetTypeUDP
299312

300313
// si may be nil
301-
response, qerr = query(pid, data, si, useudp)
314+
response, anonrelayaddr, qerr = query(pid, data, si, useudp)
302315

303316
after := time.Now()
304317

@@ -309,8 +322,8 @@ func resolve(network string, data []byte, si *serverinfo, smm *x.DNSSummary) (re
309322
var anonrelay string
310323
if si != nil {
311324
resolver = si.HostName
312-
if si.RelayTCPAddr != nil {
313-
anonrelay = si.RelayTCPAddr.IP.String()
325+
if anonrelayaddr != nil { // may be nil
326+
anonrelay = anonrelayaddr.String()
314327
}
315328
}
316329

@@ -326,7 +339,7 @@ func resolve(network string, data []byte, si *serverinfo, smm *x.DNSSummary) (re
326339
smm.RCode = xdns.Rcode(ans)
327340
smm.RTtl = xdns.RTtl(ans)
328341
smm.Server = resolver
329-
smm.RelayServer = anonrelay
342+
smm.RelayServer = anonrelay // may be empty
330343
smm.Status = status
331344

332345
noAnonRelay := len(anonrelay) <= 0
@@ -394,15 +407,15 @@ func (proxy *DcMulti) start() error {
394407
curve25519.ScalarBaseMult(&proxy.proxyPublicKey, &proxy.proxySecretKey)
395408

396409
_, err := proxy.Refresh()
397-
if len(proxy.serversInfo.registeredServers) > 0 {
410+
if proxy.serversInfo.len() > 0 {
398411
go func(ctx context.Context) {
399412
for {
400413
select {
401414
case <-ctx.Done():
402415
log.I("dnscrypt: cert refresh stopped")
403416
return
404417
default:
405-
hasServers := len(proxy.serversInfo.registeredServers) > 0
418+
hasServers := proxy.serversInfo.len() > 0
406419
allDead := len(proxy.liveServers) == 0
407420
delay := certRefreshDelay
408421
if hasServers && allDead {

intra/dnscrypt/servers.go

Lines changed: 40 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ type serverinfo struct {
5454
HostName string
5555
UDPAddr *net.UDPAddr
5656
TCPAddr *net.TCPAddr
57-
RelayUDPAddr *net.UDPAddr
58-
RelayTCPAddr *net.TCPAddr
57+
RelayUDPAddrs []*net.UDPAddr
58+
RelayTCPAddrs []*net.TCPAddr
5959
status int
6060
proxies ipn.Proxies // proxy-provider, may be nil
6161
relay ipn.Proxy // proxy relay to use, may be nil
@@ -199,14 +199,11 @@ func fetchDNSCryptServerInfo(proxy *DcMulti, name string, stamp stamps.ServerSta
199199
stamp.ServerPk = serverPk
200200
}
201201

202-
// relayudpaddr and relaytcpaddr may be nil, even if err == nil
203-
relayudpaddr, relaytcpaddr, err := route(proxy, name)
204-
if err != nil {
205-
return serverinfo{}, err
206-
}
202+
// relayudpaddrs and relaytcpaddrs may be nil, even if err == nil
203+
relayudpaddrs, relaytcpaddrs := route(proxy)
207204
// iff tcp relay is unset, unset udp relay too
208-
if relaytcpaddr == nil {
209-
relayudpaddr = nil
205+
if len(relaytcpaddrs) <= 0 {
206+
relayudpaddrs = nil
210207
}
211208
// note: relays are not used to fetch certs due to multiple issues reported by users
212209
certInfo, err := fetchCurrentDNSCryptCert(proxy, &name, stamp.ServerPk, stamp.ServerAddrStr, stamp.ProviderName)
@@ -243,14 +240,14 @@ func fetchDNSCryptServerInfo(proxy *DcMulti, name string, stamp stamps.ServerSta
243240
Name: name,
244241
UDPAddr: udpaddr,
245242
TCPAddr: tcpaddr,
246-
RelayTCPAddr: relaytcpaddr,
247-
RelayUDPAddr: relayudpaddr,
243+
RelayTCPAddrs: relaytcpaddrs,
244+
RelayUDPAddrs: relayudpaddrs,
248245
proxies: px,
249246
relay: relay,
250247
dialer: dialer,
251248
est: core.NewP50Estimator(),
252249
}
253-
log.I("dnscrypt: (%s) setup: %s; relay? %t", name, si.HostName, relay != nil)
250+
log.I("dnscrypt: (%s) setup: %s; anonrelay? %t, proxy? %t", name, si.HostName, relaytcpaddrs != nil, relay != nil)
254251
return si, nil
255252
}
256253

@@ -259,45 +256,43 @@ func fetchDoHServerInfo(proxy *DcMulti, name string, stamp stamps.ServerStamp) (
259256
return serverinfo{}, errors.New("unsupported protocol")
260257
}
261258

262-
func route(proxy *DcMulti, name string) (udpaddr *net.UDPAddr, tcpaddr *net.TCPAddr, err error) {
263-
relayNames := proxy.routes
264-
if relayNames == nil { // no err, no relays
259+
func route(proxy *DcMulti) (udpaddrs []*net.UDPAddr, tcpaddrs []*net.TCPAddr) {
260+
relays := proxy.routes
261+
if len(relays) <= 0 { // no err, no relays
265262
return
266263
}
267264

268-
var relayName string
269-
if len(relayNames) > 0 {
270-
candidate := rand.Intn(len(relayNames))
271-
relayName = relayNames[candidate]
272-
}
273-
var relayCandidateStamp *stamps.ServerStamp
274-
if len(relayName) == 0 {
275-
err = fmt.Errorf("route declared for [%s] but no relays", name)
276-
return
277-
} else if relayStamp, serr := stamps.NewServerStampFromString(relayName); serr == nil {
278-
relayCandidateStamp = &relayStamp
279-
}
265+
udpaddrs = make([]*net.UDPAddr, 0, len(relays))
266+
tcpaddrs = make([]*net.TCPAddr, 0, len(relays))
267+
for _, rr := range relays {
268+
var rrstamp *stamps.ServerStamp
269+
if len(rr) == 0 {
270+
log.W("dnscrypt: route: skip empty relay")
271+
continue
272+
} else if relayStamp, serr := stamps.NewServerStampFromString(rr); serr == nil {
273+
rrstamp = &relayStamp
274+
}
280275

281-
if relayCandidateStamp == nil {
282-
relayCandidateStamp = &stamps.ServerStamp{
283-
ServerAddrStr: relayName, // may be a hostname or ip-address
284-
Proto: stamps.StampProtoTypeDNSCryptRelay,
276+
if rrstamp == nil {
277+
rrstamp = &stamps.ServerStamp{
278+
ServerAddrStr: rr, // may be a hostname or ip-address
279+
Proto: stamps.StampProtoTypeDNSCryptRelay,
280+
}
285281
}
286-
}
287282

288-
s, p := hostport(relayCandidateStamp.ServerAddrStr)
289-
if relayCandidateStamp != nil && (relayCandidateStamp.Proto == stamps.StampProtoTypeDNSCrypt ||
290-
relayCandidateStamp.Proto == stamps.StampProtoTypeDNSCryptRelay) {
291-
var ips []netip.Addr
292-
if ips, err = dialers.Resolve(s); err == nil && len(ips) > 0 {
293-
ipp := netip.AddrPortFrom(ips[0], p) // TODO: randomize?
294-
tcpaddr = net.TCPAddrFromAddrPort(ipp)
295-
udpaddr = net.UDPAddrFromAddrPort(ipp)
283+
host, port := hostport(rrstamp.ServerAddrStr)
284+
if rrstamp != nil && (rrstamp.Proto == stamps.StampProtoTypeDNSCrypt ||
285+
rrstamp.Proto == stamps.StampProtoTypeDNSCryptRelay) {
286+
if ips, err := dialers.Resolve(host); err == nil && len(ips) > 0 {
287+
ipp := netip.AddrPortFrom(ips[0], port) // TODO: randomize?
288+
tcpaddrs = append(tcpaddrs, net.TCPAddrFromAddrPort(ipp))
289+
udpaddrs = append(udpaddrs, net.UDPAddrFromAddrPort(ipp))
290+
} else {
291+
log.W("dnscrypt: route: zero ips for relay [%s] for server [%s]; err [%v]", rr, host, err)
292+
}
296293
} else {
297-
err = fmt.Errorf("zero ips for relay [%s@%s] for server [%s]; err [%v]", relayName, s, name, err)
294+
log.W("dnscrypt: route: invalid relay [%s]", rr)
298295
}
299-
} else {
300-
err = fmt.Errorf("invalid relay [%s] for server [%s]", relayName, name)
301296
}
302297
return
303298
}
@@ -324,8 +319,8 @@ func (s *serverinfo) String() string {
324319
if s.TCPAddr != nil {
325320
serveraddr = s.TCPAddr.String()
326321
}
327-
if s.RelayTCPAddr != nil {
328-
relayaddr = s.RelayTCPAddr.String()
322+
if s.RelayTCPAddrs != nil {
323+
relayaddr = chooseAny(s.RelayTCPAddrs).String()
329324
}
330325

331326
return serverid + ":" + servername + "/" + serveraddr + "<=>" + relayaddr

0 commit comments

Comments
 (0)