Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dnscrypt-proxy/example-dnscrypt-proxy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,9 @@ netprobe_timeout = 60
## when the system starts.
## On other operating systems, the connection will be initialized
## but nothing will be sent at all.
## Multiple comma-separated addresses can be given, for example to support
## both IPv4-only and IPv6-only networks. Connectivity is detected as soon
## as any of them works: '9.9.9.9:53,[2620:fe::fe]:53'

netprobe_address = '9.9.9.9:53'

Expand Down
29 changes: 29 additions & 0 deletions dnscrypt-proxy/netprobe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package main

import (
"errors"
"net"
"strings"
)

// resolveNetprobeAddresses parses a netprobe address specification.
// Multiple addresses (for example an IPv4 and an IPv6 address) can be
// separated by commas; connectivity is detected as soon as any of them works.
func resolveNetprobeAddresses(address string) ([]*net.UDPAddr, error) {
var addrs []*net.UDPAddr
for _, part := range strings.Split(address, ",") {
part = strings.TrimSpace(part)
if len(part) == 0 {
continue
}
addr, err := net.ResolveUDPAddr("udp", part)
if err != nil {
return nil, err
}
addrs = append(addrs, addr)
}
if len(addrs) == 0 {
return nil, errors.New("No valid netprobe address in [" + address + "]")
}
return addrs, nil
}
13 changes: 10 additions & 3 deletions dnscrypt-proxy/netprobe_others.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
} else {
dlog.Critical(err)
}
remoteUDPAddr, err := net.ResolveUDPAddr("udp", address)
remoteUDPAddrs, err := resolveNetprobeAddresses(address)
if err != nil {
return err
}
Expand All @@ -31,7 +31,15 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
timeout = Min(MaxTimeout, timeout)
}
for tries := timeout; tries > 0; tries-- {
pc, err := net.DialTimeout("udp", remoteUDPAddr.String(), proxy.timeout)
var err error
for _, remoteUDPAddr := range remoteUDPAddrs {
var pc net.Conn
pc, err = net.DialTimeout("udp", remoteUDPAddr.String(), proxy.timeout)
if err == nil {
pc.Close()
break
}
}
if err != nil {
if !retried {
retried = true
Expand All @@ -41,7 +49,6 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
time.Sleep(1 * time.Second)
continue
}
pc.Close()
dlog.Notice("Network connectivity detected")
return nil
}
Expand Down
19 changes: 19 additions & 0 deletions dnscrypt-proxy/netprobe_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package main

import "testing"

func TestResolveNetprobeAddresses(t *testing.T) {
addrs, err := resolveNetprobeAddresses("9.9.9.9:53")
if err != nil || len(addrs) != 1 || addrs[0].String() != "9.9.9.9:53" {
t.Fatalf("single address: %v %v", addrs, err)
}
addrs, err = resolveNetprobeAddresses("9.9.9.9:53, [2620:fe::fe]:53")
if err != nil || len(addrs) != 2 || addrs[1].String() != "[2620:fe::fe]:53" {
t.Fatalf("multiple addresses: %v %v", addrs, err)
}
for _, bad := range []string{"9.9.9.9", "9.9.9.9:53,bogus", " , "} {
if _, err := resolveNetprobeAddresses(bad); err == nil {
t.Fatalf("expected error for %q", bad)
}
}
}
17 changes: 11 additions & 6 deletions dnscrypt-proxy/netprobe_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
} else {
dlog.Critical(err)
}
remoteUDPAddr, err := net.ResolveUDPAddr("udp", address)
remoteUDPAddrs, err := resolveNetprobeAddresses(address)
if err != nil {
return err
}
Expand All @@ -29,15 +29,21 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
timeout = Min(MaxTimeout, timeout)
}
for tries := timeout; tries > 0; tries-- {
pc, err := net.DialTimeout("udp", remoteUDPAddr.String(), proxy.timeout)
if err == nil {
var err error
for _, remoteUDPAddr := range remoteUDPAddrs {
var pc net.Conn
pc, err = net.DialTimeout("udp", remoteUDPAddr.String(), proxy.timeout)
if err != nil {
continue
}
// Write at least 1 byte. This ensures that sockets are ready to use for writing.
// Windows specific: during the system startup, sockets can be created but the underlying buffers may not be
// set up yet. If this is the case Write fails with WSAENOBUFS: "An operation on a socket could not be
// performed because the system lacked sufficient buffer space or because a queue was full"
_, err = pc.Write([]byte{0})
if err != nil {
pc.Close()
pc.Close()
if err == nil {
break
}
}
if err != nil {
Expand All @@ -49,7 +55,6 @@ func NetProbe(proxy *Proxy, address string, timeout int) error {
time.Sleep(1 * time.Second)
continue
}
pc.Close()
dlog.Notice("Network connectivity detected")
return nil
}
Expand Down
Loading