Quellcode durchsuchen

Implemented connect timeouts

Rod Hynes vor 11 Jahren
Ursprung
Commit
d6b2878c47
5 geänderte Dateien mit 34 neuen und 14 gelöschten Zeilen
  1. 8 6
      psiphon/conn.go
  2. 16 3
      psiphon/conn_unix.go
  3. 2 2
      psiphon/conn_windows.go
  4. 4 2
      psiphon/defaults.go
  5. 4 1
      psiphon/tunnel.go

+ 8 - 6
psiphon/conn.go

@@ -43,19 +43,21 @@ type Conn struct {
 	writeTimeout  time.Duration
 }
 
-// Dial creates a new, connected Conn. The connection can be interrupted
-// using pendingConns.interrupt(): the new Conn is added to pendingConns
-// before the socket connect beings. The caller is responsible for removing the
-// returned Conn from pendingConns.
+// Dial creates a new, connected Conn. The connection may be interrupted
+// using pendingConns.interrupt(): on platforms that support this, the new
+// Conn is added to pendingConns before the socket connect begins.
+// The caller is responsible for removing any Conns added to pendingConns,
+// even when Dial returns an error.
 // To implement device binding and interruptible connecting, the lower-level
 // syscall APIs are used. The sequence of syscalls in this implementation are
 // taken from: https://code.google.com/p/go/issues/detail?id=6966
+// connectTimeout is rounded up to the nearest second on some platforms.
 func Dial(
 	ipAddress string, port int,
-	readTimeout, writeTimeout time.Duration,
+	connectTimeout, readTimeout, writeTimeout time.Duration,
 	pendingConns *PendingConns) (conn *Conn, err error) {
 
-	conn, err = interruptibleDial(ipAddress, port, readTimeout, writeTimeout, pendingConns)
+	conn, err = interruptibleDial(ipAddress, port, connectTimeout, readTimeout, writeTimeout, pendingConns)
 	if err != nil {
 		return nil, ContextError(err)
 	}

+ 16 - 3
psiphon/conn_unix.go

@@ -22,6 +22,7 @@
 package psiphon
 
 import (
+	"errors"
 	"net"
 	"os"
 	"syscall"
@@ -34,14 +35,15 @@ type interruptibleConn struct {
 
 func interruptibleDial(
 	ipAddress string, port int,
-	readTimeout, writeTimeout time.Duration,
+	connectTimeout, readTimeout, writeTimeout time.Duration,
 	pendingConns *PendingConns) (conn *Conn, err error) {
 
 	socketFd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
 	if err != nil {
 		return nil, err
 	}
-	err = syscall.SetsockoptInt(socketFd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, TCP_KEEP_ALIVE_PERIOD_SECONDS)
+	err = syscall.SetsockoptInt(
+		socketFd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, TUNNEL_TCP_KEEP_ALIVE_PERIOD_SECONDS)
 	if err != nil {
 		syscall.Close(socketFd)
 		return nil, err
@@ -68,7 +70,18 @@ func interruptibleDial(
 	var addr [4]byte
 	copy(addr[:], net.ParseIP(ipAddress).To4())
 	sockAddr := syscall.SockaddrInet4{Addr: addr, Port: port}
-	err = syscall.Connect(conn.interruptible.socketFd, &sockAddr)
+	if connectTimeout != 0 {
+		errChannel := make(chan error, 2)
+		time.AfterFunc(connectTimeout, func() {
+			errChannel <- errors.New("connect timeout")
+		})
+		go func() {
+			errChannel <- syscall.Connect(conn.interruptible.socketFd, &sockAddr)
+		}()
+		err = <-errChannel
+	} else {
+		err = syscall.Connect(conn.interruptible.socketFd, &sockAddr)
+	}
 	if err != nil {
 		return nil, err
 	}

+ 2 - 2
psiphon/conn_windows.go

@@ -32,10 +32,10 @@ type interruptibleConn struct {
 
 func interruptibleDial(
 	ipAddress string, port int,
-	readTimeout, writeTimeout time.Duration,
+	connectTimeout, readTimeout, writeTimeout time.Duration,
 	pendingConns *PendingConns) (conn *Conn, err error) {
 	// Note: using net.Dial(); interruptible connections not supported on Windows
-	netConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ipAddress, port))
+	netConn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ipAddress, port), connectTimeout)
 	if err != nil {
 		return nil, err
 	}

+ 4 - 2
psiphon/defaults.go

@@ -26,10 +26,12 @@ import (
 const (
 	DATA_STORE_FILENAME                    = "psiphon.db"
 	FETCH_REMOTE_SERVER_LIST_TIMEOUT       = 5 * time.Second
-	CONNECTION_CANDIDATE_TIMEOUT           = 10 * time.Second
+	TUNNEL_CONNECT_TIMEOUT                 = 15 * time.Second
+	TUNNEL_READ_TIMEOUT                    = 0 * time.Second
+	TUNNEL_WRITE_TIMEOUT                   = 5 * time.Second
+	TUNNEL_TCP_KEEP_ALIVE_PERIOD_SECONDS   = 60
 	ESTABLISH_TUNNEL_TIMEOUT               = 60 * time.Second
 	CONNECTION_WORKER_POOL_SIZE            = 10
-	TCP_KEEP_ALIVE_PERIOD_SECONDS          = 60
 	HTTP_PROXY_READ_TIMEOUT                = 1 * time.Second
 	HTTP_PROXY_WRITE_TIMEOUT               = 10 * time.Second
 	FETCH_REMOTE_SERVER_LIST_RETRY_TIMEOUT = 5 * time.Second

+ 4 - 1
psiphon/tunnel.go

@@ -73,7 +73,10 @@ func EstablishTunnel(serverEntry *ServerEntry, pendingConns *PendingConns) (tunn
 		selectedProtocol = PROTOCOL_OBFUSCATED_SSH
 		port = serverEntry.SshObfuscatedPort
 	}
-	conn, err := Dial(serverEntry.IpAddress, port, 0, CONNECTION_CANDIDATE_TIMEOUT, pendingConns)
+	conn, err := Dial(
+		serverEntry.IpAddress, port,
+		TUNNEL_CONNECT_TIMEOUT, TUNNEL_READ_TIMEOUT, TUNNEL_WRITE_TIMEOUT,
+		pendingConns)
 	if err != nil {
 		return nil, err
 	}