1
/** Implementation for GSFileHandle for GNUStep
2
Copyright (C) 1997-2002 Free Software Foundation, Inc.
4
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
7
This file is part of the GNUstep Base Library.
9
This library is free software; you can redistribute it and/or
10
modify it under the terms of the GNU Library General Public
11
License as published by the Free Software Foundation; either
12
version 2 of the License, or (at your option) any later version.
14
This library is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
Library General Public License for more details.
19
You should have received a copy of the GNU Library General Public
20
License along with this library; if not, write to the Free
21
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
24
#define _FILE_OFFSET_BITS 64
28
#include "GNUstepBase/preface.h"
29
#include "Foundation/NSObject.h"
30
#include "Foundation/NSData.h"
31
#include "Foundation/NSArray.h"
32
#include "Foundation/NSString.h"
33
#include "Foundation/NSFileHandle.h"
34
#include "GNUstepBase/GSFileHandle.h"
35
#include "Foundation/NSException.h"
36
#include "Foundation/NSRunLoop.h"
37
#include "Foundation/NSNotification.h"
38
#include "Foundation/NSNotificationQueue.h"
39
#include "Foundation/NSHost.h"
40
#include "Foundation/NSByteOrder.h"
41
#include "Foundation/NSProcessInfo.h"
42
#include "Foundation/NSUserDefaults.h"
43
#include "Foundation/NSDebug.h"
45
#include "../Tools/gdomap.h"
50
#include <sys/types.h>
63
#define O_BINARY _O_BINARY
70
#define INADDR_NONE -1
73
// Maximum data in single I/O operation
74
#define NETBUF_SIZE 4096
75
#define READ_SIZE NETBUF_SIZE*10
77
static GSFileHandle* fh_stdin = nil;
78
static GSFileHandle* fh_stdout = nil;
79
static GSFileHandle* fh_stderr = nil;
81
// Key to info dictionary for operation mode.
82
static NSString* NotificationKey = @"NSFileHandleNotificationKey";
84
@interface GSFileHandle(private)
85
- (void) receivedEventRead;
86
- (void) receivedEventWrite;
89
@implementation GSFileHandle
92
* Encapsulates low level read operation to get data from the operating
95
- (int) read: (void*)buf length: (int)len
98
if (gzDescriptor != 0)
100
len = gzread(gzDescriptor, buf, len);
106
len = recv((SOCKET)_get_osfhandle(descriptor), buf, len, 0);
110
len = read(descriptor, buf, len);
116
* Encapsulates low level write operation to send data to the operating
119
- (int) write: (const void*)buf length: (int)len
122
if (gzDescriptor != 0)
124
len = gzwrite(gzDescriptor, (char*)buf, len);
130
len = send((SOCKET)_get_osfhandle(descriptor), buf, len, 0);
134
len = write(descriptor, buf, len);
140
getAddr(NSString* name, NSString* svc, NSString* pcl, struct sockaddr_in *sin)
142
const char *proto = "tcp";
147
proto = [pcl lossyCString];
149
memset(sin, '\0', sizeof(*sin));
150
sin->sin_family = AF_INET;
153
* If we were given a hostname, we use any address for that host.
154
* Otherwise we expect the given name to be an address unless it is
155
* a null (any address).
159
NSHost* host = [NSHost hostWithName: name];
163
name = [host address];
165
#ifndef HAVE_INET_ATON
166
sin->sin_addr.s_addr = inet_addr([name lossyCString]);
167
if (sin->sin_addr.s_addr == INADDR_NONE)
169
if (inet_aton([name lossyCString], &sin->sin_addr) == 0)
177
sin->sin_addr.s_addr = GSSwapHostI32ToBig(INADDR_ANY);
184
else if ((sp = getservbyname([svc lossyCString], proto)) == 0)
186
const char* ptr = [svc lossyCString];
189
while (isdigit(*ptr))
193
if (*ptr == '\0' && val <= 0xffff)
197
sin->sin_port = GSSwapHostI16ToBig(v);
200
else if (strcmp(ptr, "gdomap") == 0)
203
#ifdef GDOMAP_PORT_OVERRIDE
204
v = GDOMAP_PORT_OVERRIDE;
206
v = 538; // IANA allocated port
208
sin->sin_port = GSSwapHostI16ToBig(v);
218
sin->sin_port = sp->s_port;
223
+ (id) allocWithZone: (NSZone*)z
225
return NSAllocateObject ([self class], 0, z);
243
if (self == fh_stdin)
245
if (self == fh_stdout)
247
if (self == fh_stderr)
250
[self ignoreReadDescriptor];
251
[self ignoreWriteDescriptor];
255
* The gzDescriptor should always be closed when we have done with it.
257
if (gzDescriptor != 0)
259
gzclose(gzDescriptor);
262
if (descriptor != -1)
264
[self setNonBlocking: wasNonBlocking];
265
if (closeOnDealloc == YES)
269
closesocket((SOCKET)_get_osfhandle(descriptor));
270
WSACloseEvent(event);
271
event = WSA_INVALID_EVENT;
279
// Initializing a GSFileHandle Object
283
return [self initWithNullDevice];
287
* Initialise as a client socket connection ... do this by using
288
* [-initAsClientInBackgroundAtAddress:service:protocol:forModes:]
289
* and running the current run loop in NSDefaultRunLoopMode until
290
* the connection attempt succeeds, fails, or times out.
292
- (id) initAsClientAtAddress: (NSString*)a
293
service: (NSString*)s
294
protocol: (NSString*)p
296
self = [self initAsClientInBackgroundAtAddress: a
305
loop = [NSRunLoop currentRunLoop];
306
limit = [NSDate dateWithTimeIntervalSinceNow: 300];
307
while ([limit timeIntervalSinceNow] > 0
308
&& (readInfo != nil || [writeInfo count] > 0))
310
[loop runMode: NSDefaultRunLoopMode
313
if (readInfo != nil || [writeInfo count] > 0 || readOK == NO)
315
/* Must have timed out or failed */
320
[self setNonBlocking: NO];
327
* States for socks connection negotiation
329
NSString * const GSSOCKSConnect = @"GSSOCKSConnect";
330
NSString * const GSSOCKSSendAuth = @"GSSOCKSSendAuth";
331
NSString * const GSSOCKSRecvAuth = @"GSSOCKSRecvAuth";
332
NSString * const GSSOCKSSendConn = @"GSSOCKSSendConn";
333
NSString * const GSSOCKSRecvConn = @"GSSOCKSRecvConn";
334
NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
336
- (void) _socksHandler: (NSNotification*)aNotification
338
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
339
NSString *name = [aNotification name];
340
NSDictionary *info = (NSMutableDictionary*)[aNotification userInfo];
343
NSMutableDictionary *i = nil;
344
NSNotification *n = nil;
346
NSDebugMLLog(@"NSFileHandle", @"%@ SOCKS connection: %@",
347
self, aNotification);
349
[nc removeObserver: self name: name object: self];
351
modes = (NSArray*)[info objectForKey: NSFileHandleNotificationMonitorModes];
352
error = [info objectForKey: GSFileHandleNotificationError];
356
if (name == GSSOCKSConnect)
361
* Send an authorisation record to the SOCKS server.
363
i = [info mutableCopy];
365
* Authorisation record is at least three bytes -
367
* authorisation method bytes to follow (1)
368
* say we do no authorisation (0)
370
item = [[NSData alloc] initWithBytes: "\5\1\0"
372
[i setObject: item forKey: NSFileHandleNotificationDataItem];
374
[i setObject: GSSOCKSSendAuth forKey: NotificationKey];
375
[writeInfo addObject: i];
377
[nc addObserver: self
378
selector: @selector(_socksHandler:)
379
name: GSSOCKSSendAuth
381
[self watchWriteDescriptor];
383
else if (name == GSSOCKSSendAuth)
388
* We have written the authorisation record, so we
389
* request a response from the SOCKS server.
392
readInfo = [info mutableCopy];
393
[readInfo setObject: GSSOCKSRecvAuth forKey: NotificationKey];
394
item = [[NSMutableData alloc] initWithCapacity: 0];
395
[readInfo setObject: item forKey: NSFileHandleNotificationDataItem];
397
[nc addObserver: self
398
selector: @selector(_socksHandler:)
399
name: GSSOCKSRecvAuth
401
[self watchReadDescriptorForModes: modes];
403
else if (name == GSSOCKSRecvAuth)
406
const unsigned char *bytes;
408
response = [info objectForKey: NSFileHandleNotificationDataItem];
409
bytes = (const unsigned char*)[response bytes];
410
if ([response length] != 2)
412
error = @"authorisation response from SOCKS was not two bytes";
414
else if (bytes[0] != 5)
416
error = @"authorisation response from SOCKS had wrong version";
418
else if (bytes[1] != 0)
420
error = @"authorisation response from SOCKS had wrong method";
430
* Send the address information to the SOCKS server.
432
i = [info mutableCopy];
434
* Connect command is ten bytes -
439
* address 4 bytes (big endian)
440
* port 2 bytes (big endian)
442
buf[0] = 5; // Socks version number
443
buf[1] = 1; // Connect command
444
buf[2] = 0; // Reserved
445
buf[3] = 1; // Address type (IPV4)
446
ptr = [address lossyCString];
448
while (isdigit(*ptr))
452
while (isdigit(*ptr))
456
while (isdigit(*ptr))
460
p = [service intValue];
461
buf[8] = ((p & 0xff00) >> 8);
464
item = [[NSData alloc] initWithBytes: buf length: 10];
465
[i setObject: item forKey: NSFileHandleNotificationDataItem];
467
[i setObject: GSSOCKSSendConn
468
forKey: NotificationKey];
469
[writeInfo addObject: i];
471
[nc addObserver: self
472
selector: @selector(_socksHandler:)
473
name: GSSOCKSSendConn
475
[self watchWriteDescriptor];
478
else if (name == GSSOCKSSendConn)
483
* We have written the connect command, so we
484
* request a response from the SOCKS server.
487
readInfo = [info mutableCopy];
488
[readInfo setObject: GSSOCKSRecvConn forKey: NotificationKey];
489
item = [[NSMutableData alloc] initWithCapacity: 0];
490
[readInfo setObject: item forKey: NSFileHandleNotificationDataItem];
492
[nc addObserver: self
493
selector: @selector(_socksHandler:)
494
name: GSSOCKSRecvConn
496
[self watchReadDescriptorForModes: modes];
498
else if (name == GSSOCKSRecvConn)
501
const unsigned char *bytes;
504
response = [info objectForKey: NSFileHandleNotificationDataItem];
505
bytes = (const unsigned char*)[response bytes];
506
if ([response length] != 4)
508
error = @"connect response from SOCKS had bad length";
510
else if (bytes[0] != 5)
512
error = @"connect response from SOCKS had wrong version";
514
else if (bytes[1] != 0)
519
error = @"SOCKS server general failure";
522
error = @"SOCKS server says permission denied";
525
error = @"SOCKS server says network unreachable";
528
error = @"SOCKS server says host unreachable";
531
error = @"SOCKS server says connection refused";
534
error = @"SOCKS server says connection timed out";
537
error = @"SOCKS server says command not supported";
540
error = @"SOCKS server says address type not supported";
543
error = @"connect response from SOCKS was failure";
547
else if (bytes[3] == 1)
549
len = 4; // Fixed size (IPV4) address
551
else if (bytes[3] == 3)
553
len = 1 + bytes[4]; // Domain name with leading length
555
else if (bytes[3] == 4)
557
len = 16; // Fixed size (IPV6) address
561
error = @"SOCKS server returned unknown address type";
569
* We have received a success, so we must now consume the
570
* address and port information the SOCKS server sends.
573
readInfo = [info mutableCopy];
574
[readInfo setObject: GSSOCKSRecvAddr forKey: NotificationKey];
575
item = [[NSMutableData alloc] initWithCapacity: 0];
576
[readInfo setObject: item
577
forKey: NSFileHandleNotificationDataItem];
579
[nc addObserver: self
580
selector: @selector(_socksHandler:)
581
name: GSSOCKSRecvAddr
583
[self watchReadDescriptorForModes: modes];
586
else if (name == GSSOCKSRecvAddr)
589
* Success ... We read the address from the socks server so
590
* the connection is now ready to go.
592
name = GSFileHandleConnectCompletionNotification;
593
i = [info mutableCopy];
594
[i setObject: name forKey: NotificationKey];
595
n = [NSNotification notificationWithName: name
603
* Argh ... unexpected notification.
605
error = @"unexpected notification during SOCKS connection";
610
* If 'error' is non-null, we set up a notification to tell people
611
* the connection failed.
615
NSDebugMLLog(@"NSFileHandle", @"%@ SOCKS error: %@", self, error);
618
* An error in the initial connection ... notify observers
619
* by re-posting the notification with a new name.
621
name = GSFileHandleConnectCompletionNotification;
622
i = [info mutableCopy];
623
[i setObject: name forKey: NotificationKey];
624
[i setObject: error forKey: GSFileHandleNotificationError];
625
n = [NSNotification notificationWithName: name
632
* If a notification has been set up, we post it as the last thing we do.
636
NSNotificationQueue *q;
638
q = [NSNotificationQueue defaultQueue];
639
[q enqueueNotification: n
640
postingStyle: NSPostASAP
641
coalesceMask: NSNotificationNoCoalescing
646
- (id) initAsClientInBackgroundAtAddress: (NSString*)a
647
service: (NSString*)s
648
protocol: (NSString*)p
649
forModes: (NSArray*)modes
651
static NSString *esocks = nil;
652
static NSString *dsocks = nil;
653
static BOOL beenHere = NO;
655
struct sockaddr_in sin;
656
struct sockaddr_in lsin;
657
NSString *lhost = nil;
658
NSString *shost = nil;
659
NSString *sport = nil;
664
NSUserDefaults *defs;
667
defs = [NSUserDefaults standardUserDefaults];
668
dsocks = [[defs stringForKey: @"GSSOCKS"] copy];
673
env = [[NSProcessInfo processInfo] environment];
674
esocks = [env objectForKey: @"SOCKS5_SERVER"];
677
esocks = [env objectForKey: @"SOCKS_SERVER"];
679
esocks = [esocks copy];
683
if (a == nil || [a isEqualToString: @""])
689
NSLog(@"bad argument - service is nil");
694
if ([p hasPrefix: @"bind-"] == YES)
698
lhost = [p substringFromIndex: 5];
699
r = [lhost rangeOfString: @":"];
702
p = [lhost substringFromIndex: NSMaxRange(r)];
703
lhost = [lhost substringToIndex: r.location];
709
if (getAddr(lhost, p, @"tcp", &lsin) == NO)
711
NSLog(@"bad bind address specification");
719
* A protocol fo the form 'socks-...' controls socks operation,
720
* overriding defaults and environment variables.<br />
721
* If it is just 'socks-' it turns off socks for this fiel handle.<br />
722
* Otherwise, the following text must be the name of the socks server
723
* (optionally followed by :port).
725
if ([p hasPrefix: @"socks-"] == YES)
727
shost = [p substringFromIndex: 6];
730
else if (dsocks != nil)
732
shost = dsocks; // GSSOCKS user default
736
shost = esocks; // SOCKS_SERVER environment variable.
739
if (shost != nil && [shost length] > 0)
743
r = [shost rangeOfString: @":"];
746
sport = [shost substringFromIndex: NSMaxRange(r)];
747
shost = [shost substringToIndex: r.location];
756
if (getAddr(a, s, p, &sin) == NO)
759
NSLog(@"bad address-service-protocol combination");
762
[self setAddr: &sin]; // Store the address of the remote end.
765
* Don't use SOCKS if we are contacting the local host.
769
NSHost *remote = [NSHost hostWithAddress: [self socketAddress]];
770
NSHost *local = [NSHost currentHost];
772
if ([remote isEqual: local] || [remote isEqual: [NSHost localHost]])
779
if (getAddr(shost, sport, p, &sin) == NO)
781
NSLog(@"bad SOCKS host-port combination");
787
if ((net = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) == INVALID_SOCKET)
789
NSLog(@"unable to create socket - %s", GSLastErrorStr(errno));
794
* Enable tcp-level tracking of whether connection is alive.
797
setsockopt(net, SOL_SOCKET, SO_KEEPALIVE, (char *)&status, sizeof(status));
801
if (bind(net, (struct sockaddr *)&lsin, sizeof(lsin)) == SOCKET_ERROR)
803
NSLog(@"unable to bind to port %s:%d - %s", inet_ntoa(lsin.sin_addr),
804
GSSwapBigI16ToHost(sin.sin_port), GSLastErrorStr(errno));
805
(void) closesocket(net);
811
self = [self initWithNativeHandle: (void*)net closeOnDealloc: YES];
814
NSMutableDictionary* info;
817
[self setNonBlocking: YES];
818
if (connect(net, (struct sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
820
if (WSAGetLastError() != WSAEWOULDBLOCK)
822
NSLog(@"unable to make connection to %s:%d - %s",
823
inet_ntoa(sin.sin_addr),
824
GSSwapBigI16ToHost(sin.sin_port), GSLastErrorStr(errno));
830
info = [[NSMutableDictionary alloc] initWithCapacity: 4];
831
[info setObject: address forKey: NSFileHandleNotificationDataItem];
834
[info setObject: GSFileHandleConnectCompletionNotification
835
forKey: NotificationKey];
839
NSNotificationCenter *nc;
842
* If we are making a socks connection, register self as an
843
* observer of notifications and ensure we will manage this.
845
nc = [NSNotificationCenter defaultCenter];
846
[nc addObserver: self
847
selector: @selector(_socksHandler:)
850
[info setObject: GSSOCKSConnect
851
forKey: NotificationKey];
855
[info setObject: modes forKey: NSFileHandleNotificationMonitorModes];
857
[writeInfo addObject: info];
859
[self watchWriteDescriptor];
868
- (id) initAsServerAtAddress: (NSString*)a
869
service: (NSString*)s
870
protocol: (NSString*)p
872
#ifndef BROKEN_SO_REUSEADDR
876
struct sockaddr_in sin;
877
unsigned int size = sizeof(sin);
879
if (getAddr(a, s, p, &sin) == NO)
882
NSLog(@"bad address-service-protocol combination");
886
if ((net = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) == INVALID_SOCKET)
888
NSLog(@"unable to create socket - %s", GSLastErrorStr(errno));
893
#ifndef BROKEN_SO_REUSEADDR
895
* Under decent systems, SO_REUSEADDR means that the port can be reused
896
* immediately that this process exits. Under some it means
897
* that multiple processes can serve the same port simultaneously.
898
* We don't want that broken behavior!
900
setsockopt(net, SOL_SOCKET, SO_REUSEADDR, (char *)&status, sizeof(status));
903
if (bind(net, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
905
NSLog(@"unable to bind to port %s:%d - %s", inet_ntoa(sin.sin_addr),
906
GSSwapBigI16ToHost(sin.sin_port), GSLastErrorStr(errno));
907
(void) closesocket(net);
912
if (listen(net, 256) == SOCKET_ERROR)
914
NSLog(@"unable to listen on port - %s", GSLastErrorStr(errno));
915
(void) closesocket(net);
920
if (getsockname(net, (struct sockaddr*)&sin, &size) == SOCKET_ERROR)
922
NSLog(@"unable to get socket name - %s", GSLastErrorStr(errno));
923
(void) closesocket(net);
928
self = [self initWithNativeHandle: (void*)net closeOnDealloc: YES];
936
[self setAddr: &sin];
941
- (id) initForReadingAtPath: (NSString*)path
944
(unichar*)[path cStringUsingEncoding: NSUnicodeStringEncoding],
954
self = [self initWithFileDescriptor: d closeOnDealloc: YES];
965
- (id) initForWritingAtPath: (NSString*)path
968
(unichar*)[path cStringUsingEncoding: NSUnicodeStringEncoding],
978
self = [self initWithFileDescriptor: d closeOnDealloc: YES];
989
- (id) initForUpdatingAtPath: (NSString*)path
992
(unichar*)[path cStringUsingEncoding: NSUnicodeStringEncoding],
1002
self = [self initWithFileDescriptor: d closeOnDealloc: YES];
1012
- (id) initWithStandardError
1014
if (fh_stderr != nil)
1021
self = [self initWithFileDescriptor: 2 closeOnDealloc: NO];
1032
- (id) initWithStandardInput
1034
if (fh_stdin != nil)
1041
self = [self initWithFileDescriptor: 0 closeOnDealloc: NO];
1052
- (id) initWithStandardOutput
1054
if (fh_stdout != nil)
1061
self = [self initWithFileDescriptor: 1 closeOnDealloc: NO];
1072
- (id) initWithNullDevice
1075
isStandardFile = YES;
1080
- (id) initWithFileDescriptor: (int)desc closeOnDealloc: (BOOL)flag
1082
self = [super init];
1087
if (_fstat(desc, &sbuf) != 0)
1089
NSLog(@"unable to get status of descriptor %d - %s",
1090
desc, GSLastErrorStr(errno));
1094
if (S_ISREG(sbuf.st_mode))
1096
isStandardFile = YES;
1100
isStandardFile = NO;
1104
if (isStandardFile == NO)
1106
unsigned long nbio = 0;
1110
* This is probably a socket ... try
1111
* using a socket specific call and see if that fails.
1113
if (ioctlsocket((SOCKET)_get_osfhandle(desc), FIONBIO, &nbio) == 0)
1115
wasNonBlocking = (nbio == 0) ? NO : YES;
1119
isSocket = NO; // maybe special file desc. like std in/out/err?
1123
isNonBlocking = wasNonBlocking;
1125
closeOnDealloc = flag;
1127
writeInfo = [NSMutableArray new];
1136
event = CreateEvent(NULL, NO, NO, NULL);
1137
if (event == WSA_INVALID_EVENT)
1139
NSLog(@"Invalid Event - '%d'", WSAGetLastError());
1142
WSAEventSelect(_get_osfhandle(descriptor), event, FD_ALL_EVENTS);
1146
event = WSA_INVALID_EVENT;
1152
- (id) initWithNativeHandle: (void*)hdl
1154
return [self initWithFileDescriptor: _open_osfhandle((SOCKET)hdl, 0)
1155
closeOnDealloc: NO];
1158
- (id) initWithNativeHandle: (void*)hdl closeOnDealloc: (BOOL)flag
1160
return [self initWithFileDescriptor: _open_osfhandle((SOCKET)hdl, 0)
1161
closeOnDealloc: flag];
1164
- (void) checkAccept
1168
[NSException raise: NSFileHandleOperationException
1169
format: @"accept not permitted in this file handle"];
1173
id operation = [readInfo objectForKey: NotificationKey];
1175
if (operation == NSFileHandleConnectionAcceptedNotification)
1177
[NSException raise: NSFileHandleOperationException
1178
format: @"accept already in progress"];
1182
[NSException raise: NSFileHandleOperationException
1183
format: @"read already in progress"];
1188
- (void) checkConnect
1190
if (connectOK == NO)
1192
[NSException raise: NSFileHandleOperationException
1193
format: @"connect not permitted in this file handle"];
1195
if ([writeInfo count] > 0)
1197
NSDictionary *info = [writeInfo objectAtIndex: 0];
1198
id operation = [info objectForKey: NotificationKey];
1200
if (operation == GSFileHandleConnectCompletionNotification)
1202
[NSException raise: NSFileHandleOperationException
1203
format: @"connect already in progress"];
1207
[NSException raise: NSFileHandleOperationException
1208
format: @"write already in progress"];
1217
[NSException raise: NSFileHandleOperationException
1218
format: @"read not permitted on this file handle"];
1222
id operation = [readInfo objectForKey: NotificationKey];
1224
if (operation == NSFileHandleConnectionAcceptedNotification)
1226
[NSException raise: NSFileHandleOperationException
1227
format: @"accept already in progress"];
1231
[NSException raise: NSFileHandleOperationException
1232
format: @"read already in progress"];
1241
[NSException raise: NSFileHandleOperationException
1242
format: @"write not permitted in this file handle"];
1244
if ([writeInfo count] > 0)
1246
NSDictionary *info = [writeInfo objectAtIndex: 0];
1247
id operation = [info objectForKey: NotificationKey];
1249
if (operation != GSFileHandleWriteCompletionNotification)
1251
[NSException raise: NSFileHandleOperationException
1252
format: @"connect in progress"];
1257
// Returning file handles
1259
- (int) fileDescriptor
1264
- (void*) nativeHandle
1266
return (void*)(SOCKET)_get_osfhandle(descriptor);
1269
// Synchronous I/O operations
1271
- (NSData*) availableData
1273
char buf[READ_SIZE];
1278
if (isNonBlocking == YES)
1280
[self setNonBlocking: NO];
1282
d = [NSMutableData dataWithCapacity: 0];
1285
while ((len = [self read: buf length: sizeof(buf)]) > 0)
1287
[d appendBytes: buf length: len];
1292
len = [self read: buf length: sizeof(buf)];
1295
[d appendBytes: buf length: len];
1300
[NSException raise: NSFileHandleOperationException
1301
format: @"unable to read from descriptor - %s",
1302
GSLastErrorStr(errno)];
1307
- (NSData*) readDataToEndOfFile
1309
char buf[READ_SIZE];
1314
if (isNonBlocking == YES)
1316
[self setNonBlocking: NO];
1318
d = [NSMutableData dataWithCapacity: 0];
1319
while ((len = [self read: buf length: sizeof(buf)]) > 0)
1321
[d appendBytes: buf length: len];
1325
[NSException raise: NSFileHandleOperationException
1326
format: @"unable to read from descriptor - %s",
1327
GSLastErrorStr(errno)];
1332
- (NSData*) readDataOfLength: (unsigned)len
1338
if (isNonBlocking == YES)
1340
[self setNonBlocking: NO];
1346
buf = NSZoneMalloc(NSDefaultMallocZone(), len);
1347
d = [NSMutableData dataWithBytesNoCopy: buf length: len];
1348
got = [self read: [d mutableBytes] length: len];
1351
[NSException raise: NSFileHandleOperationException
1352
format: @"unable to read from descriptor - %s",
1353
GSLastErrorStr(errno)];
1359
char buf[READ_SIZE];
1361
d = [NSMutableData dataWithCapacity: 0];
1364
int chunk = len > sizeof(buf) ? sizeof(buf) : len;
1366
got = [self read: buf length: chunk];
1369
[d appendBytes: buf length: got];
1374
[NSException raise: NSFileHandleOperationException
1375
format: @"unable to read from descriptor - %s",
1376
GSLastErrorStr(errno)];
1379
while (len > 0 && got > 0);
1384
- (void) writeData: (NSData*)item
1387
const void* ptr = [item bytes];
1388
unsigned int len = [item length];
1389
unsigned int pos = 0;
1392
if (isNonBlocking == YES)
1394
[self setNonBlocking: NO];
1398
int toWrite = len - pos;
1400
if (toWrite > NETBUF_SIZE)
1402
toWrite = NETBUF_SIZE;
1404
rval = [self write: (char*)ptr+pos length: toWrite];
1407
if (WSAGetLastError()== WSAEINTR
1408
|| WSAGetLastError()== WSAEWOULDBLOCK)
1421
[NSException raise: NSFileHandleOperationException
1422
format: @"unable to write to descriptor - %s",
1423
GSLastErrorStr(errno)];
1428
// Asynchronous I/O operations
1430
- (void) acceptConnectionInBackgroundAndNotifyForModes: (NSArray*)modes
1435
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
1436
[readInfo setObject: NSFileHandleConnectionAcceptedNotification
1437
forKey: NotificationKey];
1438
[self watchReadDescriptorForModes: modes];
1441
- (void) readDataInBackgroundAndNotifyLength: (unsigned)len
1442
forModes: (NSArray*)modes
1447
if (len > 0x7fffffff)
1449
[NSException raise: NSInvalidArgumentException
1450
format: @"length (%u) too large", len];
1454
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
1455
[readInfo setObject: NSFileHandleReadCompletionNotification
1456
forKey: NotificationKey];
1457
d = [[NSMutableData alloc] initWithCapacity: readMax];
1458
[readInfo setObject: d forKey: NSFileHandleNotificationDataItem];
1460
[self watchReadDescriptorForModes: modes];
1463
- (void) readInBackgroundAndNotifyForModes: (NSArray*)modes
1468
readMax = -1; // Accept any quantity of data.
1470
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
1471
[readInfo setObject: NSFileHandleReadCompletionNotification
1472
forKey: NotificationKey];
1473
d = [[NSMutableData alloc] initWithCapacity: 0];
1474
[readInfo setObject: d forKey: NSFileHandleNotificationDataItem];
1476
[self watchReadDescriptorForModes: modes];
1479
- (void) readToEndOfFileInBackgroundAndNotifyForModes: (NSArray*)modes
1486
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
1487
[readInfo setObject: NSFileHandleReadToEndOfFileCompletionNotification
1488
forKey: NotificationKey];
1489
d = [[NSMutableData alloc] initWithCapacity: 0];
1490
[readInfo setObject: d forKey: NSFileHandleNotificationDataItem];
1492
[self watchReadDescriptorForModes: modes];
1495
- (void) waitForDataInBackgroundAndNotifyForModes: (NSArray*)modes
1500
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
1501
[readInfo setObject: NSFileHandleDataAvailableNotification
1502
forKey: NotificationKey];
1503
[readInfo setObject: [NSMutableData dataWithCapacity: 0]
1504
forKey: NSFileHandleNotificationDataItem];
1505
[self watchReadDescriptorForModes: modes];
1508
// Seeking within a file
1510
- (unsigned long long) offsetInFile
1514
if (isStandardFile && descriptor >= 0)
1517
if (gzDescriptor != 0)
1519
result = gzseek(gzDescriptor, 0, SEEK_CUR);
1523
result = _lseek(descriptor, 0, SEEK_CUR);
1527
[NSException raise: NSFileHandleOperationException
1528
format: @"failed to move to offset in file - %s",
1529
GSLastErrorStr(errno)];
1531
return (unsigned long long)result;
1534
- (unsigned long long) seekToEndOfFile
1538
if (isStandardFile && descriptor >= 0)
1541
if (gzDescriptor != 0)
1543
result = gzseek(gzDescriptor, 0, SEEK_END);
1547
result = _lseek(descriptor, 0, SEEK_END);
1551
[NSException raise: NSFileHandleOperationException
1552
format: @"failed to move to offset in file - %s",
1553
GSLastErrorStr(errno)];
1555
return (unsigned long long)result;
1558
- (void) seekToFileOffset: (unsigned long long)pos
1562
if (isStandardFile && descriptor >= 0)
1565
if (gzDescriptor != 0)
1567
result = gzseek(gzDescriptor, (off_t)pos, SEEK_SET);
1571
result = _lseek(descriptor, (off_t)pos, SEEK_SET);
1575
[NSException raise: NSFileHandleOperationException
1576
format: @"failed to move to offset in file - %s",
1577
GSLastErrorStr(errno)];
1582
// Operations on file
1588
[NSException raise: NSFileHandleOperationException
1589
format: @"attempt to close closed file"];
1591
[self ignoreReadDescriptor];
1592
[self ignoreWriteDescriptor];
1594
[self setNonBlocking: wasNonBlocking];
1596
if (gzDescriptor != 0)
1598
gzclose(gzDescriptor);
1604
(void)closesocket((SOCKET)_get_osfhandle(descriptor));
1605
WSACloseEvent(event);
1606
event = WSA_INVALID_EVENT;
1608
(void)close(descriptor);
1616
* Clear any pending operations on the file handle, sending
1617
* notifications if necessary.
1621
[readInfo setObject: @"File handle closed locally"
1622
forKey: GSFileHandleNotificationError];
1623
[self postReadNotification];
1626
if ([writeInfo count])
1628
NSMutableDictionary *info = [writeInfo objectAtIndex: 0];
1630
[info setObject: @"File handle closed locally"
1631
forKey: GSFileHandleNotificationError];
1632
[self postWriteNotification];
1633
[writeInfo removeAllObjects];
1637
- (void) synchronizeFile
1641
(void)_commit(descriptor);
1645
- (void) truncateFileAtOffset: (unsigned long long)pos
1647
_chsize(descriptor, pos);
1648
[self seekToFileOffset: pos];
1651
- (void) writeInBackgroundAndNotify: (NSData*)item forModes: (NSArray*)modes
1653
NSMutableDictionary* info;
1657
info = [[NSMutableDictionary alloc] initWithCapacity: 4];
1658
[info setObject: item forKey: NSFileHandleNotificationDataItem];
1659
[info setObject: GSFileHandleWriteCompletionNotification
1660
forKey: NotificationKey];
1663
[info setObject: modes forKey: NSFileHandleNotificationMonitorModes];
1665
[writeInfo addObject: info];
1667
[self watchWriteDescriptor];
1670
- (void) writeInBackgroundAndNotify: (NSData*)item;
1672
[self writeInBackgroundAndNotify: item forModes: nil];
1675
- (void) postReadNotification
1677
NSMutableDictionary *info = readInfo;
1679
NSNotificationQueue *q;
1683
[self ignoreReadDescriptor];
1686
modes = (NSArray*)[info objectForKey: NSFileHandleNotificationMonitorModes];
1687
name = (NSString*)[info objectForKey: NotificationKey];
1693
n = [NSNotification notificationWithName: name object: self userInfo: info];
1695
RELEASE(info); /* Retained by the notification. */
1697
q = [NSNotificationQueue defaultQueue];
1698
[q enqueueNotification: n
1699
postingStyle: NSPostASAP
1700
coalesceMask: NSNotificationNoCoalescing
1704
- (void) postWriteNotification
1706
NSMutableDictionary *info = [writeInfo objectAtIndex: 0];
1707
NSNotificationQueue *q;
1712
[self ignoreWriteDescriptor];
1713
modes = (NSArray*)[info objectForKey: NSFileHandleNotificationMonitorModes];
1714
name = (NSString*)[info objectForKey: NotificationKey];
1716
n = [NSNotification notificationWithName: name object: self userInfo: info];
1719
[writeInfo removeObjectAtIndex: 0]; /* Retained by notification. */
1721
q = [NSNotificationQueue defaultQueue];
1722
[q enqueueNotification: n
1723
postingStyle: NSPostASAP
1724
coalesceMask: NSNotificationNoCoalescing
1726
if ((writeOK || connectOK) && [writeInfo count] > 0)
1728
[self watchWriteDescriptor]; /* In case of queued writes. */
1732
- (BOOL) readInProgress
1741
- (BOOL) writeInProgress
1743
if ([writeInfo count] > 0)
1750
- (void) ignoreReadDescriptor
1759
l = [NSRunLoop currentRunLoop];
1764
modes = (NSArray*)[readInfo objectForKey:
1765
NSFileHandleNotificationMonitorModes];
1768
if (modes && [modes count])
1772
for (i = 0; i < [modes count]; i++)
1774
[l removeEvent: (void*)(gsaddr)event
1776
forMode: [modes objectAtIndex: i]
1782
[l removeEvent: (void*)(gsaddr)event
1784
forMode: NSDefaultRunLoopMode
1789
- (void) ignoreWriteDescriptor
1798
l = [NSRunLoop currentRunLoop];
1801
if ([writeInfo count] > 0)
1803
NSMutableDictionary* info = [writeInfo objectAtIndex: 0];
1805
modes=(NSArray*)[info objectForKey: NSFileHandleNotificationMonitorModes];
1808
if (modes && [modes count])
1812
for (i = 0; i < [modes count]; i++)
1814
[l removeEvent: (void*)(gsaddr)event
1816
forMode: [modes objectAtIndex: i]
1822
[l removeEvent: (void*)(gsaddr)event
1824
forMode: NSDefaultRunLoopMode
1829
- (void) watchReadDescriptorForModes: (NSArray*)modes;
1838
l = [NSRunLoop currentRunLoop];
1839
[self setNonBlocking: YES];
1840
if (modes && [modes count])
1844
for (i = 0; i < [modes count]; i++)
1846
[l addEvent: (void*)(gsaddr)event
1849
forMode: [modes objectAtIndex: i]];
1851
[readInfo setObject: modes forKey: NSFileHandleNotificationMonitorModes];
1855
[l addEvent: (void*)(gsaddr)event
1858
forMode: NSDefaultRunLoopMode];
1862
- (void) watchWriteDescriptor
1868
if ([writeInfo count] > 0)
1870
NSMutableDictionary *info = [writeInfo objectAtIndex: 0];
1871
NSRunLoop *l = [NSRunLoop currentRunLoop];
1872
NSArray *modes = nil;
1874
modes = [info objectForKey: NSFileHandleNotificationMonitorModes];
1876
[self setNonBlocking: YES];
1877
if (modes && [modes count])
1881
for (i = 0; i < [modes count]; i++)
1883
[l addEvent: (void*)(gsaddr)event
1886
forMode: [modes objectAtIndex: i]];
1891
[l addEvent: (void*)(gsaddr)event
1894
forMode: NSDefaultRunLoopMode];
1899
- (void) receivedEventRead
1901
NSString *operation;
1903
operation = [readInfo objectForKey: NotificationKey];
1904
if (operation == NSFileHandleConnectionAcceptedNotification)
1906
struct sockaddr_in buf;
1908
unsigned int blen = sizeof(buf);
1910
desc = accept((SOCKET)_get_osfhandle(descriptor),
1911
(struct sockaddr*)&buf, &blen);
1913
if (desc == INVALID_SOCKET)
1917
s = [NSString stringWithFormat: @"Accept attempt failed - %s",
1918
GSLastErrorStr(errno)];
1919
[readInfo setObject: s forKey: GSFileHandleNotificationError];
1922
{ // Accept attempt completed.
1924
struct sockaddr_in sin;
1925
unsigned int size = sizeof(sin);
1929
* Enable tcp-level tracking of whether connection is alive.
1932
setsockopt(desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&status,
1935
h = [[[self class] alloc] initWithNativeHandle: (void*)desc
1936
closeOnDealloc: YES];
1938
getpeername(desc, (struct sockaddr*)&sin, &size);
1940
[readInfo setObject: h
1941
forKey: NSFileHandleNotificationFileHandleItem];
1944
[self postReadNotification];
1946
else if (operation == NSFileHandleDataAvailableNotification)
1948
[self postReadNotification];
1952
NSMutableData *item;
1955
char buf[READ_SIZE];
1957
item = [readInfo objectForKey: NSFileHandleNotificationDataItem];
1959
* We may have a maximum data size set...
1963
length = (unsigned int)readMax - [item length];
1964
if (length > (int)sizeof(buf))
1966
length = sizeof(buf);
1971
length = sizeof(buf);
1974
received = [self read: buf length: length];
1976
{ // Read up to end of file.
1977
[self postReadNotification];
1979
else if (received < 0)
1981
if (WSAGetLastError() != WSAEINTR
1982
&& WSAGetLastError() != WSAEWOULDBLOCK)
1986
s = [NSString stringWithFormat: @"Read attempt failed - %s",
1987
GSLastErrorStr(errno)];
1988
[readInfo setObject: s forKey: GSFileHandleNotificationError];
1989
[self postReadNotification];
1994
[item appendBytes: buf length: received];
1995
if (readMax < 0 || (readMax > 0 && (int)[item length] == readMax))
1997
// Read a single chunk of data
1998
[self postReadNotification];
2004
- (void) receivedEventWrite
2006
NSString *operation;
2007
NSMutableDictionary *info;
2009
info = [writeInfo objectAtIndex: 0];
2010
operation = [info objectForKey: NotificationKey];
2011
if (operation == GSFileHandleConnectCompletionNotification
2012
|| operation == GSSOCKSConnect)
2013
{ // Connection attempt completed.
2015
unsigned len = sizeof(result);
2017
if (getsockopt((SOCKET)_get_osfhandle(descriptor), SOL_SOCKET, SO_ERROR,
2018
(char*)&result, &len) == 0 && result != 0)
2022
s = [NSString stringWithFormat: @"Connect attempt failed - %s",
2023
GSLastErrorStr(result)];
2024
[info setObject: s forKey: GSFileHandleNotificationError];
2032
[self postWriteNotification];
2040
item = [info objectForKey: NSFileHandleNotificationDataItem];
2041
length = [item length];
2043
if (writePos < length)
2047
written = [self write: (char*)ptr+writePos
2048
length: length-writePos];
2051
if (written < 0 && WSAGetLastError()!= WSAEINTR
2052
&& WSAGetLastError()!= WSAEWOULDBLOCK)
2056
s = [NSString stringWithFormat:
2057
@"Write attempt failed - %s", GSLastErrorStr(errno)];
2058
[info setObject: s forKey: GSFileHandleNotificationError];
2059
[self postWriteNotification];
2064
writePos += written;
2067
if (writePos >= length)
2068
{ // Write operation completed.
2069
[self postWriteNotification];
2074
- (void) receivedEvent: (void*)data
2075
type: (RunLoopEventType)type
2077
forMode: (NSString*)mode
2079
WSANETWORKEVENTS ocurredEvents;
2081
NSDebugMLLog(@"NSFileHandle", @"%@ event: %d", self, type);
2083
if (isNonBlocking == NO)
2085
[self setNonBlocking: YES];
2087
if (WSAEnumNetworkEvents((SOCKET)_get_osfhandle(descriptor),
2088
event, &ocurredEvents) == SOCKET_ERROR)
2090
NSLog(@"Error getting event type %d", WSAGetLastError());
2093
if (ocurredEvents.lNetworkEvents & FD_CONNECT)
2095
NSDebugMLLog(@"NSFileHandle", @"Connect on %x", extra);
2096
ocurredEvents.lNetworkEvents ^= FD_CONNECT;
2097
[self receivedEventWrite];
2100
if (ocurredEvents.lNetworkEvents & FD_ACCEPT)
2102
NSDebugMLLog(@"NSFileHandle", @"Accept on %x", extra);
2103
ocurredEvents.lNetworkEvents ^= FD_ACCEPT;
2104
[self receivedEventRead];
2107
if (ocurredEvents.lNetworkEvents & FD_WRITE)
2109
NSDebugMLLog(@"NSFileHandle", @"Write on %x", extra);
2110
ocurredEvents.lNetworkEvents ^= FD_WRITE;
2111
[self receivedEventWrite];
2114
if (ocurredEvents.lNetworkEvents & FD_READ)
2116
NSDebugMLLog(@"NSFileHandle", @"Read on %x", extra);
2117
ocurredEvents.lNetworkEvents ^= FD_READ;
2118
[self receivedEventRead];
2121
if (ocurredEvents.lNetworkEvents & FD_OOB)
2123
NSDebugMLLog(@"NSFileHandle", @"OOB on %x", extra);
2124
ocurredEvents.lNetworkEvents ^= FD_OOB;
2125
[self receivedEventRead];
2128
if (ocurredEvents.lNetworkEvents & FD_CLOSE)
2130
NSDebugMLLog(@"NSFileHandle", @"Close on %x", extra);
2131
ocurredEvents.lNetworkEvents ^= FD_CLOSE;
2132
if ([writeInfo count] > 0)
2134
[self receivedEventWrite];
2138
[self receivedEventRead];
2142
if (ocurredEvents.lNetworkEvents)
2144
NSLog(@"Event not get %d", ocurredEvents.lNetworkEvents);
2149
- (NSDate*) timedOutEvent: (void*)data
2150
type: (RunLoopEventType)type
2151
forMode: (NSString*)mode
2153
return nil; /* Don't restart timed out events */
2156
- (void) setAddr: (struct sockaddr_in *)sin
2158
address = [[NSString alloc] initWithCString: (char*)inet_ntoa(sin->sin_addr)];
2159
service = [[NSString alloc] initWithFormat: @"%d",
2160
(int)GSSwapBigI16ToHost(sin->sin_port)];
2164
- (void) setNonBlocking: (BOOL)flag
2170
else if (isStandardFile == YES)
2174
else if (isNonBlocking == flag)
2180
unsigned long dummy;
2182
if (isSocket != YES)
2188
if (ioctlsocket((SOCKET)_get_osfhandle(descriptor), FIONBIO, &dummy)
2191
NSLog(@"unable to set non-blocking mode - %s",
2192
GSLastErrorStr(errno));
2195
isNonBlocking = flag;
2200
if (ioctlsocket((SOCKET)_get_osfhandle(descriptor), FIONBIO, &dummy)
2203
NSLog(@"unable to set blocking mode - %s",
2204
GSLastErrorStr(errno));
2207
isNonBlocking = flag;
2212
- (NSString*) socketAddress
2217
- (NSString*) socketLocalAddress
2219
NSString *str = nil;
2220
struct sockaddr_in sin;
2221
unsigned size = sizeof(sin);
2223
if (getsockname(descriptor, (struct sockaddr*)&sin, &size) == SOCKET_ERROR)
2225
NSLog(@"unable to get socket name - %s", GSLastErrorStr(errno));
2229
str = [NSString stringWithCString: (char*)inet_ntoa(sin.sin_addr)];
2234
- (NSString*) socketLocalService
2236
NSString *str = nil;
2237
struct sockaddr_in sin;
2238
unsigned size = sizeof(sin);
2240
if (getsockname(descriptor, (struct sockaddr*)&sin, &size) == SOCKET_ERROR)
2242
NSLog(@"unable to get socket name - %s", GSLastErrorStr(errno));
2246
str = [NSString stringWithFormat: @"%d",
2247
(int)GSSwapBigI16ToHost(sin.sin_port)];
2252
- (NSString*) socketProtocol
2257
- (NSString*) socketService
2262
- (BOOL) useCompression
2267
if (gzDescriptor != 0)
2269
return YES; // Already open
2273
return NO; // No descriptor available.
2275
if (readOK == YES && writeOK == YES)
2277
return NO; // Can't both read and write.
2279
d = dup(descriptor);
2282
return NO; // No descriptor available.
2286
gzDescriptor = gzdopen(d, "rb");
2290
gzDescriptor = gzdopen(d, "wb");
2292
if (gzDescriptor == 0)
2295
return NO; // Open attempt failed.