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

Add read deadline to terminate dead client connections

Rod Hynes 10 лет назад
Родитель
Сommit
a60470e259
3 измененных файлов с 40 добавлено и 7 удалено
  1. 23 0
      psiphon/net.go
  2. 1 0
      psiphon/server/config.go
  3. 16 7
      psiphon/server/sshService.go

+ 23 - 0
psiphon/net.go

@@ -390,3 +390,26 @@ func IPAddressFromAddr(addr net.Addr) string {
 	}
 	return ipAddress
 }
+
+// TimeoutTCPConn wraps a net.TCPConn and sets an initial ReadDeadline. The
+// deadline is reset whenever data is received from the connection.
+type TimeoutTCPConn struct {
+	*net.TCPConn
+	deadline time.Duration
+}
+
+func NewTimeoutTCPConn(tcpConn *net.TCPConn, deadline time.Duration) *TimeoutTCPConn {
+	tcpConn.SetReadDeadline(time.Now().Add(deadline))
+	return &TimeoutTCPConn{
+		TCPConn:  tcpConn,
+		deadline: deadline,
+	}
+}
+
+func (conn *TimeoutTCPConn) Read(buffer []byte) (int, error) {
+	n, err := conn.TCPConn.Read(buffer)
+	if err == nil {
+		conn.TCPConn.SetReadDeadline(time.Now().Add(conn.deadline))
+	}
+	return n, err
+}

+ 1 - 0
psiphon/server/config.go

@@ -51,6 +51,7 @@ const (
 	SSH_RSA_HOST_KEY_BITS                  = 2048
 	DEFAULT_SSH_SERVER_PORT                = 2222
 	SSH_HANDSHAKE_TIMEOUT                  = 30 * time.Second
+	SSH_CONNECTION_READ_DEADLINE           = 5 * time.Minute
 	SSH_OBFUSCATED_KEY_BYTE_LENGTH         = 32
 	DEFAULT_OBFUSCATED_SSH_SERVER_PORT     = 3333
 )

+ 16 - 7
psiphon/server/sshService.go

@@ -149,7 +149,7 @@ func runSSHServer(
 			}
 
 			// process each client connection concurrently
-			go sshServer.handleClient(conn)
+			go sshServer.handleClient(conn.(*net.TCPConn))
 		}
 
 		sshServer.stopClients()
@@ -245,15 +245,23 @@ func (sshServer *sshServer) stopClients() {
 	sshServer.clients = make(map[string]*sshClient)
 }
 
-func (sshServer *sshServer) handleClient(conn net.Conn) {
+func (sshServer *sshServer) handleClient(tcpConn *net.TCPConn) {
 
 	startTime := time.Now()
-	geoIPData := GeoIPLookup(psiphon.IPAddressFromAddr(conn.RemoteAddr()))
+	geoIPData := GeoIPLookup(psiphon.IPAddressFromAddr(tcpConn.RemoteAddr()))
 
-	// Run the initial [obfuscated] SSH handshake in a goroutine
-	// so we can both respect shutdownBroadcast and implement a
-	// handshake timeout. The timeout is to reclaim network
-	// resources in case the handshake takes too long.
+	// Wrap the base TCP connection in a TimeoutTCPConn which will terminate
+	// the connection if it's idle for too long. This timeout is in effect for
+	// the entire duration of the SSH connection. Clients must actively use
+	// the connection or send SSH keep alive requests to keep the connection
+	// active.
+
+	conn := psiphon.NewTimeoutTCPConn(tcpConn, SSH_CONNECTION_READ_DEADLINE)
+
+	// Run the initial [obfuscated] SSH handshake in a goroutine so we can both
+	// respect shutdownBroadcast and implement a specific handshake timeout.
+	// The timeout is to reclaim network resources in case the handshake takes
+	// too long.
 
 	type sshNewServerConnResult struct {
 		conn     net.Conn
@@ -272,6 +280,7 @@ func (sshServer *sshServer) handleClient(conn net.Conn) {
 	}
 
 	go func() {
+
 		result := &sshNewServerConnResult{}
 		if sshServer.useObfuscation {
 			result.conn, result.err = psiphon.NewObfuscatedSshConn(