diff options
-rw-r--r-- | Source/SPConnectionHandler.m | 10 | ||||
-rw-r--r-- | Source/SPSSHTunnel.h | 4 | ||||
-rw-r--r-- | Source/SPSSHTunnel.m | 39 |
3 files changed, 42 insertions, 11 deletions
diff --git a/Source/SPConnectionHandler.m b/Source/SPConnectionHandler.m index 0634df68..d930a299 100644 --- a/Source/SPConnectionHandler.m +++ b/Source/SPConnectionHandler.m @@ -357,7 +357,7 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; NSInteger newState = [theTunnel state]; - // If the user cancelled the password prompt dialog + // If the user cancelled the password prompt dialog, continue with no further action. if ([theTunnel passwordPromptCancelled]) { [self _restoreConnectionInterface]; @@ -365,6 +365,14 @@ static NSString *SPLocalhostAddress = @"127.0.0.1"; } if (newState == SPMySQLProxyIdle) { + + // If the connection closed unexpectedly, and muxing was enabled, disable muxing an re-try. + if ([theTunnel taskExitedUnexpectedly] && [theTunnel connectionMuxingEnabled]) { + [theTunnel setConnectionMuxingEnabled:NO]; + [theTunnel connect]; + return; + } + #ifndef SP_REFACTOR [dbDocument setTitlebarStatus:NSLocalizedString(@"SSH Disconnected", @"SSH disconnected titlebar marker")]; #endif diff --git a/Source/SPSSHTunnel.h b/Source/SPSSHTunnel.h index b59acfe2..25a98cc4 100644 --- a/Source/SPSSHTunnel.h +++ b/Source/SPSSHTunnel.h @@ -67,9 +67,11 @@ SEL stateChangeSelector; BOOL useHostFallback; + BOOL connectionMuxingEnabled; BOOL requestedResponse; BOOL passwordInKeychain; BOOL passwordPromptCancelled; + BOOL taskExitedUnexpectedly; IBOutlet NSWindow *sshQuestionDialog; IBOutlet NSTextField *sshQuestionText; @@ -80,6 +82,8 @@ } @property (readonly) BOOL passwordPromptCancelled; +@property (readwrite) BOOL connectionMuxingEnabled; +@property (readonly) BOOL taskExitedUnexpectedly; - (id)initToHost:(NSString *)theHost port:(NSInteger)thePort login:(NSString *)theLogin tunnellingToPort:(NSInteger)targetPort onHost:(NSString *)targetHost; - (BOOL)setConnectionStateChangeSelector:(SEL)theStateChangeSelector delegate:(id)theDelegate; diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m index 467c43fc..60a8cf84 100644 --- a/Source/SPSSHTunnel.m +++ b/Source/SPSSHTunnel.m @@ -44,6 +44,8 @@ @implementation SPSSHTunnel @synthesize passwordPromptCancelled; +@synthesize connectionMuxingEnabled; +@synthesize taskExitedUnexpectedly; /* * Initialise with the supplied connection details. Host, login and port should all be provided. @@ -67,6 +69,7 @@ delegate = nil; stateChangeSelector = nil; lastError = nil; + connectionMuxingEnabled = YES; debugMessages = [[NSMutableArray alloc] init]; debugMessagesLock = [[NSLock alloc] init]; answerAvailableLock = [[NSLock alloc] init]; @@ -216,9 +219,12 @@ localPort = 0; if (connectionState != SPMySQLProxyIdle) return; + [debugMessagesLock lock]; [debugMessages removeAllObjects]; [debugMessagesLock unlock]; + taskExitedUnexpectedly = NO; + [NSThread detachNewThreadWithName:@"SPSSHTunnel SSH binary communication task" target:self selector:@selector(launchTask:) object:nil]; } @@ -309,19 +315,26 @@ // Enable verbose mode for message parsing [taskArguments addObject:@"-v"]; - // Ensure that the muxed connection can be used for only tunnels, not interactive + // Ensure that the connection can be used for only tunnels, not interactive [taskArguments addObject:@"-N"]; - // Enable automatic connection muxing/sharing, for faster connections - [taskArguments addObject:@"-o ControlMaster=auto"]; + if (connectionMuxingEnabled) { - // Set a custom control path to isolate connection sharing to Sequel Pro, to prevent picking up - // existing masters without forwarding enabled and to isolate from interactive sessions. Use a short - // hashed path to aid length limit issues. - unsigned char hashedPathResult[16]; - NSString *pathString = [NSString stringWithFormat:@"%@@%@:%ld", sshLogin?sshLogin:@"", sshHost, sshPort?sshPort:0]; - CC_MD5([pathString UTF8String], (unsigned int)strlen([pathString UTF8String]), hashedPathResult); - [taskArguments addObject:[NSString stringWithFormat:@"-o ControlPath=%@/SPSSH-%@", [NSFileManager temporaryDirectory], [[[NSData dataWithBytes:hashedPathResult length:16] dataToHexString] substringToIndex:8]]]; + // Enable automatic connection muxing/sharing, for faster connections + [taskArguments addObject:@"-o ControlMaster=auto"]; + + // Set a custom control path to isolate connection sharing to Sequel Pro, to prevent picking up + // existing masters without forwarding enabled and to isolate from interactive sessions. Use a short + // hashed path to aid length limit issues. + unsigned char hashedPathResult[16]; + NSString *pathString = [NSString stringWithFormat:@"%@@%@:%ld", sshLogin?sshLogin:@"", sshHost, sshPort?sshPort:0]; + CC_MD5([pathString UTF8String], (unsigned int)strlen([pathString UTF8String]), hashedPathResult); + [taskArguments addObject:[NSString stringWithFormat:@"-o ControlPath=%@/SPSSH-%@", [NSFileManager temporaryDirectory], [[[NSData dataWithBytes:hashedPathResult length:16] dataToHexString] substringToIndex:8]]]; + } else { + + // Disable muxing if requested + [taskArguments addObject:@"-o ControlMaster=no"]; + } // If the port forwarding fails, exit - as this is the primary use case for the instance [taskArguments addObject:@"-o ExitOnForwardFailure=yes"]; @@ -381,6 +394,11 @@ } [task setEnvironment:taskEnvironment]; + // Add the connection details to the debug messages + [debugMessagesLock lock]; + [debugMessages addObject:[NSString stringWithFormat:@"Used command: %@ %@\n", [task launchPath], [[task arguments] componentsJoinedByString:@" "]]]; + [debugMessagesLock unlock]; + // Set up the standard error pipe standardError = [[NSPipe alloc] init]; [task setStandardError:standardError]; @@ -406,6 +424,7 @@ // If the task closed unexpectedly, alert appropriately if (connectionState != SPMySQLProxyIdle) { connectionState = SPMySQLProxyIdle; + taskExitedUnexpectedly = YES; if (lastError) [lastError release]; lastError = [[NSString alloc] initWithString:NSLocalizedString(@"The SSH Tunnel has unexpectedly closed.", @"SSH tunnel unexpectedly closed")]; if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO]; |