Skip to content

Commit 6234d1b

Browse files
authored
netprobe: accept multiple comma-separated addresses (#3358)
This allows probing both an IPv4 and an IPv6 address, so that network detection works on IPv4-only as well as IPv6-only networks.
1 parent 139e66d commit 6234d1b

5 files changed

Lines changed: 72 additions & 9 deletions

File tree

‎dnscrypt-proxy/example-dnscrypt-proxy.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,9 @@ netprobe_timeout = 60
381381
## when the system starts.
382382
## On other operating systems, the connection will be initialized
383383
## but nothing will be sent at all.
384+
## Multiple comma-separated addresses can be given, for example to support
385+
## both IPv4-only and IPv6-only networks. Connectivity is detected as soon
386+
## as any of them works: '9.9.9.9:53,[2620:fe::fe]:53'
384387

385388
netprobe_address = '9.9.9.9:53'
386389

‎dnscrypt-proxy/netprobe.go‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"net"
6+
"strings"
7+
)
8+
9+
// resolveNetprobeAddresses parses a netprobe address specification.
10+
// Multiple addresses (for example an IPv4 and an IPv6 address) can be
11+
// separated by commas; connectivity is detected as soon as any of them works.
12+
func resolveNetprobeAddresses(address string) ([]*net.UDPAddr, error) {
13+
var addrs []*net.UDPAddr
14+
for _, part := range strings.Split(address, ",") {
15+
part = strings.TrimSpace(part)
16+
if len(part) == 0 {
17+
continue
18+
}
19+
addr, err := net.ResolveUDPAddr("udp", part)
20+
if err != nil {
21+
return nil, err
22+
}
23+
addrs = append(addrs, addr)
24+
}
25+
if len(addrs) == 0 {
26+
return nil, errors.New("No valid netprobe address in [" + address + "]")
27+
}
28+
return addrs, nil
29+
}

‎dnscrypt-proxy/netprobe_others.go‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
2020
} else {
2121
dlog.Critical(err)
2222
}
23-
remoteUDPAddr, err := net.ResolveUDPAddr("udp", address)
23+
remoteUDPAddrs, err := resolveNetprobeAddresses(address)
2424
if err != nil {
2525
return err
2626
}
@@ -31,7 +31,15 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
3131
timeout = Min(MaxTimeout, timeout)
3232
}
3333
for tries := timeout; tries > 0; tries-- {
34-
pc, err := net.DialTimeout("udp", remoteUDPAddr.String(), proxy.timeout)
34+
var err error
35+
for _, remoteUDPAddr := range remoteUDPAddrs {
36+
var pc net.Conn
37+
pc, err = net.DialTimeout("udp", remoteUDPAddr.String(), proxy.timeout)
38+
if err == nil {
39+
pc.Close()
40+
break
41+
}
42+
}
3543
if err != nil {
3644
if !retried {
3745
retried = true
@@ -41,7 +49,6 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
4149
time.Sleep(1 * time.Second)
4250
continue
4351
}
44-
pc.Close()
4552
dlog.Notice("Network connectivity detected")
4653
return nil
4754
}

‎dnscrypt-proxy/netprobe_test.go‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package main
2+
3+
import "testing"
4+
5+
func TestResolveNetprobeAddresses(t *testing.T) {
6+
addrs, err := resolveNetprobeAddresses("9.9.9.9:53")
7+
if err != nil || len(addrs) != 1 || addrs[0].String() != "9.9.9.9:53" {
8+
t.Fatalf("single address: %v %v", addrs, err)
9+
}
10+
addrs, err = resolveNetprobeAddresses("9.9.9.9:53, [2620:fe::fe]:53")
11+
if err != nil || len(addrs) != 2 || addrs[1].String() != "[2620:fe::fe]:53" {
12+
t.Fatalf("multiple addresses: %v %v", addrs, err)
13+
}
14+
for _, bad := range []string{"9.9.9.9", "9.9.9.9:53,bogus", " , "} {
15+
if _, err := resolveNetprobeAddresses(bad); err == nil {
16+
t.Fatalf("expected error for %q", bad)
17+
}
18+
}
19+
}

‎dnscrypt-proxy/netprobe_windows.go‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
1818
} else {
1919
dlog.Critical(err)
2020
}
21-
remoteUDPAddr, err := net.ResolveUDPAddr("udp", address)
21+
remoteUDPAddrs, err := resolveNetprobeAddresses(address)
2222
if err != nil {
2323
return err
2424
}
@@ -29,15 +29,21 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
2929
timeout = Min(MaxTimeout, timeout)
3030
}
3131
for tries := timeout; tries > 0; tries-- {
32-
pc, err := net.DialTimeout("udp", remoteUDPAddr.String(), proxy.timeout)
33-
if err == nil {
32+
var err error
33+
for _, remoteUDPAddr := range remoteUDPAddrs {
34+
var pc net.Conn
35+
pc, err = net.DialTimeout("udp", remoteUDPAddr.String(), proxy.timeout)
36+
if err != nil {
37+
continue
38+
}
3439
// Write at least 1 byte. This ensures that sockets are ready to use for writing.
3540
// Windows specific: during the system startup, sockets can be created but the underlying buffers may not be
3641
// set up yet. If this is the case Write fails with WSAENOBUFS: "An operation on a socket could not be
3742
// performed because the system lacked sufficient buffer space or because a queue was full"
3843
_, err = pc.Write([]byte{0})
39-
if err != nil {
40-
pc.Close()
44+
pc.Close()
45+
if err == nil {
46+
break
4147
}
4248
}
4349
if err != nil {
@@ -49,7 +55,6 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
4955
time.Sleep(1 * time.Second)
5056
continue
5157
}
52-
pc.Close()
5358
dlog.Notice("Network connectivity detected")
5459
return nil
5560
}

0 commit comments

Comments
 (0)