diff options
Diffstat (limited to 'Source/SPSSHTunnel.m')
-rw-r--r-- | Source/SPSSHTunnel.m | 153 |
1 files changed, 86 insertions, 67 deletions
diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m index ff220318..d2c999c4 100644 --- a/Source/SPSSHTunnel.m +++ b/Source/SPSSHTunnel.m @@ -35,10 +35,13 @@ #import "SPKeychain.h" #import "SPAlertSheets.h" #import "SPThreadAdditions.h" +#import "SPOSInfo.h" #import <netinet/in.h> #import <CommonCrypto/CommonDigest.h> +static unsigned short getRandomPort(); + @implementation SPSSHTunnel @synthesize passwordPromptCancelled; @@ -55,8 +58,7 @@ if (!theHost || !targetPort || !targetHost) return nil; if ((self = [super init])) { - SInt32 systemVersion = 0; - Gestalt(gestaltSystemVersion, &systemVersion); + BOOL isOSVersionAtLeast10_7_0 = [SPOSInfo isOSVersionAtLeastMajor:10 minor:7 patch:0]; // Store the connection settings as appropriate sshHost = [[NSString alloc] initWithString:theHost]; @@ -74,7 +76,7 @@ // Enable connection muxing on 10.7+, but only if a preference is enabled; this is because // muxing causes connection instability for a large number of users (see Issue #1457) - connectionMuxingEnabled = (systemVersion >= 0x1070) && [[NSUserDefaults standardUserDefaults] boolForKey:SPSSHEnableMuxingPreference]; + connectionMuxingEnabled = isOSVersionAtLeast10_7_0 && [[NSUserDefaults standardUserDefaults] boolForKey:SPSSHEnableMuxingPreference]; // Set up a connection for use by the tunnel process tunnelConnectionName = [[NSString alloc] initWithFormat:@"SequelPro-%lu", (unsigned long)[[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]]; @@ -116,7 +118,7 @@ delegate = theDelegate; stateChangeSelector = theStateChangeSelector; - return true; + return YES; } /* @@ -126,8 +128,8 @@ { // As this object is not a NSWindowController, use manual top-level nib item management - if (sshQuestionDialog) [sshQuestionDialog release], sshQuestionDialog = nil; - if (sshPasswordDialog) [sshPasswordDialog release], sshPasswordDialog = nil; + if (sshQuestionDialog) SPClear(sshQuestionDialog); + if (sshPasswordDialog) SPClear(sshPasswordDialog); parentWindow = theWindow; if (![NSBundle loadNibNamed:@"SSHQuestionDialog" owner:self]) { @@ -167,7 +169,7 @@ */ - (BOOL)setPasswordKeychainName:(NSString *)theName account:(NSString *)theAccount { - if (password) [password release], password = nil; + if (password) SPClear(password); passwordInKeychain = YES; keychainName = [[NSString alloc] initWithString:theName]; @@ -264,36 +266,10 @@ // If no local port has yet been chosen, choose one if (!localPort) { - int tempSocket; - struct sockaddr_in tempSocketAddress; - size_t addressLength = sizeof(tempSocketAddress); - if((tempSocket = socket(AF_INET, SOCK_STREAM, 0)) > 0) { - memset(&tempSocketAddress, 0, sizeof(tempSocketAddress)); - tempSocketAddress.sin_family = AF_INET; - tempSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); - tempSocketAddress.sin_port = 0; - if (bind(tempSocket, (struct sockaddr *)&tempSocketAddress, (socklen_t)addressLength) >= 0) { - if (getsockname(tempSocket, (struct sockaddr *)&tempSocketAddress, (uint32_t *)&addressLength) >= 0) { - localPort = ntohs(tempSocketAddress.sin_port); - } - } - close(tempSocket); - } + localPort = getRandomPort(); if (useHostFallback) { - if((tempSocket = socket(AF_INET, SOCK_STREAM, 0)) > 0) { - memset(&tempSocketAddress, 0, sizeof(tempSocketAddress)); - tempSocketAddress.sin_family = AF_INET; - tempSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); - tempSocketAddress.sin_port = 0; - if (bind(tempSocket, (struct sockaddr *)&tempSocketAddress, (socklen_t)addressLength) >= 0) { - if (getsockname(tempSocket, (struct sockaddr *)&tempSocketAddress, (uint32_t *)&addressLength) >= 0) { - localPortFallback = ntohs(tempSocketAddress.sin_port); - } - } - close(tempSocket); - } - + localPortFallback = getRandomPort(); } // Abort if no local free port could be allocated @@ -309,7 +285,20 @@ // Set up the NSTask task = [[NSTask alloc] init]; - [task setLaunchPath: @"/usr/bin/ssh"]; + NSString *launchPath = @"/usr/bin/ssh"; + NSString *userSSHPath = [[NSUserDefaults standardUserDefaults] stringForKey:SPSSHClientPath]; + + if([userSSHPath length]) { + launchPath = userSSHPath; + // And I'm sure we will get issue reports about it anyway! + [debugMessagesLock lock]; + [debugMessages addObject:@"################################################################"]; + [debugMessages addObject:[NSString stringWithFormat:@"# %@",NSLocalizedString(@"Custom SSH binary enabled. Disable in Preferences to rule out incompatibilities!", @"SSH connection : debug header with user-defined ssh binary")]]; + [debugMessages addObject:@"################################################################"]; + [debugMessagesLock unlock]; + } + + [task setLaunchPath:launchPath]; // Prepare to set up the arguments for the task taskArguments = [[NSMutableArray alloc] init]; @@ -373,10 +362,10 @@ [taskArguments addObject:sshHost]; } if (useHostFallback) { - [taskArguments addObject:[NSString stringWithFormat:@"-L %ld/127.0.0.1/%ld", (long)localPort, (long)remotePort]]; - [taskArguments addObject:[NSString stringWithFormat:@"-L %ld/%@/%ld", (long)localPortFallback, remoteHost, (long)remotePort]]; + [taskArguments addObject:[NSString stringWithFormat:@"-L %ld:127.0.0.1:%ld", (long)localPort, (long)remotePort]]; + [taskArguments addObject:[NSString stringWithFormat:@"-L %ld:%@:%ld", (long)localPortFallback, remoteHost, (long)remotePort]]; } else { - [taskArguments addObject:[NSString stringWithFormat:@"-L %ld/%@/%ld", (long)localPort, remoteHost, (long)remotePort]]; + [taskArguments addObject:[NSString stringWithFormat:@"-L %ld:%@:%ld", (long)localPort, remoteHost, (long)remotePort]]; } [task setArguments:taskArguments]; @@ -413,15 +402,24 @@ object:[standardError fileHandleForReading]]; [[standardError fileHandleForReading] waitForDataInBackgroundAndNotify]; - // Launch and run the tunnel - [task launch]; - - // Listen for output - [task waitUntilExit]; + @try { + // Launch and run the tunnel + [task launch]; //throws for invalid paths, missing +x permission + + // Listen for output + [task waitUntilExit]; + } + @catch (NSException *e) { + connectionState = SPMySQLProxyLaunchFailed; + // Log the exception. Could be improved by showing a dedicated alert instead + [debugMessagesLock lock]; + [debugMessages addObject:[NSString stringWithFormat:@"%@: %@\n", [e name], [e reason]]]; + [debugMessagesLock unlock]; + } // On tunnel close, clean up, ready for re-use if the delegate reconnects. - [task release], task = nil; - [standardError release], standardError = nil; + SPClear(task); + SPClear(standardError); [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NSFileHandleDataAvailableNotification" object:nil]; @@ -438,8 +436,8 @@ // Run the run loop for a short time to ensure all task/pipe callbacks are dealt with [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; - [taskEnvironment release], taskEnvironment = nil; - [taskArguments release], taskArguments = nil; + SPClear(taskEnvironment); + SPClear(taskArguments); [pool release]; } @@ -647,7 +645,7 @@ NSString *thePassword = nil; if (requestedPassphrase) { thePassword = [NSString stringWithString:requestedPassphrase]; - [requestedPassphrase release], requestedPassphrase = nil; + SPClear(requestedPassphrase); } // Unlock the lock again @@ -711,8 +709,7 @@ SPKeychain *keychain = [[SPKeychain alloc] init]; [keychain addPassword:thePassword forName:@"SSH" account:currentKeyName withLabel:[NSString stringWithFormat:@"SSH: %@", currentKeyName]]; [keychain release]; - [currentKeyName release]; - currentKeyName = nil; + SPClear(currentKeyName); } } @@ -727,30 +724,52 @@ { delegate = nil; [[NSNotificationCenter defaultCenter] removeObserver:self]; - if (connectionState != SPMySQLProxyIdle) [self disconnect]; + [self disconnect]; [NSObject cancelPreviousPerformRequestsWithTarget:self]; - [sshHost release]; - [sshLogin release]; - [remoteHost release]; - [tunnelConnectionName release]; - [tunnelConnectionVerifyHash release]; + SPClear(sshHost); + SPClear(sshLogin); + SPClear(remoteHost); + SPClear(tunnelConnectionName); + SPClear(tunnelConnectionVerifyHash); [tunnelConnection invalidate]; - [tunnelConnection release]; - [debugMessages release]; - [debugMessagesLock release]; + SPClear(tunnelConnection); + SPClear(debugMessages); + SPClear(debugMessagesLock); [answerAvailableLock tryLock]; [answerAvailableLock unlock]; - [answerAvailableLock release]; - if (password) [password release]; - if (keychainName) [keychainName release]; - if (keychainAccount) [keychainAccount release]; - if (identityFilePath) [identityFilePath release]; + SPClear(answerAvailableLock); + if (password) SPClear(password); + if (keychainName) SPClear(keychainName); + if (keychainAccount) SPClear(keychainAccount); + if (identityFilePath) SPClear(identityFilePath); // As this object is not a NSWindowController, use manual top-level nib item management - if (sshQuestionDialog) [sshQuestionDialog release], sshQuestionDialog = nil; - if (sshPasswordDialog) [sshPasswordDialog release], sshPasswordDialog = nil; + if (sshQuestionDialog) SPClear(sshQuestionDialog); + if (sshPasswordDialog) SPClear(sshPasswordDialog); [super dealloc]; } @end + +#pragma mark - + +unsigned short getRandomPort() { + int port = 0; + int tempSocket; + struct sockaddr_in tempSocketAddress; + size_t addressLength = sizeof(tempSocketAddress); + if((tempSocket = socket(AF_INET, SOCK_STREAM, 0)) > 0) { + memset(&tempSocketAddress, 0, sizeof(tempSocketAddress)); + tempSocketAddress.sin_family = AF_INET; + tempSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); + tempSocketAddress.sin_port = 0; + if (bind(tempSocket, (struct sockaddr *)&tempSocketAddress, (socklen_t)addressLength) >= 0) { + if (getsockname(tempSocket, (struct sockaddr *)&tempSocketAddress, (uint32_t *)&addressLength) >= 0) { + port = ntohs(tempSocketAddress.sin_port); + } + } + close(tempSocket); + } + return port; +} |