Browse Source

Support reconnecting with new authorizations

Rod Hynes 7 years ago
parent
commit
25cdbc4878

+ 6 - 0
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.h

@@ -332,6 +332,12 @@ Swift: @code func onInternetReachabilityChanged(_ currentReachability: Reachabil
 - (BOOL)start:(BOOL)ifNeeded;
 
 
+/*!
+ Reconnect a previously started PsiphonTunnel with authorizations set to the specified list.
+ Has no effect if there is no running PsiphonTunnel. authorizations may be nil.
+ */
+- (void)reconnectWithAuthorizations:(NSArray<NSString *> *_Nullable)authorizations;
+
 /*!
  Force stops the tunnel and reconnects with the current session ID.
  Retuns with FALSE immediately if no session ID has already been generated.

+ 12 - 1
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m

@@ -150,6 +150,17 @@
     return [self start];
 }
 
+// See comment in header
+- (void)reconnectWithAuthorizations:(NSArray<NSString *> *_Nullable)authorizations {
+
+    NSString *authorizationsList = @"";
+    if (authorizations != nil) {
+        authorizationsList = [authorizations componentsJoinedByString: @" "];
+    }
+
+    GoPsiReconnectTunnel(authorizationsList);
+}
+
 // See comment in header
 - (BOOL)stopAndReconnectWithCurrentSessionID {
 
@@ -1300,7 +1311,7 @@
     // Restart if the state has changed, unless the previous state was NotReachable, because
     // the tunnel should be waiting for connectivity in that case.
     if (networkStatus != previousNetworkStatus && previousNetworkStatus != NotReachable) {
-        GoPsiReconnectTunnel();
+        GoPsiReconnectTunnel(nil);
     }
 }
 

+ 14 - 4
MobileLibrary/psi/psi.go

@@ -28,9 +28,9 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"sync"
-
 	"os"
+	"strings"
+	"sync"
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
@@ -182,13 +182,23 @@ func Stop() {
 	}
 }
 
-func ReconnectTunnel() {
+// ReconnectTunnel initiates a reconnect of the current tunnel, if one is
+// running.
+//
+// ReconnectTunnel takes an optional parameter which overrides the
+// Authorizations field set in the config passed to Start. When not "", the
+// input newAuthorizationsList is a space-delimited list of base64
+// Authoriztions. newAuthorizationsList is parsed and applied before the
+// current tunnel is reconnected.
+func ReconnectTunnel(newAuthorizationsList string) {
 
 	controllerMutex.Lock()
 	defer controllerMutex.Unlock()
 
 	if controller != nil {
-		// TODO: ensure TerminateNextActiveTunnel is safe for use (see godoc)
+		if newAuthorizationsList != "" {
+			controller.SetAuthorizations(strings.Split(newAuthorizationsList, " "))
+		}
 		controller.TerminateNextActiveTunnel()
 	}
 }

+ 24 - 0
psiphon/config.go

@@ -28,6 +28,7 @@ import (
 	"os"
 	"strconv"
 	"strings"
+	"sync"
 	"unicode"
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
@@ -480,6 +481,9 @@ type Config struct {
 	// calling clientParameters.Set directly will fail to add config values.
 	clientParameters *parameters.ClientParameters
 
+	authorizationsMutex sync.Mutex
+	authorizations      []string
+
 	deviceBinder    DeviceBinder
 	networkIDGetter NetworkIDGetter
 
@@ -668,6 +672,10 @@ func (config *Config) Commit() error {
 		return common.ContextError(err)
 	}
 
+	// client authorizations default to config.Authorizations
+
+	config.SetAuthorizations(config.Authorizations)
+
 	// Initialize config.deviceBinder and config.config.networkIDGetter. These
 	// wrap config.DeviceBinder and config.NetworkIDGetter/NetworkID with
 	// loggers.
@@ -745,6 +753,22 @@ func (config *Config) SetClientParameters(tag string, skipOnError bool, applyPar
 	return nil
 }
 
+// GetAuthorizations returns the current client authorizations.
+// The caller must not modify the returned slice.
+func (config *Config) GetAuthorizations() []string {
+	config.authorizationsMutex.Lock()
+	defer config.authorizationsMutex.Unlock()
+	return config.authorizations
+}
+
+// SetAuthorizations sets the current client authorizations.
+// The caller must not modify the input slice.
+func (config *Config) SetAuthorizations(authorizations []string) {
+	config.authorizationsMutex.Lock()
+	defer config.authorizationsMutex.Unlock()
+	config.authorizations = authorizations
+}
+
 func (config *Config) makeConfigParameters() map[string]interface{} {
 
 	// Build set of config values to apply to parameters.

+ 17 - 12
psiphon/controller.go

@@ -282,6 +282,23 @@ func (controller *Controller) SignalComponentFailure() {
 	controller.stopRunning()
 }
 
+// SetAuthorizations overrides the Authorizations field of the Controller
+// config with the input value. The new value will be used in the next tunnel
+// connection.
+func (controller *Controller) SetAuthorizations(authorizations []string) {
+	controller.config.SetAuthorizations(authorizations)
+}
+
+// TerminateNextActiveTunnel terminates the active tunnel, which will initiate
+// establishment of a new tunnel.
+func (controller *Controller) TerminateNextActiveTunnel() {
+	tunnel := controller.getNextActiveTunnel()
+	if tunnel != nil {
+		controller.SignalTunnelFailure(tunnel)
+		NoticeInfo("terminated tunnel: %s", tunnel.serverEntry.IpAddress)
+	}
+}
+
 // remoteServerListFetcher fetches an out-of-band list of server entries
 // for more tunnel candidates. It fetches when signalled, with retries
 // on failure.
@@ -765,18 +782,6 @@ loop:
 	NoticeInfo("exiting run tunnels")
 }
 
-// TerminateNextActiveTunnel is a support routine for
-// test code that must terminate the active tunnel and
-// restart establishing. This function is not guaranteed
-// to be safe for use in other cases.
-func (controller *Controller) TerminateNextActiveTunnel() {
-	tunnel := controller.getNextActiveTunnel()
-	if tunnel != nil {
-		controller.SignalTunnelFailure(tunnel)
-		NoticeInfo("terminated tunnel: %s", tunnel.serverEntry.IpAddress)
-	}
-}
-
 // classifyImpairedProtocol tracks "impaired" protocol classifications for failed
 // tunnels. A protocol is classified as impaired if a tunnel using that protocol
 // fails, repeatedly, shortly after the start of the connection. During tunnel

+ 2 - 1
psiphon/serverApi.go

@@ -154,7 +154,8 @@ func (serverContext *ServerContext) doHandshakeRequest(
 	var response []byte
 	if serverContext.psiphonHttpsClient == nil {
 
-		params[protocol.PSIPHON_API_HANDSHAKE_AUTHORIZATIONS] = serverContext.tunnel.config.Authorizations
+		params[protocol.PSIPHON_API_HANDSHAKE_AUTHORIZATIONS] =
+			serverContext.tunnel.config.GetAuthorizations()
 
 		request, err := makeSSHAPIRequestPayload(params)
 		if err != nil {