diff options
Diffstat (limited to 'SSHTunnel.m')
-rwxr-xr-x | SSHTunnel.m | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/SSHTunnel.m b/SSHTunnel.m new file mode 100755 index 00000000..a2d8e1f7 --- /dev/null +++ b/SSHTunnel.m @@ -0,0 +1,531 @@ +// +// SSHTunnel.m +// SSH Tunnel Manager 2 +// +// Created by Yann Bizeul on Wed Nov 19 2003. +// Copyright (c) 2003 __MyCompanyName__. All rights reserved. +// + +#import "SSHTunnel.h" +#include <unistd.h> + +// start diff lorenz textor +/* +#define T_START NSLocalizedString(@"T_START",@"") +#define T_STOP NSLocalizedString(@"T_STOP",@"") +#define S_IDLE NSLocalizedString(@"S_IDLE",@"") +#define S_CONNECTING NSLocalizedString(@"S_CONNECTING",@"") +#define S_CONNECTED NSLocalizedString(@"S_CONNECTED",@"") +#define S_AUTH NSLocalizedString(@"S_AUTH",@"") +#define S_PORT NSLocalizedString(@"S_PORT",@"") +*/ +#define T_START @"START: %@" +#define T_STOP @"STOP: %@" +#define S_IDLE @"Idle" +#define S_CONNECTING @"Connecting..." +#define S_CONNECTED @"Connected" +#define S_AUTH @"Authenticated" +#define S_PORT "Port %@ forwarded" +// end diff lorenz textor + +@implementation SSHTunnel + +#pragma mark - +#pragma mark Initialization +-(id)init +{ + return [ self initWithName:@"New Tunnel"]; +} +-(id)initWithName:(NSString*)aName +{ + NSDictionary *dictionary = [ NSDictionary dictionaryWithObjectsAndKeys: + [ NSNumber numberWithBool: NO ],@"compression", + [ NSNumber numberWithBool: YES ],@"connAuth", + @"", @"connHost", + aName, @"connName", + @"", @"connPort", + [ NSNumber numberWithBool: NO ],@"connRemote", + @"", @"connUser", + @"3des", @"encryption", + [ NSNumber numberWithBool: NO ],@"socks4", + [ NSNumber numberWithInt: 1080 ], @"socks4p", + [ NSArray array ], @"tunnelsLocal", + [ NSArray array ], @"tunnelsRemote", + [ NSNumber numberWithBool: NO ],@"v1", nil + ]; + return [ self initWithDictionary: dictionary ]; +} +-(id)initWithDictionary:(NSDictionary*)aDictionary +{ + NSEnumerator *e; + NSString *key; + + self = [ super init ]; + e = [[ aDictionary allKeys ] objectEnumerator ]; + while (key = [ e nextObject ]) + { + [ self setValue: [ aDictionary objectForKey: key ] forKey: key ]; + } + code = 0; + if ([[ self valueForKey: @"autoConnect" ] boolValue ]) + [ self startTunnel ]; + return self; +} ++(id)tunnelWithName:(NSString*)aName +{ + return [[ SSHTunnel alloc ] initWithName: aName ]; +} ++(SSHTunnel*)tunnelFromDictionary:(NSDictionary*)aDictionary +{ + return [[ SSHTunnel alloc ] initWithDictionary: aDictionary ]; +} ++(NSArray*)tunnelsFromArray:(NSArray*)anArray +{ + NSMutableArray *newArray; + SSHTunnel *currentTunnel; + NSEnumerator *e; + NSDictionary *currentTunnelDictionary; + + newArray = [ NSMutableArray array ]; + e = [ anArray objectEnumerator ]; + while (currentTunnelDictionary = [ e nextObject ]) + { + currentTunnel = [ SSHTunnel tunnelFromDictionary: currentTunnelDictionary ]; + [ newArray addObject: currentTunnel ]; + } + return [[ newArray copy ] autorelease ]; +} + +#pragma mark - +#pragma mark Adding and removing port redir. +-(void)addLocalTunnel:(NSDictionary*)aDictionary; +{ + NSMutableArray *tempTunnelsLocal = [ NSMutableArray arrayWithArray: tunnelsLocal ]; + [ tempTunnelsLocal addObject: aDictionary ]; + [ tunnelsLocal release ]; + tunnelsLocal = [ tempTunnelsLocal copy ]; +} +- (void)removeLocal:(int)index +{ + NSMutableArray *tempLocalTunnels = [ tunnelsLocal mutableCopy ]; + [ tempLocalTunnels removeObjectAtIndex: index ]; + [ tunnelsLocal release ]; + tunnelsLocal = [ tempLocalTunnels copy ]; + [ tempLocalTunnels release ]; +} +-(void)addRemoteTunnel:(NSDictionary*)aDictionary; +{ + NSMutableArray *tempTunnelsRemote = [ NSMutableArray arrayWithArray: tunnelsRemote ]; + [ tempTunnelsRemote addObject: aDictionary ]; + [ tunnelsRemote release ]; + tunnelsRemote = [ tempTunnelsRemote copy ]; +} +- (void)removeRemote:(int)index +{ + NSMutableArray *tempRemoteTunnels = [ tunnelsRemote mutableCopy ]; + [ tempRemoteTunnels removeObjectAtIndex: index ]; + [ tunnelsRemote release ]; + tunnelsRemote = [ tempRemoteTunnels copy ]; + [ tempRemoteTunnels release ]; +} +- (void)setLocalValue:(NSString*)aValue ofTunnel:(int)index forKey:(NSString*)key +{ + NSMutableArray *tempLocalTunnel; + NSMutableDictionary *tempCurrentTunnel; + + tempLocalTunnel = [tunnelsLocal mutableCopy]; + tempCurrentTunnel = [[ tempLocalTunnel objectAtIndex: index ] mutableCopy ]; + + [ tempCurrentTunnel setObject: aValue forKey: key ]; + [ tempLocalTunnel replaceObjectAtIndex:index withObject:[tempCurrentTunnel copy ]]; + [ tempCurrentTunnel release ]; + [ tunnelsLocal release ]; + tunnelsLocal = [ tempLocalTunnel copy ]; +} +- (void)setRemoteValue:(NSString*)aValue ofTunnel:(int)index forKey:(NSString*)key +{ + NSMutableArray *tempRemoteTunnel; + NSMutableDictionary *tempCurrentTunnel; + + tempRemoteTunnel = [tunnelsRemote mutableCopy]; + tempCurrentTunnel = [[ tempRemoteTunnel objectAtIndex: index ] mutableCopy ]; + + [ tempCurrentTunnel setObject: aValue forKey: key ]; + [ tempRemoteTunnel replaceObjectAtIndex:index withObject:[tempCurrentTunnel copy ]]; + [ tempCurrentTunnel release ]; + [ tunnelsRemote release ]; + tunnelsRemote = [ tempRemoteTunnel copy ]; +} + +#pragma mark - +#pragma mark Execution related +- (void)startTunnel +{ +// NSDictionary *t; +// NSEnumerator *e; +// BOOL asRoot = NO; + + if ([ self isRunning ]) + return; + + shouldStop = NO; + /* + [ arguments addObject: @"-N" ]; + [ arguments addObject: @"-v" ]; + [ arguments addObject: @"-p" ]; + if ([ connPort length ]) + [ arguments addObject: connPort]; + else + [ arguments addObject: @"22" ]; + + if (connRemote) + [ arguments addObject: @"-g" ]; + if (compression) + [ arguments addObject: @"-C" ]; + if (v1) + [ arguments addObject: @"-1" ]; + + [ arguments addObject: @"-c"]; + if (encryption) + [ arguments addObject: encryption]; + else + [ arguments addObject: @"3des"]; + + if (socks4 && socks4p != nil) + { + [ arguments addObject: @"-D" ]; + [ arguments addObject: [ socks4p stringValue ]]; + } + [ arguments addObject: [ NSString stringWithFormat: @"%@@%@", + connUser, connHost ] + ]; + + NSString *hostPort; + e = [ tunnelsLocal objectEnumerator ]; + while (t = [ e nextObject ]) + { + [ arguments addObject: @"-L" ]; + if ([[ t objectForKey:@"hostport"] isEqualTo: @"" ]) + hostPort = [ t objectForKey:@"port" ]; + else + hostPort = [ t objectForKey:@"hostport" ]; + [ arguments addObject: [NSString stringWithFormat:@"%@/%@/%@", + [ t objectForKey:@"port"], + [ t objectForKey:@"host"], + hostPort + ] ]; + if ([[ t objectForKey:@"port"] intValue] < 1024) + asRoot=YES; + } + + e = [ tunnelsRemote objectEnumerator ]; + while (t = [ e nextObject ]) + { + [ arguments addObject: @"-R" ]; + if ([[ t objectForKey:@"hostport"] isEqualTo: @"" ]) + hostPort = [ t objectForKey:@"port" ]; + else + hostPort = [ t objectForKey:@"hostport" ]; + [ arguments addObject: [NSString stringWithFormat:@"%@/%@/%@", + [ t objectForKey:@"port"], + [ t objectForKey:@"host"], + hostPort + ]]; + } + args = [ NSMutableDictionary dictionary ]; + [ args setObject: arguments forKey:@"arguments" ]; + [ args setObject: [ NSNumber numberWithBool: connAuth ] forKey: @"handleAuth" ]; + [ args setObject: connName forKey:@"tunnelName" ]; + [ args setObject: [ NSNumber numberWithBool: asRoot ] forKey: @"asRoot" ]; + + + [ NSThread detachNewThreadSelector:@selector(launchTunnel:) + toTarget: self + withObject: args ]; + */ + [ NSThread detachNewThreadSelector:@selector(launchTunnel:) + toTarget: self + withObject: nil ]; +// [ arguments release ]; + +} +- (void)stopTunnel +{ + if (! [ self isRunning ]) + return; + shouldStop=YES; + [ self setValue: nil forKey: @"status" ]; + [ task terminate ]; + code = 0; + [[ NSNotificationCenter defaultCenter] postNotificationName:@"STMStatusChanged" object:self ]; +} + +- (void)toggleTunnel +{ + if ([ self isRunning ]) + [ self stopTunnel ]; + else + [ self startTunnel ]; +} + +- (void)launchTunnel:(id)foo; +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (task) + [ task release ]; + task = [[ NSTask alloc ] init ]; + NSMutableDictionary *environment = [ NSMutableDictionary dictionaryWithDictionary: [[ NSProcessInfo processInfo ] environment ]]; + NSString *pathToAuthentifier = [[ NSBundle mainBundle ] pathForResource: @"askForPass" ofType: @"sh" ]; + if (socks4) + [ task setLaunchPath: [[ NSBundle mainBundle ] pathForResource: @"ssh" ofType: @"" ]]; + else + [ task setLaunchPath: @"/usr/bin/ssh" ]; + [ task setArguments: [ self arguments ]]; + if (connAuth) + { + [ environment removeObjectForKey: @"SSH_AGENT_PID" ]; + [ environment removeObjectForKey: @"SSH_AUTH_SOCK" ]; + [ environment setObject: pathToAuthentifier forKey: @"SSH_ASKPASS" ]; + [ environment setObject:@":0" forKey:@"DISPLAY" ]; + } + [ environment setObject: connName forKey: @"TUNNEL_NAME" ]; + [ task setEnvironment: environment ]; + + stdErrPipe = [[ NSPipe alloc ] init ]; + [ task setStandardError: stdErrPipe ]; + + [[ NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(stdErr:) + name: @"NSFileHandleDataAvailableNotification" + object:[ stdErrPipe fileHandleForReading]]; + + [[ stdErrPipe fileHandleForReading] waitForDataInBackgroundAndNotify ]; + + NSLog(T_START,connName); + [ self setValue: S_CONNECTING forKey: @"status" ]; + code = 1; + [ task launch ]; + [[ NSNotificationCenter defaultCenter] postNotificationName:@"STMStatusChanged" object:self ]; + [ task waitUntilExit ]; + sleep(1); + code = 0; + [ self setValue: S_IDLE forKey: @"status" ]; + NSLog(T_STOP,connName); + [[ NSNotificationCenter defaultCenter] removeObserver:self + name: @"NSFileHandleDataAvailableNotification" + object:[ stdErrPipe fileHandleForReading]]; + [ task release ]; + task = nil; + [ stdErrPipe release ]; + stdErrPipe = nil; + [[ NSNotificationCenter defaultCenter] postNotificationName:@"STMStatusChanged" object:self ]; + if (! shouldStop) + [ self startTunnel ]; + [ pool release ]; +} + +- (void)stdErr:(NSNotification*)aNotification +{ + NSData *data = [[ aNotification object ] availableData ]; + NSString *log = [[ NSString alloc ] initWithData: data encoding: NSASCIIStringEncoding ]; + BOOL wait = YES; + if ([ log length ]) + { + //NSLog(log); + NSArray *lines = [ log componentsSeparatedByString:@"\n" ]; + NSEnumerator *e = [ lines objectEnumerator ]; + NSString *line; + while (line = [ e nextObject ]) + { + if ([ line rangeOfString:@"Entering interactive session." ].location != NSNotFound) + { + code = 2; + [ self setValue: S_CONNECTED forKey: @"status"]; + } + if ([ line rangeOfString:@"Authentication succeeded" ].location != NSNotFound) + [ self setValue: S_AUTH forKey: @"status"]; + if ([ line rangeOfString:@"Connections to local port" ].location != NSNotFound) + { + NSScanner *s; + NSString *port; + s = [ NSScanner scannerWithString:log]; + [ s scanUpToString: @"Connections to local port " intoString: nil ]; + [ s scanString: @"Connections to local port " intoString: nil ]; + [ s scanUpToString: @"forwarded" intoString:&port]; + [ self setValue: [ NSString stringWithFormat: @"Port %@ forwarded", port ] forKey: @"status"]; + } + if ([ line rangeOfString:@"closed by remote host." ].location != NSNotFound) + { + [ task terminate]; + [ self setValue: @"Connection closed" forKey: @"status"]; + wait = NO; + } + [[ NSNotificationCenter defaultCenter] postNotificationName:@"STMStatusChanged" object:self ]; + } + if (wait) + [[ stdErrPipe fileHandleForReading ] waitForDataInBackgroundAndNotify ]; + } + [ log release] ; +} + +- (BOOL)isRunning +{ + if ([ task isRunning ]) + return YES; + return NO; +} + +#pragma mark - +#pragma mark Getting tunnel informations +- (NSString *)status +{ + if (status) + return status; + return S_IDLE; +} +- (NSArray*)arguments +{ + NSMutableArray *arguments; + NSEnumerator *e; + NSDictionary *t; + BOOL asRoot; + + arguments = [ NSMutableArray array ]; + [ arguments addObject: @"-N" ]; + [ arguments addObject: @"-v" ]; + [ arguments addObject: @"-p" ]; + if ([ connPort length ]) + [ arguments addObject: connPort]; + else + [ arguments addObject: @"22" ]; + + if (connRemote) + [ arguments addObject: @"-g" ]; + if (compression) + [ arguments addObject: @"-C" ]; + if (v1) + [ arguments addObject: @"-1" ]; + + [ arguments addObject: @"-c"]; + if (encryption) + [ arguments addObject: encryption]; + else + [ arguments addObject: @"3des"]; + + if (socks4 && socks4p != nil) + { + [ arguments addObject: @"-D" ]; + [ arguments addObject: [ socks4p stringValue ]]; + } + [ arguments addObject: [ NSString stringWithFormat: @"%@@%@", + connUser, connHost ] + ]; + + NSString *hostPort; + e = [ tunnelsLocal objectEnumerator ]; + while (t = [ e nextObject ]) + { + [ arguments addObject: @"-L" ]; + if ([[ t objectForKey:@"hostport"] isEqualTo: @"" ]) + hostPort = [ t objectForKey:@"port" ]; + else + hostPort = [ t objectForKey:@"hostport" ]; + [ arguments addObject: [NSString stringWithFormat:@"%@/%@/%@", + [ t objectForKey:@"port"], + [ t objectForKey:@"host"], + hostPort + ] ]; + if ([[ t objectForKey:@"port"] intValue] < 1024) + asRoot=YES; + } + + e = [ tunnelsRemote objectEnumerator ]; + while (t = [ e nextObject ]) + { + [ arguments addObject: @"-R" ]; + if ([[ t objectForKey:@"hostport"] isEqualTo: @"" ]) + hostPort = [ t objectForKey:@"port" ]; + else + hostPort = [ t objectForKey:@"hostport" ]; + [ arguments addObject: [NSString stringWithFormat:@"%@/%@/%@", + [ t objectForKey:@"port"], + [ t objectForKey:@"host"], + hostPort + ]]; + } + + return [[ arguments copy ] autorelease ]; +} + +- (NSDictionary*)dictionary +{ + return [ NSDictionary dictionaryWithObjectsAndKeys: + [ NSNumber numberWithBool: compression ],@"compression", + [ NSNumber numberWithBool: connAuth ],@"connAuth", + [ NSNumber numberWithBool: autoConnect ],@"autoConnect", + connHost, @"connHost", + connName, @"connName", + connPort, @"connPort", + [ NSNumber numberWithBool: connRemote ],@"connRemote", + connUser, @"connUser", + encryption, @"encryption", + [ NSNumber numberWithBool: socks4 ],@"socks4", + socks4p, @"socks4p", + tunnelsLocal, @"tunnelsLocal", + tunnelsRemote, @"tunnelsRemote", + [ NSNumber numberWithBool: v1 ],@"v1", nil + ]; +} + + +#pragma mark - +#pragma mark Key/Value coding +- (NSImage*)icon +{ + switch (code) + { + case 0: + return [ NSImage imageNamed: @"offState" ]; + break; + case 1: + return [ NSImage imageNamed: @"middleState" ]; + break; + case 2: + return [ NSImage imageNamed: @"onState" ]; + break; + } + return [ NSImage imageNamed: @"offState" ]; +} +- (void)setValue:(id)value forUndefinedKey:(NSString *)key +{ + NSLog(@"key %@ undefined",key); +} +- (id)valueForUndefinedKey:(NSString *)key +{ + return nil; +} + +#pragma mark - +#pragma mark Misc. +-(void)dealloc +{ + [ self stopTunnel ]; + [ tunnelsLocal release ]; + [ tunnelsRemote release ]; + + [ task release ]; + [ stdErrPipe release ]; + [ connName release ]; + [ status release ]; + [ connPort release ]; + [ encryption release ]; + [ socks4p release ]; + [ connUser release ]; + [ connHost release ]; + + // start diff lorenz textor + [super dealloc]; + // end diff lorenz textor +} +@end |