diff --git a/ChangeLog b/ChangeLog
index aacd8d91a..ad58336e5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,11 @@
* Source/NSObject.m: Fix a few errors which crept in to the map
table based reference counting.
* Examples/GNUmakefile: Permit local makefile to be missing.
+ * Source/UnixFileHandle.m: Added outgoing SOCKS5 support.
+ * Source/NSFileHandle.m: Added SOCKS5 documentation.
+ * Source/Base/gsdoc: Dopcumented SOCKS enironemnet variables and defs.
+ * Source/NSNotification.m: Made -description more informative.
+ * Testing/call.m: Trivial test program for tcp connections.
2002-06-10 Richard Frith-Macdonald
diff --git a/Headers/gnustep/base/UnixFileHandle.h b/Headers/gnustep/base/UnixFileHandle.h
index a0e1fff57..5c519d25e 100644
--- a/Headers/gnustep/base/UnixFileHandle.h
+++ b/Headers/gnustep/base/UnixFileHandle.h
@@ -48,7 +48,7 @@
BOOL readOK;
BOOL writeOK;
NSMutableDictionary *readInfo;
- int readPos;
+ int readMax;
NSMutableArray *writeInfo;
int writePos;
NSString *address;
diff --git a/Source/Base.gsdoc b/Source/Base.gsdoc
index f4c1be0b1..4bd901ac2 100644
--- a/Source/Base.gsdoc
+++ b/Source/Base.gsdoc
@@ -96,6 +96,17 @@
should cope with both cases anyway.
+ GSSOCKS
+
+
+ May be used to specify a default SOCKS5 server (and optionally
+ a port separated from the server by a colon) to which tcp/ip
+ connections made using the NSFileHandle extension methods
+ should be directed.
+ This default overrides the SOCKS5_SERVER and SOCKS_SERVER
+ environment variables.
+
+
NSWriteOldStylePropertyLists
@@ -305,6 +316,25 @@
allocated for objects will be leaked!
+ SOCKS5_SERVER
+
+
+ Specifies the default socks server to be used when making
+ outgoing tcp/ip connections using NSFileHandle. This may
+ also specify a port after the host name (and spearated
+ from it by a colon).
+ This environment variable is used only if the GSSOCKS
+ user default is not set.
+
+
+ SOCKS_SERVER
+
+
+ Equivalent to SOCKS5_SERVER, but used only if that is not
+ defined.
+
+
+
TZ
diff --git a/Source/NSFileHandle.m b/Source/NSFileHandle.m
index 1eee65f51..26903814e 100644
--- a/Source/NSFileHandle.m
+++ b/Source/NSFileHandle.m
@@ -309,6 +309,12 @@ NSString * const NSFileHandleOperationException
@implementation NSFileHandle (GNUstepExtensions)
+/**
+ * Opens an outgoing network connection by initiating an asynchronous
+ * connection (see
+ * [+fileHandleAsClientInBackgroundAtAddress:service:protocol:forModes:])
+ * and waiting for it to succeed, fail, or time out.
+ */
+ (id) fileHandleAsClientAtAddress: (NSString*)address
service: (NSString*)service
protocol: (NSString*)protocol
@@ -320,6 +326,10 @@ NSString * const NSFileHandleOperationException
protocol: protocol]);
}
+/**
+ * Opens an outgoing network connection asynchronously using
+ * [+fileHandleAsClientInBackgroundAtAddress:service:protocol:forModes:]
+ */
+ (id) fileHandleAsClientInBackgroundAtAddress: (NSString*)address
service: (NSString*)service
protocol: (NSString*)protocol
@@ -332,6 +342,44 @@ NSString * const NSFileHandleOperationException
forModes: nil]);
}
+/**
+ *
+ * Opens an outgoing network connection asynchronously.
+ *
+ *
+ * -
+ * The address is the name (or IP dotted quad) of the machine to
+ * which the connection should be made.
+ *
+ * -
+ * The service is the name (or number) of the port to
+ * which the connection should be made.
+ *
+ * -
+ * The protocol is provided so support different network protocols,
+ * but at present only 'tcp' is supported. However, a protocol
+ * specification of the form 'socks-...' can be used to control socks5
+ * support.
+ * If '...' is empty (ie the string is just 'socks-' then the connection
+ * is not made via a socks server.
+ * Otherwise, the text '...' must be the name of the host on which the
+ * socks5 server is running, with an optional port number separated
+ * from the host name by a colon.
+ *
+ * -
+ * If modes is nil or empty, uses NSDefaultRunLoopMode.
+ *
+ *
+ *
+ * This method supports connection through a firewall via socks5. The
+ * socks5 connection may be controlled via the protocol argument, but if
+ * no socks infromation is supplied here, the GSSOCKS user default
+ * will be used, and failing that, the SOCKS5_SERVER or
+ * SOCKS_SERVER environment variables will be used to set the
+ * socks server. If none of these mechanisms specify a socks server, the
+ * connection will be made directly rather than through socks.
+ *
+ */
+ (id) fileHandleAsClientInBackgroundAtAddress: (NSString*)address
service: (NSString*)service
protocol: (NSString*)protocol
diff --git a/Source/NSNotification.m b/Source/NSNotification.m
index d1dc6be38..d1837df60 100644
--- a/Source/NSNotification.m
+++ b/Source/NSNotification.m
@@ -47,16 +47,6 @@
@implementation NSNotification
-- (void) dealloc
-{
- RELEASE(_name);
- TEST_RELEASE(_object);
- TEST_RELEASE(_info);
- [super dealloc];
-}
-
-
-/* Creating autoreleased Notification objects. */
+ (NSNotification*) notificationWithName: (NSString*)name
object: (id)object
@@ -72,8 +62,31 @@
return [self notificationWithName: name object: object userInfo: nil];
}
-
-/* Querying a Notification object. */
+
+- (id) copyWithZone: (NSZone*)zone
+{
+ if (NSShouldRetainWithZone (self, zone))
+ {
+ return [self retain];
+ }
+ return [[[self class] allocWithZone: zone] initWithName: _name
+ object: _object
+ userInfo: _info];
+}
+
+- (void) dealloc
+{
+ RELEASE(_name);
+ TEST_RELEASE(_object);
+ TEST_RELEASE(_info);
+ [super dealloc];
+}
+
+- (NSString*) description
+{
+ return [[super description] stringByAppendingFormat:
+ @" Name: %@ Object: %@ Info: %@", _name, _object, _info];
+}
- (NSString*) name
{
@@ -90,20 +103,6 @@
return _info;
}
-
-/* NSCopying protocol. */
-
-- (id) copyWithZone: (NSZone*)zone
-{
- if (NSShouldRetainWithZone (self, zone))
- return [self retain];
-
- return [[[self class] allocWithZone: zone]
- initWithName: _name
- object: _object
- userInfo: _info];
-}
-
/*
* NSCoding protocol - the MacOS-X documentation says it should conform,
* but how can we meaningfully encode/decode the object and userInfo.
diff --git a/Source/UnixFileHandle.m b/Source/UnixFileHandle.m
index 1222dba13..eec708a7c 100644
--- a/Source/UnixFileHandle.m
+++ b/Source/UnixFileHandle.m
@@ -2,7 +2,7 @@
Copyright (C) 1997-1999 Free Software Foundation, Inc.
Written by: Richard Frith-Macdonald
- Date: 1997
+ Date: 1997, 2002
This file is part of the GNUstep Base Library.
@@ -37,6 +37,9 @@
#include
#include
#include
+#include
+#include
+#include
#include "../Tools/gdomap.h"
@@ -121,21 +124,24 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
{
NSHost* host = [NSHost hostWithName: name];
- if (host)
- name = [host address];
-
+ if (host != nil)
+ {
+ name = [host address];
+ }
#ifndef HAVE_INET_ATON
sin->sin_addr.s_addr = inet_addr([name lossyCString]);
if (sin->sin_addr.s_addr == INADDR_NONE)
- return NO;
#else
if (inet_aton([name lossyCString], &sin->sin_addr) == 0)
- return NO;
#endif
+ {
+ return NO;
+ }
}
else
- sin->sin_addr.s_addr = GSSwapHostI32ToBig(INADDR_ANY);
-
+ {
+ sin->sin_addr.s_addr = GSSwapHostI32ToBig(INADDR_ANY);
+ }
if (svc == nil)
{
sin->sin_port = 0;
@@ -249,77 +255,464 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
return [self initWithNullDevice];
}
+/**
+ * Initialise as a client socket connection ... do this by using
+ * [-initAsClientInBackgroundAtAddress:service:protocol:forModes:]
+ * and running the current run loop in NSDefaultRunLoopMode until
+ * the connection attempt succeeds, fails, or times out.
+ */
- (id) initAsClientAtAddress: (NSString*)a
service: (NSString*)s
protocol: (NSString*)p
{
- int net;
- struct sockaddr_in sin;
+ NSRunLoop *loop;
+ NSDate *limit;
- if (s == nil)
- {
- NSLog(@"bad argument - service is nil");
- RELEASE(self);
- return nil;
- }
- if (getAddr(a, s, p, &sin) == NO)
- {
- NSLog(@"bad address-service-protocol combination");
- RELEASE(self);
- return nil;
- }
- [self setAddr: &sin];
+ self = [self initAsClientInBackgroundAtAddress: a
+ service: s
+ protocol: p
+ forModes: nil];
- if ((net = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0)
+ loop = [NSRunLoop currentRunLoop];
+ limit = [NSDate dateWithTimeIntervalSinceNow: 300];
+ while ([limit timeIntervalSinceNow] > 0
+ && (readInfo != nil || [writeInfo count] > 0))
{
- NSLog(@"unable to create socket - %s", GSLastErrorStr(errno));
- RELEASE(self);
- return nil;
+ [loop runMode: NSDefaultRunLoopMode
+ beforeDate: limit];
}
-
- self = [self initWithFileDescriptor: net closeOnDealloc: YES];
- if (self)
+ if (readInfo != nil || [writeInfo count] > 0 || readOK == NO)
{
- if (connect(net, (struct sockaddr*)&sin, sizeof(sin)) < 0)
- {
- NSLog(@"unable to make connection to %s:%d - %s",
- inet_ntoa(sin.sin_addr),
- GSSwapBigI16ToHost(sin.sin_port), GSLastErrorStr(errno));
- RELEASE(self);
- return nil;
- }
-
- connectOK = NO;
- acceptOK = NO;
- readOK = YES;
- writeOK = YES;
+ /* Must have timed out or failed */
+ DESTROY(self);
+ }
+ else
+ {
+ [self setNonBlocking: NO];
}
return self;
}
+/*
+ * States for socks connection negotiation
+ */
+static NSString * GSSOCKSConnect = @"GSSOCKSConnect";
+static NSString * GSSOCKSSendAuth = @"GSSOCKSSendAuth";
+static NSString * GSSOCKSRecvAuth = @"GSSOCKSRecvAuth";
+static NSString * GSSOCKSSendConn = @"GSSOCKSSendConn";
+static NSString * GSSOCKSRecvConn = @"GSSOCKSRecvConn";
+static NSString * GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
+
+- (void) _socksHandler: (NSNotification*)aNotification
+{
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ NSString *name = [aNotification name];
+ NSDictionary *info = (NSMutableDictionary*)[aNotification userInfo];
+ NSArray *modes;
+ NSString *error;
+ NSMutableDictionary *i = nil;
+ NSNotification *n = nil;
+
+ NSDebugMLLog(@"NSFileHandle", @"%@ SOCKS connection: %@",
+ self, aNotification);
+
+ [nc removeObserver: self name: name object: self];
+
+ modes = (NSArray*)[info objectForKey: NSFileHandleNotificationMonitorModes];
+ error = [info objectForKey: GSFileHandleNotificationError];
+
+ if (error == nil)
+ {
+ if (name == GSSOCKSConnect)
+ {
+ NSData *item;
+
+ /*
+ * Send an authorisation record to the SOCKS server.
+ */
+ i = [info mutableCopy];
+ /*
+ * Authorisation record is at least three bytes -
+ * socks version (5)
+ * authorisation method bytes to follow (1)
+ * say we do no authorisation (0)
+ */
+ item = [[NSData alloc] initWithBytes: "\5\1\0"
+ length: 3];
+ [i setObject: item forKey: NSFileHandleNotificationDataItem];
+ RELEASE(item);
+ [i setObject: GSSOCKSSendAuth forKey: NotificationKey];
+ [writeInfo addObject: i];
+ RELEASE(i);
+ [nc addObserver: self
+ selector: @selector(_socksHandler:)
+ name: GSSOCKSSendAuth
+ object: self];
+ [self watchWriteDescriptor];
+ }
+ else if (name == GSSOCKSSendAuth)
+ {
+ NSMutableData *item;
+
+ /*
+ * We have written the authorisation record, so we
+ * request a response from the SOCKS server.
+ */
+ readMax = 2;
+ readInfo = [info mutableCopy];
+ [readInfo setObject: GSSOCKSRecvAuth forKey: NotificationKey];
+ item = [[NSMutableData alloc] initWithCapacity: 0];
+ [readInfo setObject: item forKey: NSFileHandleNotificationDataItem];
+ RELEASE(item);
+ [nc addObserver: self
+ selector: @selector(_socksHandler:)
+ name: GSSOCKSRecvAuth
+ object: self];
+ [self watchReadDescriptorForModes: modes];
+ }
+ else if (name == GSSOCKSRecvAuth)
+ {
+ NSData *response;
+ const unsigned char *bytes;
+
+ response = [info objectForKey: NSFileHandleNotificationDataItem];
+ bytes = (const unsigned char*)[response bytes];
+ if ([response length] != 2)
+ {
+ error = @"authorisation response from SOCKS was not two bytes";
+ }
+ else if (bytes[0] != 5)
+ {
+ error = @"authorisation response from SOCKS had wrong version";
+ }
+ else if (bytes[1] != 0)
+ {
+ error = @"authorisation response from SOCKS had wrong method";
+ }
+ else
+ {
+ NSData *item;
+ char buf[10];
+ const char *ptr;
+ int p;
+
+ /*
+ * Send the address information to the SOCKS server.
+ */
+ i = [info mutableCopy];
+ /*
+ * Connect command is ten bytes -
+ * socks version
+ * connect command
+ * reserved byte
+ * address type
+ * address 4 bytes (big endian)
+ * port 2 bytes (big endian)
+ */
+ buf[0] = 5; // Socks version number
+ buf[1] = 1; // Connect command
+ buf[2] = 0; // Reserved
+ buf[3] = 1; // Address type (IPV4)
+ ptr = [address lossyCString];
+ buf[4] = atoi(ptr);
+ while (isdigit(*ptr))
+ ptr++;
+ ptr++;
+ buf[5] = atoi(ptr);
+ while (isdigit(*ptr))
+ ptr++;
+ ptr++;
+ buf[6] = atoi(ptr);
+ while (isdigit(*ptr))
+ ptr++;
+ ptr++;
+ buf[7] = atoi(ptr);
+ p = [service intValue];
+ buf[8] = ((p & 0xff00) >> 8);
+ buf[9] = (p & 0xff);
+
+ item = [[NSData alloc] initWithBytes: buf length: 10];
+ [i setObject: item forKey: NSFileHandleNotificationDataItem];
+ RELEASE(item);
+ [i setObject: GSSOCKSSendConn
+ forKey: NotificationKey];
+ [writeInfo addObject: i];
+ RELEASE(i);
+ [nc addObserver: self
+ selector: @selector(_socksHandler:)
+ name: GSSOCKSSendConn
+ object: self];
+ [self watchWriteDescriptor];
+ }
+ }
+ else if (name == GSSOCKSSendConn)
+ {
+ NSMutableData *item;
+
+ /*
+ * We have written the connect command, so we
+ * request a response from the SOCKS server.
+ */
+ readMax = 4;
+ readInfo = [info mutableCopy];
+ [readInfo setObject: GSSOCKSRecvConn forKey: NotificationKey];
+ item = [[NSMutableData alloc] initWithCapacity: 0];
+ [readInfo setObject: item forKey: NSFileHandleNotificationDataItem];
+ RELEASE(item);
+ [nc addObserver: self
+ selector: @selector(_socksHandler:)
+ name: GSSOCKSRecvConn
+ object: self];
+ [self watchReadDescriptorForModes: modes];
+ }
+ else if (name == GSSOCKSRecvConn)
+ {
+ NSData *response;
+ const unsigned char *bytes;
+ unsigned len = 0;
+
+ response = [info objectForKey: NSFileHandleNotificationDataItem];
+ bytes = (const unsigned char*)[response bytes];
+ if ([response length] != 4)
+ {
+ error = @"connect response from SOCKS had bad length";
+ }
+ else if (bytes[0] != 5)
+ {
+ error = @"connect response from SOCKS had wrong version";
+ }
+ else if (bytes[1] != 0)
+ {
+ switch (bytes[1])
+ {
+ case 1:
+ error = @"SOCKS server general failure";
+ break;
+ case 2:
+ error = @"SOCKS server says permission denied";
+ break;
+ case 3:
+ error = @"SOCKS server says network unreachable";
+ break;
+ case 4:
+ error = @"SOCKS server says host unreachable";
+ break;
+ case 5:
+ error = @"SOCKS server says connection refused";
+ break;
+ case 6:
+ error = @"SOCKS server says connection timed out";
+ break;
+ case 7:
+ error = @"SOCKS server says command not supported";
+ break;
+ case 8:
+ error = @"SOCKS server says address type not supported";
+ break;
+ default:
+ error = @"connect response from SOCKS was failure";
+ break;
+ }
+ }
+ else if (bytes[3] == 1)
+ {
+ len = 4; // Fixed size (IPV4) address
+ }
+ else if (bytes[3] == 3)
+ {
+ len = 1 + bytes[4]; // Domain name with leading length
+ }
+ else if (bytes[3] == 4)
+ {
+ len = 16; // Fixed size (IPV6) address
+ }
+ else
+ {
+ error = @"SOCKS server returned unknown address type";
+ }
+
+ if (error == nil)
+ {
+ NSMutableData *item;
+
+ /*
+ * We have received a success, so we must now consume the
+ * address and port information the SOCKS server sends.
+ */
+ readMax = len + 2;
+ readInfo = [info mutableCopy];
+ [readInfo setObject: GSSOCKSRecvAddr forKey: NotificationKey];
+ item = [[NSMutableData alloc] initWithCapacity: 0];
+ [readInfo setObject: item
+ forKey: NSFileHandleNotificationDataItem];
+ RELEASE(item);
+ [nc addObserver: self
+ selector: @selector(_socksHandler:)
+ name: GSSOCKSRecvAddr
+ object: self];
+ [self watchReadDescriptorForModes: modes];
+ }
+ }
+ else if (name == GSSOCKSRecvAddr)
+ {
+ /*
+ * Success ... We read the address from the socks server so
+ * the connection is now ready to go.
+ */
+ name = GSFileHandleConnectCompletionNotification;
+ i = [info mutableCopy];
+ [i setObject: name forKey: NotificationKey];
+ n = [NSNotification notificationWithName: name
+ object: self
+ userInfo: i];
+ RELEASE(i);
+ }
+ else
+ {
+ /*
+ * Argh ... unexpected notification.
+ */
+ error = @"unexpected notification during SOCKS connection";
+ }
+ }
+
+ /*
+ * If 'error' is non-null, we set up a notification to tell people
+ * the connection failed.
+ */
+ if (error != nil)
+ {
+ NSDebugMLLog(@"NSFileHandle", @"%@ SOCKS error: %@", self, error);
+
+ /*
+ * An error in the initial connection ... notify observers
+ * by re-posting the notification with a new name.
+ */
+ name = GSFileHandleConnectCompletionNotification;
+ i = [info mutableCopy];
+ [i setObject: name forKey: NotificationKey];
+ [i setObject: error forKey: GSFileHandleNotificationError];
+ n = [NSNotification notificationWithName: name
+ object: self
+ userInfo: i];
+ RELEASE(i);
+ }
+
+ /*
+ * If a notification has been set up, we post it as the last thing we do.
+ */
+ if (n != nil)
+ {
+ NSNotificationQueue *q;
+
+ q = [NSNotificationQueue defaultQueue];
+ [q enqueueNotification: n
+ postingStyle: NSPostASAP
+ coalesceMask: NSNotificationNoCoalescing
+ forModes: modes];
+ }
+}
+
- (id) initAsClientInBackgroundAtAddress: (NSString*)a
service: (NSString*)s
protocol: (NSString*)p
forModes: (NSArray*)modes
{
- int net;
+ static NSString *esocks = nil;
+ static NSString *dsocks = nil;
+ BOOL beenHere = NO;
+ int net;
struct sockaddr_in sin;
+ NSString *shost = nil;
+ NSString *sport = nil;
+
+ if (beenHere == NO);
+ {
+ NSUserDefaults *defs;
+
+ beenHere = YES;
+ defs = [NSUserDefaults standardUserDefaults];
+ dsocks = [[defs stringForKey: @"GSSOCKS"] copy];
+ if (dsocks == nil)
+ {
+ NSDictionary *env;
+
+ env = [[NSProcessInfo processInfo] environment];
+ esocks = [env objectForKey: @"SOCKS5_SERVER"];
+ if (esocks == nil)
+ {
+ esocks = [env objectForKey: @"SOCKS_SERVER"];
+ }
+ [esocks = esocks copy];
+ }
+ }
if (a == nil || [a isEqualToString: @""])
- a = @"localhost";
+ {
+ a = @"localhost";
+ }
if (s == nil)
{
NSLog(@"bad argument - service is nil");
RELEASE(self);
return nil;
}
+
+ /**
+ * A protocol fo the form 'socks-...' controls socks operation,
+ * overriding defaults and environment variables.
+ * If it is just 'socks-' it turns off socks for this fiel handle.
+ * Otherwise, the following text must be the name of the socks server
+ * (optionally followed by :port).
+ */
+ if ([p hasPrefix: @"socks-"] == YES)
+ {
+ shost = [p substringFromIndex: 6];
+ p = @"tcp";
+ }
+ else if (dsocks != nil)
+ {
+ shost = dsocks; // GSSOCKS user default
+ }
+ else
+ {
+ shost = esocks; // SOCKS_SERVER environment variable.
+ }
+
+ if (shost != nil && [shost length] > 0)
+ {
+ NSRange r;
+
+ r = [shost rangeOfString: @":"];
+ if (r.length > 0)
+ {
+ sport = [shost substringFromIndex: NSMaxRange(r)];
+ shost = [shost substringToIndex: r.location];
+ }
+ else
+ {
+ sport = @"1080";
+ }
+ p = @"tcp";
+ }
+
if (getAddr(a, s, p, &sin) == NO)
{
NSLog(@"bad address-service-protocol combination");
RELEASE(self);
return nil;
}
- [self setAddr: &sin];
+ [self setAddr: &sin]; // Store the address of the remote end.
+
+ if (shost != nil)
+ {
+ if (getAddr(shost, sport, p, &sin) == NO)
+ {
+ NSLog(@"bad SOCKS host-port combination");
+ RELEASE(self);
+ return nil;
+ }
+ }
if ((net = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0)
{
@@ -338,18 +731,39 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
if (errno != EINPROGRESS)
{
NSLog(@"unable to make connection to %s:%d - %s",
- inet_ntoa(sin.sin_addr),
- GSSwapBigI16ToHost(sin.sin_port), GSLastErrorStr(errno));
+ inet_ntoa(sin.sin_addr),
+ GSSwapBigI16ToHost(sin.sin_port), GSLastErrorStr(errno));
RELEASE(self);
return nil;
}
info = [[NSMutableDictionary alloc] initWithCapacity: 4];
[info setObject: address forKey: NSFileHandleNotificationDataItem];
- [info setObject: GSFileHandleConnectCompletionNotification
- forKey: NotificationKey];
+ if (shost == nil)
+ {
+ [info setObject: GSFileHandleConnectCompletionNotification
+ forKey: NotificationKey];
+ }
+ else
+ {
+ NSNotificationCenter *nc;
+
+ /*
+ * If we are making a socks connection, register self as an
+ * observer of notifications and ensure we will manage this.
+ */
+ nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver: self
+ selector: @selector(_socksHandler:)
+ name: GSSOCKSConnect
+ object: self];
+ [info setObject: GSSOCKSConnect
+ forKey: NotificationKey];
+ }
if (modes)
- [info setObject: modes forKey: NSFileHandleNotificationMonitorModes];
+ {
+ [info setObject: modes forKey: NSFileHandleNotificationMonitorModes];
+ }
[writeInfo addObject: info];
RELEASE(info);
[self watchWriteDescriptor];
@@ -609,7 +1023,7 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
closeOnDealloc = flag;
readInfo = nil;
writeInfo = [NSMutableArray new];
- readPos = 0;
+ readMax = 0;
writePos = 0;
readOK = YES;
writeOK = YES;
@@ -714,10 +1128,10 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
id info = [writeInfo objectAtIndex: 0];
id operation = [info objectForKey: NotificationKey];
- if (operation == GSFileHandleConnectCompletionNotification)
+ if (operation != GSFileHandleWriteCompletionNotification)
{
[NSException raise: NSFileHandleOperationException
- format: @"connect already in progress"];
+ format: @"connect in progress"];
}
}
}
@@ -799,7 +1213,9 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
[self checkRead];
if (isNonBlocking == YES)
- [self setNonBlocking: NO];
+ {
+ [self setNonBlocking: NO];
+ }
d = [NSMutableData dataWithCapacity: 0];
#if USE_ZLIB
if (gzDescriptor != 0)
@@ -831,7 +1247,9 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
[self checkRead];
if (isNonBlocking == YES)
- [self setNonBlocking: NO];
+ {
+ [self setNonBlocking: NO];
+ }
if (len <= 65536)
{
char *buf;
@@ -943,7 +1361,7 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
- (void) acceptConnectionInBackgroundAndNotifyForModes: (NSArray*)modes
{
[self checkAccept];
- readPos = 0;
+ readMax = 0;
RELEASE(readInfo);
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
[readInfo setObject: NSFileHandleConnectionAcceptedNotification
@@ -958,14 +1376,17 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
- (void) readInBackgroundAndNotifyForModes: (NSArray*)modes
{
+ NSMutableData *d;
+
[self checkRead];
- readPos = 0;
+ readMax = -1; // Accept any quantity of data.
RELEASE(readInfo);
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
[readInfo setObject: NSFileHandleReadCompletionNotification
forKey: NotificationKey];
- [readInfo setObject: [NSMutableData dataWithCapacity: 0]
- forKey: NSFileHandleNotificationDataItem];
+ d = [[NSMutableData alloc] initWithCapacity: 0];
+ [readInfo setObject: d forKey: NSFileHandleNotificationDataItem];
+ RELEASE(d);
[self watchReadDescriptorForModes: modes];
}
@@ -976,14 +1397,17 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
- (void) readToEndOfFileInBackgroundAndNotifyForModes: (NSArray*)modes
{
+ NSMutableData *d;
+
[self checkRead];
- readPos = 0;
+ readMax = 0;
RELEASE(readInfo);
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
[readInfo setObject: NSFileHandleReadToEndOfFileCompletionNotification
forKey: NotificationKey];
- [readInfo setObject: [NSMutableData dataWithCapacity: 0]
- forKey: NSFileHandleNotificationDataItem];
+ d = [[NSMutableData alloc] initWithCapacity: 0];
+ [readInfo setObject: d forKey: NSFileHandleNotificationDataItem];
+ RELEASE(d);
[self watchReadDescriptorForModes: modes];
}
@@ -995,7 +1419,7 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
- (void) waitForDataInBackgroundAndNotifyForModes: (NSArray*)modes
{
[self checkRead];
- readPos = 0;
+ readMax = 0;
RELEASE(readInfo);
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
[readInfo setObject: NSFileHandleDataAvailableNotification
@@ -1135,7 +1559,9 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
- (void) synchronizeFile
{
if (isStandardFile)
- (void)sync();
+ {
+ (void)sync();
+ }
}
- (void) truncateFileAtOffset: (unsigned long long)pos
@@ -1155,9 +1581,10 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
[info setObject: item forKey: NSFileHandleNotificationDataItem];
[info setObject: GSFileHandleWriteCompletionNotification
forKey: NotificationKey];
- if (modes)
- [info setObject: modes forKey: NSFileHandleNotificationMonitorModes];
-
+ if (modes != nil)
+ {
+ [info setObject: modes forKey: NSFileHandleNotificationMonitorModes];
+ }
[writeInfo addObject: info];
RELEASE(info);
[self watchWriteDescriptor];
@@ -1172,11 +1599,13 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
{
NSMutableDictionary *info = readInfo;
NSNotification *n;
+ NSNotificationQueue *q;
NSArray *modes;
NSString *name;
[self ignoreReadDescriptor];
readInfo = nil;
+ readMax = 0;
modes = (NSArray*)[info objectForKey: NSFileHandleNotificationMonitorModes];
name = (NSString*)[info objectForKey: NotificationKey];
@@ -1184,15 +1613,17 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
RELEASE(info); /* Retained by the notification. */
- [[NSNotificationQueue defaultQueue] enqueueNotification: n
- postingStyle: NSPostASAP
- coalesceMask: NSNotificationNoCoalescing
+ q = [NSNotificationQueue defaultQueue];
+ [q enqueueNotification: n
+ postingStyle: NSPostASAP
+ coalesceMask: NSNotificationNoCoalescing
forModes: modes];
}
- (void) postWriteNotification
{
NSMutableDictionary *info = [writeInfo objectAtIndex: 0];
+ NSNotificationQueue *q;
NSNotification *n;
NSArray *modes;
NSString *name;
@@ -1206,25 +1637,32 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
writePos = 0;
[writeInfo removeObjectAtIndex: 0]; /* Retained by notification. */
- [[NSNotificationQueue defaultQueue] enqueueNotification: n
- postingStyle: NSPostASAP
- coalesceMask: NSNotificationNoCoalescing
+ q = [NSNotificationQueue defaultQueue];
+ [q enqueueNotification: n
+ postingStyle: NSPostASAP
+ coalesceMask: NSNotificationNoCoalescing
forModes: modes];
if ((writeOK || connectOK) && [writeInfo count] > 0)
- [self watchWriteDescriptor]; /* In case of queued writes. */
+ {
+ [self watchWriteDescriptor]; /* In case of queued writes. */
+ }
}
- (BOOL) readInProgress
{
if (readInfo)
- return YES;
+ {
+ return YES;
+ }
return NO;
}
- (BOOL) writeInProgress
{
if ([writeInfo count] > 0)
- return YES;
+ {
+ return YES;
+ }
return NO;
}
@@ -1234,8 +1672,9 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
NSArray *modes;
if (descriptor < 0)
- return;
-
+ {
+ return;
+ }
l = [NSRunLoop currentRunLoop];
modes = nil;
@@ -1275,7 +1714,6 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
{
return;
}
-
l = [NSRunLoop currentRunLoop];
modes = nil;
@@ -1312,8 +1750,9 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
NSRunLoop *l;
if (descriptor < 0)
- return;
-
+ {
+ return;
+ }
l = [NSRunLoop currentRunLoop];
[self setNonBlocking: YES];
if (modes && [modes count])
@@ -1341,14 +1780,14 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
- (void) watchWriteDescriptor
{
if (descriptor < 0)
- return;
-
+ {
+ return;
+ }
if ([writeInfo count] > 0)
{
- NSMutableDictionary* info = [writeInfo objectAtIndex: 0];
- NSRunLoop* l = [NSRunLoop currentRunLoop];
- NSArray* modes = nil;
-
+ NSMutableDictionary *info = [writeInfo objectAtIndex: 0];
+ NSRunLoop *l = [NSRunLoop currentRunLoop];
+ NSArray *modes = nil;
modes = [info objectForKey: NSFileHandleNotificationMonitorModes];
@@ -1382,6 +1821,8 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
{
NSString *operation;
+ NSDebugMLLog(@"NSFileHandle", @"%@ event: %d", self, type);
+
if (isNonBlocking == NO)
{
[self setNonBlocking: YES];
@@ -1433,16 +1874,30 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
char buf[NETBUF_SIZE];
item = [readInfo objectForKey: NSFileHandleNotificationDataItem];
- length = [item length];
+ /*
+ * We may have a maximum data size set...
+ */
+ if (readMax > 0)
+ {
+ length = readMax - [item length];
+ if (length > sizeof(buf))
+ {
+ length = sizeof(buf);
+ }
+ }
+ else
+ {
+ length = sizeof(buf);
+ }
#if USE_ZLIB
if (gzDescriptor != 0)
{
- received = gzread(gzDescriptor, buf, sizeof(buf));
+ received = gzread(gzDescriptor, buf, length);
}
else
#endif
- received = read(descriptor, buf, sizeof(buf));
+ received = read(descriptor, buf, length);
if (received == 0)
{ // Read up to end of file.
[self postReadNotification];
@@ -1462,7 +1917,7 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
else
{
[item appendBytes: buf length: received];
- if (operation == NSFileHandleReadCompletionNotification)
+ if (readMax < 0 || (readMax > 0 && [item length] == readMax))
{
// Read a single chunk of data
[self postReadNotification];
@@ -1476,7 +1931,30 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
info = [writeInfo objectAtIndex: 0];
operation = [info objectForKey: NotificationKey];
- if (operation == GSFileHandleWriteCompletionNotification)
+ if (operation == GSFileHandleConnectCompletionNotification
+ || operation == GSSOCKSConnect)
+ { // Connection attempt completed.
+ int result;
+ int len = sizeof(result);
+
+ if (getsockopt(descriptor, SOL_SOCKET, SO_ERROR,
+ (char*)&result, &len) == 0 && result != 0)
+ {
+ NSString *s;
+
+ s = [NSString stringWithFormat: @"Connect attempt failed - %s",
+ GSLastErrorStr(result)];
+ [info setObject: s forKey: GSFileHandleNotificationError];
+ }
+ else
+ {
+ readOK = YES;
+ writeOK = YES;
+ }
+ connectOK = NO;
+ [self postWriteNotification];
+ }
+ else
{
NSData *item;
int length;
@@ -1520,28 +1998,6 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
[self postWriteNotification];
}
}
- else
- { // Connection attempt completed.
- int result;
- int len = sizeof(result);
-
- if (getsockopt(descriptor, SOL_SOCKET, SO_ERROR,
- (char*)&result, &len) == 0 && result != 0)
- {
- NSString *s;
-
- s = [NSString stringWithFormat: @"Connect attempt failed - %s",
- GSLastErrorStr(result)];
- [info setObject: s forKey: GSFileHandleNotificationError];
- }
- else
- {
- readOK = YES;
- writeOK = YES;
- }
- connectOK = NO;
- [self postWriteNotification];
- }
}
}
@@ -1556,7 +2012,7 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
{
address = [[NSString alloc] initWithCString: (char*)inet_ntoa(sin->sin_addr)];
service = [[NSString alloc] initWithFormat: @"%d",
- (int)GSSwapBigI16ToHost(sin->sin_port)];
+ (int)GSSwapBigI16ToHost(sin->sin_port)];
protocol = @"tcp";
}
@@ -1565,28 +2021,40 @@ getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
int e;
if (descriptor < 0)
- return;
-
- if (isStandardFile)
- return;
-
+ {
+ return;
+ }
+ if (isStandardFile == YES)
+ {
+ return;
+ }
if (isNonBlocking == flag)
- return;
-
+ {
+ return;
+ }
if ((e = fcntl(descriptor, F_GETFL, 0)) >= 0)
{
- if (flag)
- e |= NBLK_OPT;
+ if (flag == YES)
+ {
+ e |= NBLK_OPT;
+ }
else
- e &= ~NBLK_OPT;
-
+ {
+ e &= ~NBLK_OPT;
+ }
if (fcntl(descriptor, F_SETFL, e) < 0)
- NSLog(@"unable to set non-blocking mode - %s", GSLastErrorStr(errno));
+ {
+ NSLog(@"unable to set non-blocking mode - %s", GSLastErrorStr(errno));
+ }
else
- isNonBlocking = flag;
+ {
+ isNonBlocking = flag;
+ }
}
- else
+ else
+ {
NSLog(@"unable to get non-blocking mode - %s", GSLastErrorStr(errno));
+ }
}
- (NSString*) socketAddress