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;
 - (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.
  Force stops the tunnel and reconnects with the current session ID.
  Retuns with FALSE immediately if no session ID has already been generated.
  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];
     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
 // See comment in header
 - (BOOL)stopAndReconnectWithCurrentSessionID {
 - (BOOL)stopAndReconnectWithCurrentSessionID {
 
 
@@ -1300,7 +1311,7 @@
     // Restart if the state has changed, unless the previous state was NotReachable, because
     // Restart if the state has changed, unless the previous state was NotReachable, because
     // the tunnel should be waiting for connectivity in that case.
     // the tunnel should be waiting for connectivity in that case.
     if (networkStatus != previousNetworkStatus && previousNetworkStatus != NotReachable) {
     if (networkStatus != previousNetworkStatus && previousNetworkStatus != NotReachable) {
-        GoPsiReconnectTunnel();
+        GoPsiReconnectTunnel(nil);
     }
     }
 }
 }
 
 

+ 14 - 4
MobileLibrary/psi/psi.go

@@ -28,9 +28,9 @@ import (
 	"context"
 	"context"
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"sync"
-
 	"os"
 	"os"
+	"strings"
+	"sync"
 
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"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()
 	controllerMutex.Lock()
 	defer controllerMutex.Unlock()
 	defer controllerMutex.Unlock()
 
 
 	if controller != nil {
 	if controller != nil {
-		// TODO: ensure TerminateNextActiveTunnel is safe for use (see godoc)
+		if newAuthorizationsList != "" {
+			controller.SetAuthorizations(strings.Split(newAuthorizationsList, " "))
+		}
 		controller.TerminateNextActiveTunnel()
 		controller.TerminateNextActiveTunnel()
 	}
 	}
 }
 }

+ 24 - 0
psiphon/config.go

@@ -28,6 +28,7 @@ import (
 	"os"
 	"os"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
+	"sync"
 	"unicode"
 	"unicode"
 
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"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.
 	// calling clientParameters.Set directly will fail to add config values.
 	clientParameters *parameters.ClientParameters
 	clientParameters *parameters.ClientParameters
 
 
+	authorizationsMutex sync.Mutex
+	authorizations      []string
+
 	deviceBinder    DeviceBinder
 	deviceBinder    DeviceBinder
 	networkIDGetter NetworkIDGetter
 	networkIDGetter NetworkIDGetter
 
 
@@ -668,6 +672,10 @@ func (config *Config) Commit() error {
 		return common.ContextError(err)
 		return common.ContextError(err)
 	}
 	}
 
 
+	// client authorizations default to config.Authorizations
+
+	config.SetAuthorizations(config.Authorizations)
+
 	// Initialize config.deviceBinder and config.config.networkIDGetter. These
 	// Initialize config.deviceBinder and config.config.networkIDGetter. These
 	// wrap config.DeviceBinder and config.NetworkIDGetter/NetworkID with
 	// wrap config.DeviceBinder and config.NetworkIDGetter/NetworkID with
 	// loggers.
 	// loggers.
@@ -745,6 +753,22 @@ func (config *Config) SetClientParameters(tag string, skipOnError bool, applyPar
 	return nil
 	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{} {
 func (config *Config) makeConfigParameters() map[string]interface{} {
 
 
 	// Build set of config values to apply to parameters.
 	// Build set of config values to apply to parameters.

+ 17 - 12
psiphon/controller.go

@@ -282,6 +282,23 @@ func (controller *Controller) SignalComponentFailure() {
 	controller.stopRunning()
 	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
 // remoteServerListFetcher fetches an out-of-band list of server entries
 // for more tunnel candidates. It fetches when signalled, with retries
 // for more tunnel candidates. It fetches when signalled, with retries
 // on failure.
 // on failure.
@@ -765,18 +782,6 @@ loop:
 	NoticeInfo("exiting run tunnels")
 	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
 // classifyImpairedProtocol tracks "impaired" protocol classifications for failed
 // tunnels. A protocol is classified as impaired if a tunnel using that protocol
 // tunnels. A protocol is classified as impaired if a tunnel using that protocol
 // fails, repeatedly, shortly after the start of the connection. During tunnel
 // 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
 	var response []byte
 	if serverContext.psiphonHttpsClient == nil {
 	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)
 		request, err := makeSSHAPIRequestPayload(params)
 		if err != nil {
 		if err != nil {