// // 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