Przeglądaj źródła

Merge pull request #220 from efryntov/master

New client verification mechanism
Eugene Fryntov 9 lat temu
rodzic
commit
f3a765d1a5

+ 2 - 2
AndroidLibrary/psi/psi.go

@@ -124,7 +124,7 @@ func Stop() {
 	}
 }
 
-// This is a passthrough to Controller.SetClientVerificationPayload.
+// This is a passthrough to Controller.SetClientVerificationPayloadForActiveTunnels.
 // Note: should only be called after Start() and before Stop(); otherwise,
 // will silently take no action.
 func SetClientVerificationPayload(clientVerificationPayload string) {
@@ -133,6 +133,6 @@ func SetClientVerificationPayload(clientVerificationPayload string) {
 	defer controllerMutex.Unlock()
 
 	if controller != nil {
-		controller.SetClientVerificationPayload(clientVerificationPayload)
+		controller.SetClientVerificationPayloadForActiveTunnels(clientVerificationPayload)
 	}
 }

+ 8 - 3
psiphon/common/throttled.go

@@ -57,13 +57,18 @@ type RateLimits struct {
 // The underlying rate limiter uses the token bucket algorithm to
 // calculate delay times for read and write operations.
 type ThrottledConn struct {
-	net.Conn
+	// https://golang.org/src/sync/atomic/doc.go#L50
+	// On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit
+	// alignment of 64-bit words accessed atomically. The first word in a global
+	// variable or in an allocated struct or slice can be relied upon to be
+	// 64-bit aligned.
 	unlimitedReadBytes  int64
-	limitingReads       int32
-	limitedReader       io.Reader
 	unlimitedWriteBytes int64
+	limitingReads       int32
 	limitingWrites      int32
+	limitedReader       io.Reader
 	limitedWriter       io.Writer
+	net.Conn
 }
 
 // NewThrottledConn initializes a new ThrottledConn.

+ 1 - 0
psiphon/config.go

@@ -68,6 +68,7 @@ const (
 	PSIPHON_API_CONNECTED_REQUEST_RETRY_PERIOD           = 5 * time.Second
 	PSIPHON_API_TUNNEL_STATS_MAX_COUNT                   = 100
 	PSIPHON_API_CLIENT_VERIFICATION_REQUEST_RETRY_PERIOD = 5 * time.Second
+	PSIPHON_API_CLIENT_VERIFICATION_REQUEST_MAX_RETRIES  = 10
 	FETCH_ROUTES_TIMEOUT_SECONDS                         = 60
 	DOWNLOAD_UPGRADE_TIMEOUT                             = 15 * time.Minute
 	DOWNLOAD_UPGRADE_RETRY_PERIOD_SECONDS                = 30

+ 7 - 13
psiphon/controller.go

@@ -133,8 +133,8 @@ func NewController(config *Config) (controller *Controller, err error) {
 		signalFetchRemoteServerList: make(chan struct{}),
 		signalDownloadUpgrade:       make(chan string),
 		signalReportConnected:       make(chan struct{}),
-		// Buffer allows SetClientVerificationPayload to submit one new payload
-		// without blocking or dropping it.
+		// Buffer allows SetClientVerificationPayloadForActiveTunnels to submit one
+		// new payload without blocking or dropping it.
 		newClientVerificationPayload: make(chan string, 1),
 	}
 
@@ -249,11 +249,9 @@ func (controller *Controller) SignalComponentFailure() {
 	}
 }
 
-// SetClientVerificationPayload sets the client verification payload
-// that is to be sent in client verification requests to all established
-// tunnels. Calling this function both sets the payload to be used for
-// all future tunnels as wells as triggering requests with this payload
-// for all currently established tunneled.
+// SetClientVerificationPayloadForActiveTunnels sets the client verification
+// payload that is to be sent in client verification requests to all established
+// tunnels.
 //
 // Client verification is used to verify that the client is a
 // valid Psiphon client, which will determine how the server treats
@@ -264,10 +262,10 @@ func (controller *Controller) SignalComponentFailure() {
 // after tunnel-core starts, the payload cannot be simply specified in
 // the Config.
 //
-// SetClientVerificationPayload will not block enqueuing a new verification
+// SetClientVerificationPayloadForActiveTunnels will not block enqueuing a new verification
 // payload. One new payload can be enqueued, after which additional payloads
 // will be dropped if a payload is still enqueued.
-func (controller *Controller) SetClientVerificationPayload(clientVerificationPayload string) {
+func (controller *Controller) SetClientVerificationPayloadForActiveTunnels(clientVerificationPayload string) {
 	select {
 	case controller.newClientVerificationPayload <- clientVerificationPayload:
 	default:
@@ -589,10 +587,6 @@ loop:
 				break
 			}
 
-			if clientVerificationPayload != "" {
-				establishedTunnel.SetClientVerificationPayload(clientVerificationPayload)
-			}
-
 			NoticeActiveTunnel(establishedTunnel.serverEntry.IpAddress, establishedTunnel.protocol)
 
 			if tunnelCount == 1 {

+ 11 - 21
psiphon/serverApi.go

@@ -167,17 +167,13 @@ func (serverContext *ServerContext) doHandshakeRequest() error {
 	// - 'preemptive_reconnect_lifetime_milliseconds' is currently unused
 	// - 'ssh_session_id' is ignored; client session ID is used instead
 	var handshakeResponse struct {
-		Homepages                     []string            `json:"homepages"`
-		UpgradeClientVersion          string              `json:"upgrade_client_version"`
-		PageViewRegexes               []map[string]string `json:"page_view_regexes"`
-		HttpsRequestRegexes           []map[string]string `json:"https_request_regexes"`
-		EncodedServerList             []string            `json:"encoded_server_list"`
-		ClientRegion                  string              `json:"client_region"`
-		ServerTimestamp               string              `json:"server_timestamp"`
-		ClientVerificationRequired    bool                `json:"client_verification_required"`
-		ClientVerificationServerNonce string              `json:"client_verification_server_nonce"`
-		ClientVerificationTTLSeconds  int                 `json:"client_verification_ttl_seconds"`
-		ClientVerificationResetCache  bool                `json:"client_verification_reset_cache"`
+		Homepages            []string            `json:"homepages"`
+		UpgradeClientVersion string              `json:"upgrade_client_version"`
+		PageViewRegexes      []map[string]string `json:"page_view_regexes"`
+		HttpsRequestRegexes  []map[string]string `json:"https_request_regexes"`
+		EncodedServerList    []string            `json:"encoded_server_list"`
+		ClientRegion         string              `json:"client_region"`
+		ServerTimestamp      string              `json:"server_timestamp"`
 	}
 	err := json.Unmarshal(response, &handshakeResponse)
 	if err != nil {
@@ -243,12 +239,6 @@ func (serverContext *ServerContext) doHandshakeRequest() error {
 
 	serverContext.serverHandshakeTimestamp = handshakeResponse.ServerTimestamp
 
-	if handshakeResponse.ClientVerificationRequired {
-		NoticeClientVerificationRequired(handshakeResponse.ClientVerificationServerNonce,
-			handshakeResponse.ClientVerificationTTLSeconds,
-			handshakeResponse.ClientVerificationResetCache)
-	}
-
 	return nil
 }
 
@@ -688,10 +678,10 @@ func (serverContext *ServerContext) DoClientVerificationRequest(
 		ClientVerificationResetCache  bool   `json:"client_verification_reset_cache"`
 	}
 
-	err = json.Unmarshal(response, &clientVerificationResponse)
-	if err != nil {
-		return common.ContextError(err)
-	}
+	// In case of empty response body the json.Unmarshal will fail
+	// and clientVerificationResponse will be initialized with default values
+
+	_ = json.Unmarshal(response, &clientVerificationResponse)
 
 	if clientVerificationResponse.ClientVerificationTTLSeconds > 0 {
 		NoticeClientVerificationRequired(

+ 13 - 6
psiphon/tunnel.go

@@ -871,19 +871,28 @@ func (tunnel *Tunnel) operateTunnel(tunnelOwner TunnelOwner) {
 	go func() {
 		defer requestsWaitGroup.Done()
 
+		clientVerificationRequestSuccess := true
 		clientVerificationPayload := ""
+		failCount := 0
 		for {
 			// TODO: use reflect.SelectCase?
-			if clientVerificationPayload == "" {
+			if clientVerificationRequestSuccess == true {
+				failCount = 0
 				select {
 				case clientVerificationPayload = <-tunnel.newClientVerificationPayload:
 				case <-signalStopClientVerificationRequests:
 					return
 				}
 			} else {
-				// When clientVerificationPayload is not "", the request for that
-				// payload so retry after a delay. Will use a new payload instead
+				// If sendClientVerification failed to send the payload we
+				// will retry after a delay. Will use a new payload instead
 				// if that arrives in the meantime.
+				// If failures count is more than PSIPHON_API_CLIENT_VERIFICATION_REQUEST_MAX_RETRIES
+				// stop retrying for this tunnel.
+				failCount += 1
+				if failCount > PSIPHON_API_CLIENT_VERIFICATION_REQUEST_MAX_RETRIES {
+					return
+				}
 				timeout := time.After(PSIPHON_API_CLIENT_VERIFICATION_REQUEST_RETRY_PERIOD)
 				select {
 				case <-timeout:
@@ -892,10 +901,8 @@ func (tunnel *Tunnel) operateTunnel(tunnelOwner TunnelOwner) {
 					return
 				}
 			}
-			if sendClientVerification(tunnel, clientVerificationPayload) {
-				clientVerificationPayload = ""
-			}
 
+			clientVerificationRequestSuccess = sendClientVerification(tunnel, clientVerificationPayload)
 		}
 	}()