Просмотр исходного кода

Add a dial timeout to TLSPacketConn.

In my testing locally, specifying -dot with a non-responsive TCP port
would time out after about 30 seconds anyway:
	$ time ./dnstt-client -dot tns.example.com:8000 -pubkey-file server.pub t.example.com 127.0.0.1:7000
	dial tcp 45.79.134.119:8000: connect: connection timed out
	real	0m31.398s
	user	0m0.006s
	sys	0m0.003s
Which is in line with the documentation for net.Dialer:
	https://golang.org/pkg/net/#Dialer
	With or without a timeout, the operating system may impose its
	own earlier timeout. For instance, TCP timeouts are often around
	3 minutes.
But may as well be explicit.

This commit has the side effect of changing the error message from
"connection timed out" to "i/o timeout".
	$ time ./dnstt-client -dot tns.example.com:8000 -pubkey-file server.pub t.example.com 127.0.0.1:7000
	dial tcp 45.79.134.119:8000: i/o timeout
	real	0m30.007s
	user	0m0.003s
	sys	0m0.007s
I tried setting the dialTimeout to 40 seconds, and in that case the
system timeout take precedence after ≈31 seconds, with the "connection
timed out" error as before.

This is issue UCB-02-007 from the 2021 security audit of Turbo Tunnel by
Cure53.

The audit report additionally recommends calling SetReadDeadline before
each read operation. I have chosen not to do that. It is intended that
the TLS connection should be able to remain idle if there is nothing to
send. As DNS is a query–response protocol, one might expect a response
(and within a certain amount of time) only after sending a query;
sendLoop could refresh the ReadDeadline for recvLoop every time it sends
a query. But a malicious DoT server could keep a useless connection
alive anyway by sending Slowloris-style short responses within each
deadline, and an external adversary could capable of delaying responses
could deny service indefinitely or simply block the server. In any case,
the smux KeepAliveTimeout serves as a check that prevents stalled
connections from remaining indefinitely.
David Fifield 5 лет назад
Родитель
Сommit
4de69201d1
1 измененных файлов с 8 добавлено и 2 удалено
  1. 8 2
      dnstt-client/tls.go

+ 8 - 2
dnstt-client/tls.go

@@ -8,10 +8,13 @@ import (
 	"log"
 	"log"
 	"net"
 	"net"
 	"sync"
 	"sync"
+	"time"
 
 
 	"www.bamsoftware.com/git/dnstt.git/turbotunnel"
 	"www.bamsoftware.com/git/dnstt.git/turbotunnel"
 )
 )
 
 
+const dialTimeout = 30 * time.Second
+
 // TLSPacketConn is a TLS- and TCP-based transport for DNS messages, used for
 // TLSPacketConn is a TLS- and TCP-based transport for DNS messages, used for
 // DNS over TLS (DoT). Its WriteTo and ReadFrom methods exchange DNS messages
 // DNS over TLS (DoT). Its WriteTo and ReadFrom methods exchange DNS messages
 // over a TLS channel, prefixing each message with a two-octet length field as
 // over a TLS channel, prefixing each message with a two-octet length field as
@@ -41,8 +44,11 @@ func NewTLSPacketConn(addr string) (*TLSPacketConn, error) {
 	// becomes disconnected. We do the first dial here, outside the
 	// becomes disconnected. We do the first dial here, outside the
 	// goroutine, so that any immediate and permanent connection errors are
 	// goroutine, so that any immediate and permanent connection errors are
 	// reported directly to the caller of NewTLSPacketConn.
 	// reported directly to the caller of NewTLSPacketConn.
+	dialer := &net.Dialer{
+		Timeout: dialTimeout,
+	}
 	tlsConfig := &tls.Config{}
 	tlsConfig := &tls.Config{}
-	conn, err := tls.Dial("tcp", addr, tlsConfig)
+	conn, err := tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -69,7 +75,7 @@ func NewTLSPacketConn(addr string) (*TLSPacketConn, error) {
 			conn.Close()
 			conn.Close()
 
 
 			// Whenever the TLS connection dies, redial a new one.
 			// Whenever the TLS connection dies, redial a new one.
-			conn, err = tls.Dial("tcp", addr, tlsConfig)
+			conn, err = tls.DialWithDialer(dialer, "tcp", addr, tlsConfig)
 			if err != nil {
 			if err != nil {
 				log.Printf("tls.Dial: %v", err)
 				log.Printf("tls.Dial: %v", err)
 				break
 				break