aboutsummaryrefslogtreecommitdiffstats
path: root/Source/SPSSHTunnel.m
diff options
context:
space:
mode:
Diffstat (limited to 'Source/SPSSHTunnel.m')
-rw-r--r--Source/SPSSHTunnel.m62
1 files changed, 54 insertions, 8 deletions
diff --git a/Source/SPSSHTunnel.m b/Source/SPSSHTunnel.m
index 6df20d2e..a0a3bdd7 100644
--- a/Source/SPSSHTunnel.m
+++ b/Source/SPSSHTunnel.m
@@ -45,15 +45,13 @@
sshHost = [[NSString alloc] initWithString:theHost];
sshLogin = [[NSString alloc] initWithString:(theLogin?theLogin:@"")];
sshPort = thePort;
- if ([theHost isEqualToString:targetHost]) {
- remoteHost = [[NSString alloc] initWithString:@"127.0.0.1"];
- } else {
- remoteHost = [[NSString alloc] initWithString:targetHost];
- }
+ useHostFallback = [theHost isEqualToString:targetHost];
+ remoteHost = [[NSString alloc] initWithString:targetHost];
remotePort = targetPort;
delegate = nil;
stateChangeSelector = nil;
lastError = nil;
+ debugMessages = [[NSMutableArray alloc] init];
// Set up a connection for use by the tunnel process
tunnelConnectionName = [[NSString alloc] initWithFormat:@"SequelPro-%f", [[NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]] hash]];
@@ -149,11 +147,20 @@
}
/*
+ * Returns all the debug text for this tunnel as a string, separated
+ * by line endings.
+ */
+- (NSString *) debugMessages {
+ return [debugMessages componentsJoinedByString:@"\n"];
+}
+
+/*
* Initiate the SSH tunnel connection, launching the task in a background thread.
*/
- (void) connect
{
localPort = 0;
+ [debugMessages removeAllObjects];
if (connectionState != SPSSH_STATE_IDLE || (!passwordInKeychain && !password)) return;
[NSThread detachNewThreadSelector:@selector(launchTask:) toTarget: self withObject: nil ];
}
@@ -208,8 +215,24 @@
close(tempSocket);
}
+ 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, addressLength) >= 0) {
+ if (getsockname(tempSocket, (struct sockaddr *)&tempSocketAddress, (uint32_t *)&addressLength) >= 0) {
+ localPortFallback = ntohs(tempSocketAddress.sin_port);
+ }
+ }
+ close(tempSocket);
+ }
+
+ }
+
// Abort if no local free port could be allocated
- if (!localPort) {
+ if (!localPort || (useHostFallback && !localPortFallback)) {
connectionState = SPSSH_STATE_IDLE;
if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
if (lastError) [lastError release];
@@ -242,7 +265,12 @@
} else {
[taskArguments addObject:sshHost];
}
- [taskArguments addObject:[NSString stringWithFormat:@"-L %i/%@/%i", localPort, remoteHost, remotePort]];
+ if (useHostFallback) {
+ [taskArguments addObject:[NSString stringWithFormat:@"-L %i/127.0.0.1/%i", localPort, remotePort]];
+ [taskArguments addObject:[NSString stringWithFormat:@"-L %i/%@/%i", localPortFallback, remoteHost, remotePort]];
+ } else {
+ [taskArguments addObject:[NSString stringWithFormat:@"-L %i/%@/%i", localPort, remoteHost, remotePort]];
+ }
[task setArguments:taskArguments];
// Set up the environment for the task
@@ -337,7 +365,9 @@
if ([notificationText length]) {
messages = [notificationText componentsSeparatedByString:@"\n"];
enumerator = [messages objectEnumerator];
- while (message = [enumerator nextObject]) {
+ while (message = [[enumerator nextObject] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
+ if (![message length]) continue;
+ [debugMessages addObject:[NSString stringWithString:message]];
if ([message rangeOfString:@"Entering interactive session."].location != NSNotFound) {
connectionState = SPSSH_STATE_CONNECTED;
@@ -363,6 +393,11 @@
lastError = [[NSString alloc] initWithString:NSLocalizedString(@"The SSH Tunnel could not authenticate with the remote host. Please check your password and ensure you still have access.", @"SSH tunnel authentication failed message")];
if (delegate) [delegate performSelectorOnMainThread:stateChangeSelector withObject:self waitUntilDone:NO];
}
+ if ([message rangeOfString:@"connect failed: Connection refused" ].location != NSNotFound) {
+ connectionState = SPSSH_STATE_FORWARDING_FAILED;
+ if (lastError) [lastError release];
+ lastError = [[NSString alloc] initWithString:NSLocalizedString(@"The SSH Tunnel was established successfully, but could not forward data to the remote port as the remote port refused the connection.", @"SSH tunnel forwarding port connection refused message")];
+ }
if ([message rangeOfString:@"Operation timed out" ].location != NSNotFound) {
connectionState = SPSSH_STATE_IDLE;
[task terminate];
@@ -389,6 +424,15 @@
}
/*
+ * Returns the local port assigned for fallback use by the tunnel, if any
+ */
+- (int) localPortFallback
+{
+ if (!useHostFallback) return 0;
+ return localPortFallback;
+}
+
+/*
* Method to request the password for the current connection, as used by TunnelPassphraseRequester;
* called with a verification hash to check against the stored hash, to provide basic security. Note
* that this is easily bypassed, but if bypassed the password can already easily be retrieved in the same way.
@@ -519,12 +563,14 @@
- (void)dealloc
{
+ if (connectionState != SPSSH_STATE_IDLE) [self disconnect];
[sshHost release];
[sshLogin release];
[remoteHost release];
[tunnelConnectionName release];
[tunnelConnectionVerifyHash release];
[tunnelConnection release];
+ [debugMessages release];
if (password) [password release];
if (keychainName) [keychainName release];
if (keychainAccount) [keychainAccount release];