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

Report tunnel duration based on last confirmed data transfer time

Rod Hynes 9 лет назад
Родитель
Сommit
3d26e4adec
2 измененных файлов с 48 добавлено и 7 удалено
  1. 33 2
      psiphon/net.go
  2. 15 5
      psiphon/server/tunnelServer.go

+ 33 - 2
psiphon/net.go

@@ -749,6 +749,8 @@ type ActivityMonitoredConn struct {
 	net.Conn
 	net.Conn
 	inactivityTimeout time.Duration
 	inactivityTimeout time.Duration
 	activeOnWrite     bool
 	activeOnWrite     bool
+	startTime         int64
+	lastActivityTime  int64
 	lruEntry          *LRUConnsEntry
 	lruEntry          *LRUConnsEntry
 }
 }
 
 
@@ -761,20 +763,42 @@ func NewActivityMonitoredConn(
 	if inactivityTimeout > 0 {
 	if inactivityTimeout > 0 {
 		conn.SetReadDeadline(time.Now().Add(inactivityTimeout))
 		conn.SetReadDeadline(time.Now().Add(inactivityTimeout))
 	}
 	}
+
+	now := time.Now().UnixNano()
+
 	return &ActivityMonitoredConn{
 	return &ActivityMonitoredConn{
 		Conn:              conn,
 		Conn:              conn,
 		inactivityTimeout: inactivityTimeout,
 		inactivityTimeout: inactivityTimeout,
 		activeOnWrite:     activeOnWrite,
 		activeOnWrite:     activeOnWrite,
+		startTime:         now,
+		lastActivityTime:  now,
 		lruEntry:          lruEntry,
 		lruEntry:          lruEntry,
 	}
 	}
 }
 }
 
 
+// GetStartTime gets the time when the ActivityMonitoredConn was
+// initialized.
+func (conn *ActivityMonitoredConn) GetStartTime() time.Time {
+	return time.Unix(0, conn.startTime)
+}
+
+// GetActiveDuration returns the time elapsed between the initialization
+// of the ActivityMonitoredConn and the last Read (or Write when
+// activeOnWrite is specified).
+func (conn *ActivityMonitoredConn) GetActiveDuration() time.Duration {
+	return time.Duration(atomic.LoadInt64(&conn.lastActivityTime) - conn.startTime)
+}
+
 func (conn *ActivityMonitoredConn) Read(buffer []byte) (int, error) {
 func (conn *ActivityMonitoredConn) Read(buffer []byte) (int, error) {
 	n, err := conn.Conn.Read(buffer)
 	n, err := conn.Conn.Read(buffer)
 	if err == nil {
 	if err == nil {
+
+		atomic.StoreInt64(&conn.lastActivityTime, time.Now().UnixNano())
+
 		if conn.inactivityTimeout > 0 {
 		if conn.inactivityTimeout > 0 {
 			conn.Conn.SetReadDeadline(time.Now().Add(conn.inactivityTimeout))
 			conn.Conn.SetReadDeadline(time.Now().Add(conn.inactivityTimeout))
 		}
 		}
+
 		if conn.lruEntry != nil {
 		if conn.lruEntry != nil {
 			conn.lruEntry.Touch()
 			conn.lruEntry.Touch()
 		}
 		}
@@ -785,9 +809,16 @@ func (conn *ActivityMonitoredConn) Read(buffer []byte) (int, error) {
 func (conn *ActivityMonitoredConn) Write(buffer []byte) (int, error) {
 func (conn *ActivityMonitoredConn) Write(buffer []byte) (int, error) {
 	n, err := conn.Conn.Write(buffer)
 	n, err := conn.Conn.Write(buffer)
 	if err == nil {
 	if err == nil {
-		if conn.inactivityTimeout > 0 && conn.activeOnWrite {
-			conn.Conn.SetReadDeadline(time.Now().Add(conn.inactivityTimeout))
+
+		if conn.activeOnWrite {
+
+			atomic.StoreInt64(&conn.lastActivityTime, time.Now().UnixNano())
+
+			if conn.inactivityTimeout > 0 {
+				conn.Conn.SetReadDeadline(time.Now().Add(conn.inactivityTimeout))
+			}
 		}
 		}
+
 		if conn.lruEntry != nil {
 		if conn.lruEntry != nil {
 			conn.lruEntry.Touch()
 			conn.lruEntry.Touch()
 		}
 		}

+ 15 - 5
psiphon/server/tunnelServer.go

@@ -367,11 +367,12 @@ func (sshServer *sshServer) handleClient(tunnelProtocol string, clientConn net.C
 	// must actively use the connection or send SSH keep alive requests to keep
 	// must actively use the connection or send SSH keep alive requests to keep
 	// the connection active.
 	// the connection active.
 
 
-	clientConn = psiphon.NewActivityMonitoredConn(
+	activityConn := psiphon.NewActivityMonitoredConn(
 		clientConn,
 		clientConn,
 		SSH_CONNECTION_READ_DEADLINE,
 		SSH_CONNECTION_READ_DEADLINE,
 		false,
 		false,
 		nil)
 		nil)
+	clientConn = activityConn
 
 
 	// Further wrap the connection in a rate limiting ThrottledConn.
 	// Further wrap the connection in a rate limiting ThrottledConn.
 
 
@@ -458,6 +459,7 @@ func (sshServer *sshServer) handleClient(tunnelProtocol string, clientConn net.C
 
 
 	sshClient.Lock()
 	sshClient.Lock()
 	sshClient.sshConn = result.sshConn
 	sshClient.sshConn = result.sshConn
+	sshClient.activityConn = activityConn
 	sshClient.Unlock()
 	sshClient.Unlock()
 
 
 	clientID, ok := sshServer.registerClient(sshClient)
 	clientID, ok := sshServer.registerClient(sshClient)
@@ -478,7 +480,7 @@ type sshClient struct {
 	sshServer               *sshServer
 	sshServer               *sshServer
 	tunnelProtocol          string
 	tunnelProtocol          string
 	sshConn                 ssh.Conn
 	sshConn                 ssh.Conn
-	startTime               time.Time
+	activityConn            *psiphon.ActivityMonitoredConn
 	geoIPData               GeoIPData
 	geoIPData               GeoIPData
 	psiphonSessionID        string
 	psiphonSessionID        string
 	udpChannel              ssh.Channel
 	udpChannel              ssh.Channel
@@ -503,7 +505,6 @@ func newSshClient(
 	return &sshClient{
 	return &sshClient{
 		sshServer:               sshServer,
 		sshServer:               sshServer,
 		tunnelProtocol:          tunnelProtocol,
 		tunnelProtocol:          tunnelProtocol,
-		startTime:               time.Now(),
 		geoIPData:               geoIPData,
 		geoIPData:               geoIPData,
 		trafficRules:            trafficRules,
 		trafficRules:            trafficRules,
 		tcpTrafficState:         &trafficState{},
 		tcpTrafficState:         &trafficState{},
@@ -576,11 +577,20 @@ func (sshClient *sshClient) stop() {
 	close(sshClient.stopBroadcast)
 	close(sshClient.stopBroadcast)
 	sshClient.channelHandlerWaitGroup.Wait()
 	sshClient.channelHandlerWaitGroup.Wait()
 
 
+	// Note: reporting duration based on last confirmed data transfer, which
+	// is reads for sshClient.activityConn.GetActiveDuration(), and not
+	// connection closing is important for protocols such as meek. For
+	// meek, the connection remains open until the HTTP session expires,
+	// which may be some time after the tunnel has closed. (The meek
+	// protocol has no allowance for signalling payload EOF, and even if
+	// it did the client may not have the opportunity to send a final
+	// request with an EOF flag set.)
+
 	sshClient.Lock()
 	sshClient.Lock()
 	log.WithContextFields(
 	log.WithContextFields(
 		LogFields{
 		LogFields{
-			"startTime":                         sshClient.startTime,
-			"duration":                          time.Now().Sub(sshClient.startTime),
+			"startTime":                         sshClient.activityConn.GetStartTime(),
+			"duration":                          sshClient.activityConn.GetActiveDuration(),
 			"psiphonSessionID":                  sshClient.psiphonSessionID,
 			"psiphonSessionID":                  sshClient.psiphonSessionID,
 			"country":                           sshClient.geoIPData.Country,
 			"country":                           sshClient.geoIPData.Country,
 			"city":                              sshClient.geoIPData.City,
 			"city":                              sshClient.geoIPData.City,