Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 449 lines (403 sloc) 12.7 KB
//
// 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>
#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",@"")
@implementation SSHTunnel
@synthesize connName;
#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
{
if ([ self isRunning ])
return;
shouldStop = NO;
[ NSThread detachNewThreadSelector:@selector(launchTunnel:)
toTarget: self
withObject: nil ];
}
- (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 ];
[super dealloc];
}
@end