2002-06-30 09:19:30 +00:00
|
|
|
/** Implementation for GSFileHandle for GNUStep
|
|
|
|
Copyright (C) 1997-2002 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
Date: 1997, 2002
|
|
|
|
|
|
|
|
This file is part of the GNUstep Base Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-09-14 11:36:11 +00:00
|
|
|
modify it under the terms of the GNU Lesser General Public
|
2002-06-30 09:19:30 +00:00
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-08 10:38:33 +00:00
|
|
|
version 2 of the License, or (at your option) any later version.
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
2007-09-14 11:36:11 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2002-06-30 09:19:30 +00:00
|
|
|
License along with this library; if not, write to the Free
|
2006-06-20 16:42:08 +00:00
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02111 USA.
|
2002-06-30 09:19:30 +00:00
|
|
|
*/
|
|
|
|
|
2010-02-19 08:12:46 +00:00
|
|
|
#import "common.h"
|
2010-04-13 10:58:40 +00:00
|
|
|
#define EXPOSE_NSFileHandle_IVARS 1
|
2010-02-14 10:48:10 +00:00
|
|
|
#define EXPOSE_GSFileHandle_IVARS 1
|
2007-11-29 20:53:26 +00:00
|
|
|
#import "Foundation/NSData.h"
|
|
|
|
#import "Foundation/NSArray.h"
|
|
|
|
#import "Foundation/NSFileHandle.h"
|
|
|
|
#import "Foundation/NSException.h"
|
|
|
|
#import "Foundation/NSRunLoop.h"
|
|
|
|
#import "Foundation/NSNotification.h"
|
|
|
|
#import "Foundation/NSNotificationQueue.h"
|
|
|
|
#import "Foundation/NSHost.h"
|
|
|
|
#import "Foundation/NSByteOrder.h"
|
|
|
|
#import "Foundation/NSProcessInfo.h"
|
|
|
|
#import "Foundation/NSUserDefaults.h"
|
|
|
|
#import "GSPrivate.h"
|
2011-10-03 16:03:19 +00:00
|
|
|
#import "GSNetwork.h"
|
2011-10-19 15:25:38 +00:00
|
|
|
#import "GSFileHandle.h"
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2007-11-29 20:53:26 +00:00
|
|
|
#import "../Tools/gdomap.h"
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <arpa/inet.h>
|
2011-12-15 09:42:39 +00:00
|
|
|
|
|
|
|
#if defined(HAVE_SYS_FILE_H)
|
|
|
|
# include <sys/file.h>
|
|
|
|
#endif
|
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
#include <sys/stat.h>
|
2011-12-15 09:42:39 +00:00
|
|
|
|
|
|
|
#if defined(HAVE_SYS_FCNTL_H)
|
|
|
|
# include <sys/fcntl.h>
|
|
|
|
#elif defined(HAVE_FCNTL_H)
|
|
|
|
# include <fcntl.h>
|
|
|
|
#endif
|
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#ifdef __svr4__
|
2012-08-26 08:55:49 +00:00
|
|
|
# ifdef HAVE_SYS_FILIO_H
|
|
|
|
# include <sys/filio.h>
|
|
|
|
# endif
|
2002-06-30 09:19:30 +00:00
|
|
|
#endif
|
|
|
|
#include <netdb.h>
|
2003-07-11 11:31:26 +00:00
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
/*
|
|
|
|
* Stuff for setting the sockets into non-blocking mode.
|
|
|
|
*/
|
2012-10-30 13:35:00 +00:00
|
|
|
#if defined(__POSIX_SOURCE)\
|
|
|
|
|| defined(__EXT_POSIX1_198808)\
|
|
|
|
|| defined(O_NONBLOCK)
|
2002-06-30 09:19:30 +00:00
|
|
|
#define NBLK_OPT O_NONBLOCK
|
|
|
|
#else
|
|
|
|
#define NBLK_OPT FNDELAY
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef O_BINARY
|
|
|
|
#ifdef _O_BINARY
|
|
|
|
#define O_BINARY _O_BINARY
|
|
|
|
#else
|
|
|
|
#define O_BINARY 0
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef INADDR_NONE
|
|
|
|
#define INADDR_NONE -1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Maximum data in single I/O operation
|
2013-11-23 14:46:48 +00:00
|
|
|
#define NETBUF_SIZE (1024 * 16)
|
2005-05-11 08:14:32 +00:00
|
|
|
#define READ_SIZE NETBUF_SIZE*10
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2016-03-04 18:06:01 +00:00
|
|
|
static GSFileHandle *fh_stdin = nil;
|
|
|
|
static GSFileHandle *fh_stdout = nil;
|
|
|
|
static GSFileHandle *fh_stderr = nil;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2013-11-23 14:46:48 +00:00
|
|
|
@interface GSTcpTune : NSObject
|
2013-11-27 17:05:32 +00:00
|
|
|
- (int) delay;
|
2013-11-23 14:46:48 +00:00
|
|
|
- (int) recvSize;
|
|
|
|
- (int) sendSize: (int)bytesToSend;
|
|
|
|
- (void) tune: (void*)handle;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSTcpTune
|
|
|
|
|
2013-11-27 17:05:32 +00:00
|
|
|
static int tuneDelay = 0;
|
2013-11-23 14:46:48 +00:00
|
|
|
static int tuneLinger = -1;
|
|
|
|
static int tuneReceive = 0;
|
|
|
|
static BOOL tuneSendAll = NO;
|
|
|
|
static int tuneRBuf = 0;
|
|
|
|
static int tuneSBuf = 0;
|
|
|
|
|
|
|
|
+ (void) defaultsChanged: (NSNotification*)n
|
|
|
|
{
|
|
|
|
NSUserDefaults *defs = (NSUserDefaults*)[n object];
|
|
|
|
NSString *str;
|
|
|
|
|
|
|
|
if (nil == defs)
|
|
|
|
{
|
|
|
|
defs = [NSUserDefaults standardUserDefaults];
|
|
|
|
}
|
|
|
|
str = [defs stringForKey: @"GSTcpLinger"];
|
|
|
|
if (nil == str)
|
|
|
|
{
|
|
|
|
tuneLinger = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tuneLinger = [str intValue];
|
|
|
|
}
|
|
|
|
tuneRBuf = (int)[defs integerForKey: @"GSTcpRcvBuf"];
|
|
|
|
tuneSBuf = (int)[defs integerForKey: @"GSTcpSndBuf"];
|
|
|
|
tuneReceive = (int)[defs integerForKey: @"GSTcpReceive"];
|
|
|
|
tuneSendAll = [defs boolForKey: @"GSTcpSendAll"];
|
2013-11-27 17:05:32 +00:00
|
|
|
tuneDelay = [defs boolForKey: @"GSTcpDelay"];
|
2013-11-23 14:46:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
static BOOL beenHere = NO;
|
|
|
|
|
|
|
|
if (NO == beenHere)
|
|
|
|
{
|
|
|
|
NSNotificationCenter *nc;
|
|
|
|
NSUserDefaults *defs;
|
|
|
|
|
|
|
|
beenHere = YES;
|
|
|
|
nc = [NSNotificationCenter defaultCenter];
|
|
|
|
defs = [NSUserDefaults standardUserDefaults];
|
|
|
|
[nc addObserver: self
|
|
|
|
selector: @selector(defaultsChanged:)
|
|
|
|
name: NSUserDefaultsDidChangeNotification
|
|
|
|
object: defs];
|
|
|
|
[self defaultsChanged: nil];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-27 17:05:32 +00:00
|
|
|
- (int) delay
|
|
|
|
{
|
|
|
|
return tuneDelay; // Milliseconds to delay close
|
|
|
|
}
|
|
|
|
|
2013-11-23 14:46:48 +00:00
|
|
|
- (int) recvSize
|
|
|
|
{
|
|
|
|
if (tuneReceive > 0)
|
|
|
|
{
|
|
|
|
return tuneReceive; // Return receive buffer size
|
|
|
|
}
|
|
|
|
if (tuneRBuf > 0)
|
|
|
|
{
|
|
|
|
return tuneRBuf; // Return socket receive buffer size
|
|
|
|
}
|
|
|
|
return READ_SIZE; // Return hard-coded default
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int) sendSize: (int)bytesToSend
|
|
|
|
{
|
|
|
|
if (YES == tuneSendAll)
|
|
|
|
{
|
|
|
|
return bytesToSend; // Try to send all in one go
|
|
|
|
}
|
|
|
|
if (tuneSBuf > 0 && tuneSBuf <= bytesToSend)
|
|
|
|
{
|
|
|
|
return tuneSBuf; // Limit to socket send buffer
|
|
|
|
}
|
|
|
|
if (NETBUF_SIZE <= bytesToSend)
|
|
|
|
{
|
|
|
|
return NETBUF_SIZE; // Limit to hard coded default
|
|
|
|
}
|
|
|
|
return bytesToSend;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) tune: (void*)handle
|
|
|
|
{
|
|
|
|
int desc = (int)(intptr_t)handle;
|
|
|
|
int value;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable tcp-level tracking of whether connection is alive.
|
|
|
|
*/
|
|
|
|
value = 1;
|
2018-02-04 08:28:51 +00:00
|
|
|
if (setsockopt(desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&value, sizeof(value))
|
|
|
|
< 0)
|
|
|
|
{
|
|
|
|
NSDebugMLLog(@"GSTcpTune", @"setsockopt keepalive failed");
|
|
|
|
}
|
2013-11-23 14:46:48 +00:00
|
|
|
|
|
|
|
if (tuneLinger >= 0)
|
|
|
|
{
|
|
|
|
struct linger l;
|
|
|
|
|
|
|
|
l.l_onoff = 1;
|
|
|
|
l.l_linger = tuneLinger;
|
|
|
|
if (setsockopt(desc, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l)) < 0)
|
|
|
|
{
|
|
|
|
NSLog(@"Failed to set GSTcpLinger %d: %@",
|
|
|
|
tuneLinger, [NSError _last]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tuneRBuf > 0)
|
|
|
|
{
|
|
|
|
/* Set the receive buffer for the socket.
|
|
|
|
*/
|
|
|
|
if (setsockopt(desc, SOL_SOCKET, SO_RCVBUF,
|
|
|
|
(char *)&tuneRBuf, sizeof(tuneRBuf)) < 0)
|
|
|
|
{
|
|
|
|
NSLog(@"Failed to set GSTcpRcvBuf %d: %@", tuneRBuf, [NSError _last]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSDebugMLLog(@"GSTcpTune", @"Set GSTcpRcvBuf %d", tuneRBuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (tuneSBuf > 0)
|
|
|
|
{
|
|
|
|
/* Set the send buffer for the socket.
|
|
|
|
*/
|
|
|
|
if (setsockopt(desc, SOL_SOCKET, SO_SNDBUF,
|
|
|
|
(char *)&tuneSBuf, sizeof(tuneSBuf)) < 0)
|
|
|
|
{
|
|
|
|
NSLog(@"Failed to set GSTcpSndBuf %d: %@", tuneSBuf, [NSError _last]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSDebugMLLog(@"GSTcpTune", @"Set GSTcpSndBuf %d", tuneSBuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
// Key to info dictionary for operation mode.
|
|
|
|
static NSString* NotificationKey = @"NSFileHandleNotificationKey";
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
@interface GSFileHandle(private)
|
|
|
|
- (void) receivedEventRead;
|
|
|
|
- (void) receivedEventWrite;
|
|
|
|
@end
|
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
@implementation GSFileHandle
|
|
|
|
|
2013-11-23 14:46:48 +00:00
|
|
|
static GSTcpTune *tune = nil;
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (nil == tune)
|
|
|
|
{
|
|
|
|
tune = [GSTcpTune new];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-09-30 08:33:25 +00:00
|
|
|
/**
|
|
|
|
* Encapsulates low level read operation to get data from the operating
|
|
|
|
* system.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) read: (void*)buf length: (NSUInteger)len
|
2002-09-29 08:43:24 +00:00
|
|
|
{
|
2011-11-02 17:19:37 +00:00
|
|
|
int result;
|
|
|
|
|
|
|
|
do
|
2002-09-29 08:43:24 +00:00
|
|
|
{
|
2011-11-02 17:19:37 +00:00
|
|
|
#if USE_ZLIB
|
|
|
|
if (gzDescriptor != 0)
|
|
|
|
{
|
|
|
|
result = gzread(gzDescriptor, buf, len);
|
|
|
|
}
|
|
|
|
else
|
2002-09-29 08:43:24 +00:00
|
|
|
#endif
|
2011-11-02 17:19:37 +00:00
|
|
|
if (isSocket)
|
|
|
|
{
|
|
|
|
result = recv(descriptor, buf, len, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = read(descriptor, buf, len);
|
|
|
|
}
|
2002-09-29 08:43:24 +00:00
|
|
|
}
|
2011-11-02 17:19:37 +00:00
|
|
|
while (result < 0 && EINTR == errno);
|
|
|
|
return result;
|
2002-09-29 08:43:24 +00:00
|
|
|
}
|
|
|
|
|
2002-09-30 08:33:25 +00:00
|
|
|
/**
|
|
|
|
* Encapsulates low level write operation to send data to the operating
|
|
|
|
* system.
|
|
|
|
*/
|
2009-02-23 20:42:32 +00:00
|
|
|
- (NSInteger) write: (const void*)buf length: (NSUInteger)len
|
2002-09-29 08:43:24 +00:00
|
|
|
{
|
2011-11-02 17:19:37 +00:00
|
|
|
int result;
|
|
|
|
|
|
|
|
do
|
2002-09-29 08:43:24 +00:00
|
|
|
{
|
2011-11-02 17:19:37 +00:00
|
|
|
#if USE_ZLIB
|
|
|
|
if (gzDescriptor != 0)
|
|
|
|
{
|
|
|
|
result = gzwrite(gzDescriptor, (char*)buf, len);
|
|
|
|
}
|
|
|
|
else
|
2002-09-29 08:43:24 +00:00
|
|
|
#endif
|
2011-11-02 17:19:37 +00:00
|
|
|
if (isSocket)
|
|
|
|
{
|
|
|
|
result = send(descriptor, buf, len, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = write(descriptor, buf, len);
|
|
|
|
}
|
2002-09-29 08:43:24 +00:00
|
|
|
}
|
2011-11-02 17:19:37 +00:00
|
|
|
while (result < 0 && EINTR == errno);
|
|
|
|
return result;
|
2002-09-29 08:43:24 +00:00
|
|
|
}
|
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
+ (id) allocWithZone: (NSZone*)z
|
|
|
|
{
|
|
|
|
return NSAllocateObject ([self class], 0, z);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
2013-10-27 05:42:40 +00:00
|
|
|
if (self == fh_stdin)
|
|
|
|
{
|
|
|
|
RETAIN(self);
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
format: @"Attempt to deallocate standard input handle"];
|
|
|
|
}
|
|
|
|
if (self == fh_stdout)
|
|
|
|
{
|
|
|
|
RETAIN(self);
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
format: @"Attempt to deallocate standard output handle"];
|
|
|
|
}
|
|
|
|
if (self == fh_stderr)
|
|
|
|
{
|
|
|
|
RETAIN(self);
|
|
|
|
[NSException raise: NSGenericException
|
|
|
|
format: @"Attempt to deallocate standard error handle"];
|
|
|
|
}
|
2010-08-31 14:32:40 +00:00
|
|
|
DESTROY(address);
|
|
|
|
DESTROY(service);
|
|
|
|
DESTROY(protocol);
|
|
|
|
DESTROY(readInfo);
|
|
|
|
DESTROY(writeInfo);
|
2015-08-28 09:21:45 +00:00
|
|
|
|
|
|
|
/* Finalize *after* destroying readInfo and writeInfo so that, if the
|
|
|
|
* file handle needs to be closed, we don't generate any notifications
|
|
|
|
* containing the deallocated object. Tnanks to david for this fix.
|
|
|
|
*/
|
|
|
|
[self finalize];
|
2002-06-30 09:19:30 +00:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2009-01-12 18:36:37 +00:00
|
|
|
- (void) finalize
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
|
|
|
[self ignoreReadDescriptor];
|
|
|
|
[self ignoreWriteDescriptor];
|
|
|
|
|
2013-11-27 12:26:33 +00:00
|
|
|
if (closeOnDealloc == YES && descriptor != -1)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2013-11-27 12:26:33 +00:00
|
|
|
[self closeFile];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
2013-11-27 12:26:33 +00:00
|
|
|
else
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2013-11-27 12:26:33 +00:00
|
|
|
#if USE_ZLIB
|
|
|
|
/*
|
|
|
|
* The gzDescriptor should always be closed when we have done with it.
|
|
|
|
*/
|
|
|
|
if (gzDescriptor != 0)
|
|
|
|
{
|
|
|
|
gzclose(gzDescriptor);
|
|
|
|
gzDescriptor = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (descriptor != -1)
|
|
|
|
{
|
|
|
|
[self setNonBlocking: wasNonBlocking];
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initializing a GSFileHandle Object
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
|
|
|
self = [self initAsClientInBackgroundAtAddress: a
|
|
|
|
service: s
|
|
|
|
protocol: p
|
|
|
|
forModes: nil];
|
|
|
|
if (self != nil)
|
|
|
|
{
|
|
|
|
NSRunLoop *loop;
|
|
|
|
NSDate *limit;
|
|
|
|
|
|
|
|
loop = [NSRunLoop currentRunLoop];
|
|
|
|
limit = [NSDate dateWithTimeIntervalSinceNow: 300];
|
|
|
|
while ([limit timeIntervalSinceNow] > 0
|
|
|
|
&& (readInfo != nil || [writeInfo count] > 0))
|
|
|
|
{
|
|
|
|
[loop runMode: NSDefaultRunLoopMode
|
|
|
|
beforeDate: limit];
|
|
|
|
}
|
|
|
|
if (readInfo != nil || [writeInfo count] > 0 || readOK == NO)
|
|
|
|
{
|
|
|
|
/* Must have timed out or failed */
|
|
|
|
DESTROY(self);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self setNonBlocking: NO];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* States for socks connection negotiation
|
|
|
|
*/
|
|
|
|
NSString * const GSSOCKSConnect = @"GSSOCKSConnect";
|
|
|
|
NSString * const GSSOCKSSendAuth = @"GSSOCKSSendAuth";
|
|
|
|
NSString * const GSSOCKSRecvAuth = @"GSSOCKSRecvAuth";
|
|
|
|
NSString * const GSSOCKSSendConn = @"GSSOCKSSendConn";
|
|
|
|
NSString * const GSSOCKSRecvConn = @"GSSOCKSRecvConn";
|
|
|
|
NSString * const 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
|
2005-02-22 11:22:44 +00:00
|
|
|
* connect command
|
2002-06-30 09:19:30 +00:00
|
|
|
* 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)
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
len = 4; // Fixed size (IPV4) address
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
else if (bytes[3] == 3)
|
|
|
|
{
|
|
|
|
len = 1 + bytes[4]; // Domain name with leading length
|
|
|
|
}
|
|
|
|
else if (bytes[3] == 4)
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
len = 16; // Fixed size (IPV6) address
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
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
|
|
|
|
{
|
|
|
|
static NSString *esocks = nil;
|
|
|
|
static NSString *dsocks = nil;
|
2003-11-09 12:49:15 +00:00
|
|
|
static BOOL beenHere = NO;
|
2005-11-14 09:28:37 +00:00
|
|
|
int net;
|
2011-10-03 16:03:19 +00:00
|
|
|
struct sockaddr sin;
|
|
|
|
struct sockaddr lsin;
|
2004-03-29 14:53:37 +00:00
|
|
|
NSString *lhost = nil;
|
2002-06-30 09:19:30 +00:00
|
|
|
NSString *shost = nil;
|
|
|
|
NSString *sport = nil;
|
|
|
|
|
2003-01-22 10:54:29 +00:00
|
|
|
if (beenHere == NO)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
|
|
|
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"];
|
|
|
|
}
|
2005-10-26 21:22:35 +00:00
|
|
|
esocks = [esocks copy];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (a == nil || [a isEqualToString: @""])
|
|
|
|
{
|
|
|
|
a = @"localhost";
|
|
|
|
}
|
|
|
|
if (s == nil)
|
|
|
|
{
|
|
|
|
NSLog(@"bad argument - service is nil");
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2004-03-29 14:53:37 +00:00
|
|
|
if ([p hasPrefix: @"bind-"] == YES)
|
|
|
|
{
|
|
|
|
NSRange r;
|
|
|
|
|
|
|
|
lhost = [p substringFromIndex: 5];
|
|
|
|
r = [lhost rangeOfString: @":"];
|
|
|
|
if (r.length > 0)
|
|
|
|
{
|
|
|
|
p = [lhost substringFromIndex: NSMaxRange(r)];
|
|
|
|
lhost = [lhost substringToIndex: r.location];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p = nil;
|
|
|
|
}
|
2011-10-03 16:03:19 +00:00
|
|
|
if (GSPrivateSockaddrSetup(lhost, 0, p, @"tcp", &lsin) == NO)
|
2004-03-29 14:53:37 +00:00
|
|
|
{
|
|
|
|
NSLog(@"bad bind address specification");
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2004-03-29 14:53:37 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
p = @"tcp";
|
|
|
|
}
|
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
/**
|
|
|
|
* A protocol fo the form 'socks-...' controls socks operation,
|
|
|
|
* overriding defaults and environment variables.<br />
|
|
|
|
* If it is just 'socks-' it turns off socks for this fiel handle.<br />
|
|
|
|
* 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";
|
|
|
|
}
|
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
if (GSPrivateSockaddrSetup(a, 0, s, p, &sin) == NO)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
NSLog(@"bad address-service-protocol combination");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
[self setAddr: &sin]; // Store the address of the remote end.
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't use SOCKS if we are contacting the local host.
|
|
|
|
*/
|
|
|
|
if (shost != nil)
|
|
|
|
{
|
|
|
|
NSHost *remote = [NSHost hostWithAddress: [self socketAddress]];
|
|
|
|
NSHost *local = [NSHost currentHost];
|
|
|
|
|
|
|
|
if ([remote isEqual: local] || [remote isEqual: [NSHost localHost]])
|
|
|
|
{
|
|
|
|
shost = nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (shost != nil)
|
|
|
|
{
|
2011-10-03 16:03:19 +00:00
|
|
|
if (GSPrivateSockaddrSetup(shost, 0, sport, p, &sin) == NO)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
|
|
|
NSLog(@"bad SOCKS host-port combination");
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
if ((net = socket(sin.sa_family, SOCK_STREAM, PF_UNSPEC)) == -1)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2006-10-20 10:56:27 +00:00
|
|
|
NSLog(@"unable to create socket - %@", [NSError _last]);
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
2005-06-15 03:56:13 +00:00
|
|
|
|
2013-11-23 14:46:48 +00:00
|
|
|
[tune tune: (void*)(intptr_t)net];
|
2013-10-18 07:30:49 +00:00
|
|
|
|
2004-03-29 14:53:37 +00:00
|
|
|
if (lhost != nil)
|
|
|
|
{
|
2011-10-03 16:03:19 +00:00
|
|
|
if (bind(net, &lsin, GSPrivateSockaddrLength(&lsin)) == -1)
|
2004-03-29 14:53:37 +00:00
|
|
|
{
|
2011-10-03 16:03:19 +00:00
|
|
|
NSLog(@"unable to bind to port %@ - %@",
|
|
|
|
GSPrivateSockaddrName(&lsin), [NSError _last]);
|
2004-03-29 14:53:37 +00:00
|
|
|
(void) close(net);
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2004-03-29 14:53:37 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
self = [self initWithFileDescriptor: net closeOnDealloc: YES];
|
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
NSMutableDictionary* info;
|
|
|
|
|
2002-06-30 09:43:21 +00:00
|
|
|
isSocket = YES;
|
2002-06-30 09:19:30 +00:00
|
|
|
[self setNonBlocking: YES];
|
2011-10-03 16:03:19 +00:00
|
|
|
if (connect(net, &sin, GSPrivateSockaddrLength(&sin)) == -1)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2011-10-08 16:29:19 +00:00
|
|
|
if (!GSWOULDBLOCK)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2011-10-13 13:39:35 +00:00
|
|
|
NSError *e = [NSError _last];
|
|
|
|
|
2011-10-14 09:34:35 +00:00
|
|
|
NSLog(@"unable to make socket connection to %@ - %@ (%d)",
|
|
|
|
GSPrivateSockaddrName(&sin), e, (int)[e code]);
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
2012-08-26 08:55:49 +00:00
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
info = [[NSMutableDictionary alloc] initWithCapacity: 4];
|
|
|
|
[info setObject: address forKey: NSFileHandleNotificationDataItem];
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
[writeInfo addObject: info];
|
|
|
|
RELEASE(info);
|
|
|
|
[self watchWriteDescriptor];
|
|
|
|
connectOK = YES;
|
|
|
|
acceptOK = NO;
|
|
|
|
readOK = NO;
|
|
|
|
writeOK = NO;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initAsServerAtAddress: (NSString*)a
|
|
|
|
service: (NSString*)s
|
|
|
|
protocol: (NSString*)p
|
|
|
|
{
|
|
|
|
#ifndef BROKEN_SO_REUSEADDR
|
2005-11-14 09:28:37 +00:00
|
|
|
int status = 1;
|
2002-06-30 09:19:30 +00:00
|
|
|
#endif
|
2005-11-14 09:28:37 +00:00
|
|
|
int net;
|
2011-10-03 16:03:19 +00:00
|
|
|
struct sockaddr sin;
|
2005-07-08 11:48:37 +00:00
|
|
|
unsigned int size = sizeof(sin);
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
if (GSPrivateSockaddrSetup(a, 0, s, p, &sin) == NO)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
NSLog(@"bad address-service-protocol combination");
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
if ((net = socket(sin.sa_family, SOCK_STREAM, PF_UNSPEC)) == -1)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2006-10-20 10:56:27 +00:00
|
|
|
NSLog(@"unable to create socket - %@", [NSError _last]);
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef BROKEN_SO_REUSEADDR
|
|
|
|
/*
|
|
|
|
* Under decent systems, SO_REUSEADDR means that the port can be reused
|
|
|
|
* immediately that this process exits. Under some it means
|
|
|
|
* that multiple processes can serve the same port simultaneously.
|
|
|
|
* We don't want that broken behavior!
|
|
|
|
*/
|
2018-02-04 08:28:51 +00:00
|
|
|
if (setsockopt(net, SOL_SOCKET, SO_REUSEADDR, (char*)&status, sizeof(status))
|
|
|
|
< 0)
|
|
|
|
{
|
|
|
|
NSDebugMLLog(@"GSTcpTune", @"setsockopt reuseaddr failed");
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
#endif
|
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
if (bind(net, &sin, GSPrivateSockaddrLength(&sin)) == -1)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2011-10-13 13:39:35 +00:00
|
|
|
NSError *e = [NSError _last];
|
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
NSLog(@"unable to bind to port %@ - %@",
|
2011-10-13 13:39:35 +00:00
|
|
|
GSPrivateSockaddrName(&sin), e);
|
2002-06-30 09:19:30 +00:00
|
|
|
(void) close(net);
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2010-09-30 14:16:11 +00:00
|
|
|
/* We try to allow a large number of connections.
|
|
|
|
*/
|
2010-10-01 09:22:52 +00:00
|
|
|
if (listen(net, GSBACKLOG) == -1)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2006-10-20 10:56:27 +00:00
|
|
|
NSLog(@"unable to listen on port - %@", [NSError _last]);
|
2002-06-30 09:19:30 +00:00
|
|
|
(void) close(net);
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
if (getsockname(net, &sin, &size) == -1)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2006-10-20 10:56:27 +00:00
|
|
|
NSLog(@"unable to get socket name - %@", [NSError _last]);
|
2002-06-30 09:19:30 +00:00
|
|
|
(void) close(net);
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
self = [self initWithFileDescriptor: net closeOnDealloc: YES];
|
|
|
|
if (self)
|
|
|
|
{
|
2002-06-30 09:43:21 +00:00
|
|
|
isSocket = YES;
|
2002-06-30 09:19:30 +00:00
|
|
|
connectOK = NO;
|
|
|
|
acceptOK = YES;
|
|
|
|
readOK = NO;
|
|
|
|
writeOK = NO;
|
|
|
|
[self setAddr: &sin];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initForReadingAtPath: (NSString*)path
|
|
|
|
{
|
|
|
|
int d = open([path fileSystemRepresentation], O_RDONLY|O_BINARY);
|
|
|
|
|
|
|
|
if (d < 0)
|
|
|
|
{
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self = [self initWithFileDescriptor: d closeOnDealloc: YES];
|
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
connectOK = NO;
|
|
|
|
acceptOK = NO;
|
|
|
|
writeOK = NO;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initForWritingAtPath: (NSString*)path
|
|
|
|
{
|
|
|
|
int d = open([path fileSystemRepresentation], O_WRONLY|O_BINARY);
|
|
|
|
|
|
|
|
if (d < 0)
|
|
|
|
{
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self = [self initWithFileDescriptor: d closeOnDealloc: YES];
|
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
connectOK = NO;
|
|
|
|
acceptOK = NO;
|
|
|
|
readOK = NO;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initForUpdatingAtPath: (NSString*)path
|
|
|
|
{
|
|
|
|
int d = open([path fileSystemRepresentation], O_RDWR|O_BINARY);
|
|
|
|
|
|
|
|
if (d < 0)
|
|
|
|
{
|
2010-02-25 18:49:31 +00:00
|
|
|
DESTROY(self);
|
2002-06-30 09:19:30 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self = [self initWithFileDescriptor: d closeOnDealloc: YES];
|
|
|
|
if (self != nil)
|
|
|
|
{
|
|
|
|
connectOK = NO;
|
|
|
|
acceptOK = NO;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithStandardError
|
|
|
|
{
|
|
|
|
if (fh_stderr != nil)
|
|
|
|
{
|
2010-04-07 06:46:01 +00:00
|
|
|
ASSIGN(self, fh_stderr);
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self = [self initWithFileDescriptor: 2 closeOnDealloc: NO];
|
2013-10-27 05:42:40 +00:00
|
|
|
ASSIGN(fh_stderr, self);
|
2010-08-31 14:32:40 +00:00
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
readOK = NO;
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithStandardInput
|
|
|
|
{
|
|
|
|
if (fh_stdin != nil)
|
|
|
|
{
|
2010-04-07 06:46:01 +00:00
|
|
|
ASSIGN(self, fh_stdin);
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self = [self initWithFileDescriptor: 0 closeOnDealloc: NO];
|
2013-10-27 05:42:40 +00:00
|
|
|
ASSIGN(fh_stdin, self);
|
2010-08-31 14:32:40 +00:00
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
writeOK = NO;
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithStandardOutput
|
|
|
|
{
|
|
|
|
if (fh_stdout != nil)
|
|
|
|
{
|
2010-04-07 06:46:01 +00:00
|
|
|
ASSIGN(self, fh_stdout);
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
self = [self initWithFileDescriptor: 1 closeOnDealloc: NO];
|
2013-10-27 05:42:40 +00:00
|
|
|
ASSIGN(fh_stdout, self);
|
2010-08-31 14:32:40 +00:00
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
readOK = NO;
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithNullDevice
|
|
|
|
{
|
|
|
|
self = [self initWithFileDescriptor: open("/dev/null", O_RDWR|O_BINARY)
|
|
|
|
closeOnDealloc: YES];
|
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
isNullDevice = YES;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithFileDescriptor: (int)desc closeOnDealloc: (BOOL)flag
|
|
|
|
{
|
|
|
|
self = [super init];
|
2010-04-07 14:56:51 +00:00
|
|
|
if (nil == self)
|
|
|
|
{
|
|
|
|
if (YES == flag)
|
|
|
|
{
|
|
|
|
close(desc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2005-11-14 09:25:31 +00:00
|
|
|
struct stat sbuf;
|
|
|
|
int e;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
if (fstat(desc, &sbuf) < 0)
|
|
|
|
{
|
2016-03-09 13:16:16 +00:00
|
|
|
#if defined(_WIN32)
|
2009-06-17 10:35:49 +00:00
|
|
|
/* On windows, an fstat will fail if the descriptor is a pipe
|
|
|
|
* or socket, so we simply mark the descriptor as not being a
|
|
|
|
* standard file.
|
|
|
|
*/
|
|
|
|
isStandardFile = NO;
|
|
|
|
#else
|
|
|
|
/* This should never happen on unix. If it does, we have somehow
|
|
|
|
* ended up with a bad descriptor.
|
|
|
|
*/
|
2006-10-09 14:00:01 +00:00
|
|
|
NSLog(@"unable to get status of descriptor %d - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
desc, [NSError _last]);
|
2009-06-17 10:35:49 +00:00
|
|
|
isStandardFile = NO;
|
|
|
|
#endif
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (S_ISREG(sbuf.st_mode))
|
|
|
|
{
|
|
|
|
isStandardFile = YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
isStandardFile = NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((e = fcntl(desc, F_GETFL, 0)) >= 0)
|
|
|
|
{
|
|
|
|
if (e & NBLK_OPT)
|
|
|
|
{
|
|
|
|
wasNonBlocking = YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wasNonBlocking = NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isNonBlocking = wasNonBlocking;
|
|
|
|
descriptor = desc;
|
|
|
|
closeOnDealloc = flag;
|
|
|
|
readInfo = nil;
|
|
|
|
writeInfo = [NSMutableArray new];
|
|
|
|
readMax = 0;
|
|
|
|
writePos = 0;
|
|
|
|
readOK = YES;
|
|
|
|
writeOK = YES;
|
|
|
|
acceptOK = YES;
|
|
|
|
connectOK = YES;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithNativeHandle: (void*)hdl
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
return [self initWithFileDescriptor: (uintptr_t)hdl closeOnDealloc: NO];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithNativeHandle: (void*)hdl closeOnDealloc: (BOOL)flag
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
return [self initWithFileDescriptor: (uintptr_t)hdl closeOnDealloc: flag];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) checkAccept
|
|
|
|
{
|
|
|
|
if (acceptOK == NO)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"accept not permitted in this file handle"];
|
|
|
|
}
|
|
|
|
if (readInfo)
|
|
|
|
{
|
|
|
|
id operation = [readInfo objectForKey: NotificationKey];
|
|
|
|
|
|
|
|
if (operation == NSFileHandleConnectionAcceptedNotification)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"accept already in progress"];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"read already in progress"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) checkConnect
|
|
|
|
{
|
|
|
|
if (connectOK == NO)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"connect not permitted in this file handle"];
|
|
|
|
}
|
|
|
|
if ([writeInfo count] > 0)
|
|
|
|
{
|
2005-03-07 11:19:34 +00:00
|
|
|
NSDictionary *info = [writeInfo objectAtIndex: 0];
|
|
|
|
id operation = [info objectForKey: NotificationKey];
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
if (operation == GSFileHandleConnectCompletionNotification)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"connect already in progress"];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"write already in progress"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) checkRead
|
|
|
|
{
|
|
|
|
if (readOK == NO)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"read not permitted on this file handle"];
|
|
|
|
}
|
|
|
|
if (readInfo)
|
|
|
|
{
|
|
|
|
id operation = [readInfo objectForKey: NotificationKey];
|
|
|
|
|
|
|
|
if (operation == NSFileHandleConnectionAcceptedNotification)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"accept already in progress"];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"read already in progress"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) checkWrite
|
|
|
|
{
|
|
|
|
if (writeOK == NO)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"write not permitted in this file handle"];
|
|
|
|
}
|
|
|
|
if ([writeInfo count] > 0)
|
|
|
|
{
|
2005-03-07 11:19:34 +00:00
|
|
|
NSDictionary *info = [writeInfo objectAtIndex: 0];
|
|
|
|
id operation = [info objectForKey: NotificationKey];
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
if (operation != GSFileHandleWriteCompletionNotification)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"connect in progress"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returning file handles
|
|
|
|
|
|
|
|
- (int) fileDescriptor
|
|
|
|
{
|
|
|
|
return descriptor;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void*) nativeHandle
|
|
|
|
{
|
2006-01-10 10:29:11 +00:00
|
|
|
return (void*)(intptr_t)descriptor;
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Synchronous I/O operations
|
|
|
|
|
|
|
|
- (NSData*) availableData
|
|
|
|
{
|
2013-11-23 14:46:48 +00:00
|
|
|
int rmax = [tune recvSize];
|
|
|
|
char buf[rmax];
|
2002-06-30 09:19:30 +00:00
|
|
|
NSMutableData* d;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
[self checkRead];
|
|
|
|
d = [NSMutableData dataWithCapacity: 0];
|
|
|
|
if (isStandardFile)
|
|
|
|
{
|
2006-02-14 08:30:16 +00:00
|
|
|
if (isNonBlocking == YES)
|
|
|
|
{
|
|
|
|
[self setNonBlocking: NO];
|
|
|
|
}
|
2002-09-29 08:43:24 +00:00
|
|
|
while ((len = [self read: buf length: sizeof(buf)]) > 0)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
|
|
|
[d appendBytes: buf length: len];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-02-14 08:30:16 +00:00
|
|
|
if (isNonBlocking == NO)
|
|
|
|
{
|
|
|
|
[self setNonBlocking: YES];
|
|
|
|
}
|
2002-09-29 08:43:24 +00:00
|
|
|
len = [self read: buf length: sizeof(buf)];
|
2006-02-14 08:30:16 +00:00
|
|
|
|
|
|
|
if (len <= 0)
|
|
|
|
{
|
|
|
|
if (errno == EAGAIN || errno == EINTR)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Read would have blocked ... so try to get a single character
|
|
|
|
* in non-blocking mode (to ensure we wait until data arrives)
|
|
|
|
* and then try again.
|
|
|
|
* This ensures that we block for *some* data as we should.
|
|
|
|
*/
|
|
|
|
[self setNonBlocking: NO];
|
|
|
|
len = [self read: buf length: 1];
|
|
|
|
[self setNonBlocking: YES];
|
|
|
|
if (len == 1)
|
|
|
|
{
|
|
|
|
len = [self read: &buf[1] length: sizeof(buf) - 1];
|
|
|
|
if (len <= 0)
|
|
|
|
{
|
|
|
|
len = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
len = len + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-06-30 09:43:21 +00:00
|
|
|
if (len > 0)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
|
|
|
[d appendBytes: buf length: len];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (len < 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
2006-10-09 14:00:01 +00:00
|
|
|
format: @"unable to read from descriptor - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSData*) readDataToEndOfFile
|
|
|
|
{
|
2013-11-23 14:46:48 +00:00
|
|
|
int rmax = [tune recvSize];
|
|
|
|
char buf[rmax];
|
2002-06-30 09:19:30 +00:00
|
|
|
NSMutableData* d;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
[self checkRead];
|
|
|
|
if (isNonBlocking == YES)
|
|
|
|
{
|
|
|
|
[self setNonBlocking: NO];
|
|
|
|
}
|
|
|
|
d = [NSMutableData dataWithCapacity: 0];
|
2002-09-29 08:43:24 +00:00
|
|
|
while ((len = [self read: buf length: sizeof(buf)]) > 0)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
|
|
|
[d appendBytes: buf length: len];
|
|
|
|
}
|
|
|
|
if (len < 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
2006-10-09 14:00:01 +00:00
|
|
|
format: @"unable to read from descriptor - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSData*) readDataOfLength: (unsigned)len
|
|
|
|
{
|
2005-06-04 05:01:01 +00:00
|
|
|
NSMutableData *d;
|
|
|
|
int got;
|
2013-11-23 14:46:48 +00:00
|
|
|
int rmax = [tune recvSize];
|
|
|
|
char buf[rmax];
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
[self checkRead];
|
|
|
|
if (isNonBlocking == YES)
|
|
|
|
{
|
|
|
|
[self setNonBlocking: NO];
|
|
|
|
}
|
2007-04-13 05:14:21 +00:00
|
|
|
|
2013-11-23 14:46:48 +00:00
|
|
|
d = [NSMutableData dataWithCapacity: len < sizeof(buf) ? len : sizeof(buf)];
|
2007-04-13 05:14:21 +00:00
|
|
|
do
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2007-04-13 05:14:21 +00:00
|
|
|
int chunk = len > sizeof(buf) ? sizeof(buf) : len;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2007-04-13 05:14:21 +00:00
|
|
|
got = [self read: buf length: chunk];
|
|
|
|
if (got > 0)
|
|
|
|
{
|
|
|
|
[d appendBytes: buf length: got];
|
|
|
|
len -= got;
|
|
|
|
}
|
|
|
|
else if (got < 0)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
2006-10-09 14:00:01 +00:00
|
|
|
format: @"unable to read from descriptor - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
}
|
2007-04-13 05:14:21 +00:00
|
|
|
while (len > 0 && got > 0);
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) writeData: (NSData*)item
|
|
|
|
{
|
|
|
|
int rval = 0;
|
|
|
|
const void* ptr = [item bytes];
|
|
|
|
unsigned int len = [item length];
|
|
|
|
unsigned int pos = 0;
|
|
|
|
|
|
|
|
[self checkWrite];
|
|
|
|
if (isNonBlocking == YES)
|
|
|
|
{
|
|
|
|
[self setNonBlocking: NO];
|
|
|
|
}
|
|
|
|
while (pos < len)
|
|
|
|
{
|
|
|
|
int toWrite = len - pos;
|
|
|
|
|
2013-11-23 14:46:48 +00:00
|
|
|
toWrite = [tune sendSize: toWrite];
|
2002-09-29 08:43:24 +00:00
|
|
|
rval = [self write: (char*)ptr+pos length: toWrite];
|
2002-06-30 09:19:30 +00:00
|
|
|
if (rval < 0)
|
|
|
|
{
|
|
|
|
if (errno == EAGAIN || errno == EINTR)
|
|
|
|
{
|
|
|
|
rval = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pos += rval;
|
|
|
|
}
|
|
|
|
if (rval < 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
2006-10-09 14:00:01 +00:00
|
|
|
format: @"unable to write to descriptor - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Asynchronous I/O operations
|
|
|
|
|
|
|
|
- (void) acceptConnectionInBackgroundAndNotifyForModes: (NSArray*)modes
|
|
|
|
{
|
|
|
|
[self checkAccept];
|
|
|
|
readMax = 0;
|
|
|
|
RELEASE(readInfo);
|
|
|
|
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
|
|
|
|
[readInfo setObject: NSFileHandleConnectionAcceptedNotification
|
|
|
|
forKey: NotificationKey];
|
|
|
|
[self watchReadDescriptorForModes: modes];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) readDataInBackgroundAndNotifyLength: (unsigned)len
|
|
|
|
forModes: (NSArray*)modes
|
|
|
|
{
|
|
|
|
NSMutableData *d;
|
|
|
|
|
|
|
|
[self checkRead];
|
|
|
|
if (len > 0x7fffffff)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
|
|
|
format: @"length (%u) too large", len];
|
|
|
|
}
|
|
|
|
readMax = len;
|
|
|
|
RELEASE(readInfo);
|
|
|
|
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
|
|
|
|
[readInfo setObject: NSFileHandleReadCompletionNotification
|
|
|
|
forKey: NotificationKey];
|
|
|
|
d = [[NSMutableData alloc] initWithCapacity: readMax];
|
|
|
|
[readInfo setObject: d forKey: NSFileHandleNotificationDataItem];
|
|
|
|
RELEASE(d);
|
|
|
|
[self watchReadDescriptorForModes: modes];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) readInBackgroundAndNotifyForModes: (NSArray*)modes
|
|
|
|
{
|
|
|
|
NSMutableData *d;
|
|
|
|
|
|
|
|
[self checkRead];
|
|
|
|
readMax = -1; // Accept any quantity of data.
|
|
|
|
RELEASE(readInfo);
|
|
|
|
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
|
|
|
|
[readInfo setObject: NSFileHandleReadCompletionNotification
|
|
|
|
forKey: NotificationKey];
|
|
|
|
d = [[NSMutableData alloc] initWithCapacity: 0];
|
|
|
|
[readInfo setObject: d forKey: NSFileHandleNotificationDataItem];
|
|
|
|
RELEASE(d);
|
|
|
|
[self watchReadDescriptorForModes: modes];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) readToEndOfFileInBackgroundAndNotifyForModes: (NSArray*)modes
|
|
|
|
{
|
|
|
|
NSMutableData *d;
|
|
|
|
|
|
|
|
[self checkRead];
|
|
|
|
readMax = 0;
|
|
|
|
RELEASE(readInfo);
|
|
|
|
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
|
|
|
|
[readInfo setObject: NSFileHandleReadToEndOfFileCompletionNotification
|
|
|
|
forKey: NotificationKey];
|
|
|
|
d = [[NSMutableData alloc] initWithCapacity: 0];
|
|
|
|
[readInfo setObject: d forKey: NSFileHandleNotificationDataItem];
|
|
|
|
RELEASE(d);
|
|
|
|
[self watchReadDescriptorForModes: modes];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) waitForDataInBackgroundAndNotifyForModes: (NSArray*)modes
|
|
|
|
{
|
|
|
|
[self checkRead];
|
|
|
|
readMax = 0;
|
|
|
|
RELEASE(readInfo);
|
|
|
|
readInfo = [[NSMutableDictionary alloc] initWithCapacity: 4];
|
|
|
|
[readInfo setObject: NSFileHandleDataAvailableNotification
|
|
|
|
forKey: NotificationKey];
|
|
|
|
[readInfo setObject: [NSMutableData dataWithCapacity: 0]
|
|
|
|
forKey: NSFileHandleNotificationDataItem];
|
|
|
|
[self watchReadDescriptorForModes: modes];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seeking within a file
|
|
|
|
|
|
|
|
- (unsigned long long) offsetInFile
|
|
|
|
{
|
|
|
|
off_t result = -1;
|
|
|
|
|
|
|
|
if (isStandardFile && descriptor >= 0)
|
|
|
|
{
|
|
|
|
#if USE_ZLIB
|
|
|
|
if (gzDescriptor != 0)
|
|
|
|
{
|
|
|
|
result = gzseek(gzDescriptor, 0, SEEK_CUR);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
result = lseek(descriptor, 0, SEEK_CUR);
|
|
|
|
}
|
|
|
|
if (result < 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
2006-10-09 14:00:01 +00:00
|
|
|
format: @"failed to move to offset in file - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
return (unsigned long long)result;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (unsigned long long) seekToEndOfFile
|
|
|
|
{
|
|
|
|
off_t result = -1;
|
|
|
|
|
|
|
|
if (isStandardFile && descriptor >= 0)
|
|
|
|
{
|
|
|
|
#if USE_ZLIB
|
|
|
|
if (gzDescriptor != 0)
|
|
|
|
{
|
|
|
|
result = gzseek(gzDescriptor, 0, SEEK_END);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
result = lseek(descriptor, 0, SEEK_END);
|
|
|
|
}
|
|
|
|
if (result < 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
2006-10-09 14:00:01 +00:00
|
|
|
format: @"failed to move to offset in file - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
return (unsigned long long)result;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) seekToFileOffset: (unsigned long long)pos
|
|
|
|
{
|
|
|
|
off_t result = -1;
|
|
|
|
|
|
|
|
if (isStandardFile && descriptor >= 0)
|
|
|
|
{
|
|
|
|
#if USE_ZLIB
|
|
|
|
if (gzDescriptor != 0)
|
|
|
|
{
|
|
|
|
result = gzseek(gzDescriptor, (off_t)pos, SEEK_SET);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
result = lseek(descriptor, (off_t)pos, SEEK_SET);
|
|
|
|
}
|
|
|
|
if (result < 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
2006-10-09 14:00:01 +00:00
|
|
|
format: @"failed to move to offset in file - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Operations on file
|
|
|
|
|
|
|
|
- (void) closeFile
|
|
|
|
{
|
|
|
|
if (descriptor < 0)
|
|
|
|
{
|
|
|
|
[NSException raise: NSFileHandleOperationException
|
|
|
|
format: @"attempt to close closed file"];
|
|
|
|
}
|
|
|
|
[self ignoreReadDescriptor];
|
|
|
|
[self ignoreWriteDescriptor];
|
|
|
|
|
2013-11-23 14:46:48 +00:00
|
|
|
[self setNonBlocking: NO];
|
2002-06-30 09:19:30 +00:00
|
|
|
#if USE_ZLIB
|
|
|
|
if (gzDescriptor != 0)
|
|
|
|
{
|
|
|
|
gzclose(gzDescriptor);
|
|
|
|
gzDescriptor = 0;
|
|
|
|
}
|
|
|
|
#endif
|
2013-11-23 14:46:48 +00:00
|
|
|
if (YES == isSocket)
|
|
|
|
{
|
2013-11-27 17:05:32 +00:00
|
|
|
int milli = [tune delay];
|
|
|
|
|
2013-11-23 14:46:48 +00:00
|
|
|
shutdown(descriptor, SHUT_WR);
|
2013-11-27 17:05:32 +00:00
|
|
|
if (milli > 0)
|
2013-11-23 14:46:48 +00:00
|
|
|
{
|
2013-11-27 17:05:32 +00:00
|
|
|
NSTimeInterval until;
|
|
|
|
|
|
|
|
until = [NSDate timeIntervalSinceReferenceDate];
|
|
|
|
until += ((double)milli) / 1000.0;
|
2013-11-23 14:46:48 +00:00
|
|
|
|
2013-11-27 17:05:32 +00:00
|
|
|
[self setNonBlocking: YES];
|
|
|
|
while ([NSDate timeIntervalSinceReferenceDate] < until)
|
2013-11-23 14:46:48 +00:00
|
|
|
{
|
2013-11-27 17:05:32 +00:00
|
|
|
int result;
|
|
|
|
char buffer[4096];
|
|
|
|
|
|
|
|
result = read(descriptor, buffer, sizeof(buffer));
|
|
|
|
if (result <= 0)
|
2013-11-23 14:46:48 +00:00
|
|
|
{
|
2013-11-27 17:05:32 +00:00
|
|
|
if (result < 0)
|
|
|
|
{
|
|
|
|
if (EAGAIN == errno || EINTR == errno)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
NSLog(@"%@ read fail on socket shutdown: %@",
|
|
|
|
self, [NSError _last]);
|
|
|
|
}
|
|
|
|
break;
|
2013-11-23 14:46:48 +00:00
|
|
|
}
|
|
|
|
}
|
2013-11-27 17:05:32 +00:00
|
|
|
[self setNonBlocking: YES];
|
2013-11-23 14:46:48 +00:00
|
|
|
}
|
|
|
|
}
|
2004-07-28 11:36:09 +00:00
|
|
|
(void)close(descriptor);
|
2002-06-30 09:19:30 +00:00
|
|
|
descriptor = -1;
|
|
|
|
acceptOK = NO;
|
|
|
|
connectOK = NO;
|
|
|
|
readOK = NO;
|
|
|
|
writeOK = NO;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear any pending operations on the file handle, sending
|
|
|
|
* notifications if necessary.
|
|
|
|
*/
|
|
|
|
if (readInfo)
|
|
|
|
{
|
|
|
|
[readInfo setObject: @"File handle closed locally"
|
|
|
|
forKey: GSFileHandleNotificationError];
|
|
|
|
[self postReadNotification];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([writeInfo count])
|
|
|
|
{
|
|
|
|
NSMutableDictionary *info = [writeInfo objectAtIndex: 0];
|
|
|
|
|
|
|
|
[info setObject: @"File handle closed locally"
|
|
|
|
forKey: GSFileHandleNotificationError];
|
|
|
|
[self postWriteNotification];
|
|
|
|
[writeInfo removeAllObjects];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) synchronizeFile
|
|
|
|
{
|
|
|
|
if (isStandardFile)
|
|
|
|
{
|
|
|
|
(void)sync();
|
2003-07-11 11:31:26 +00:00
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) truncateFileAtOffset: (unsigned long long)pos
|
|
|
|
{
|
|
|
|
if (isStandardFile && descriptor >= 0)
|
|
|
|
{
|
|
|
|
(void)ftruncate(descriptor, pos);
|
|
|
|
}
|
|
|
|
[self seekToFileOffset: pos];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) writeInBackgroundAndNotify: (NSData*)item forModes: (NSArray*)modes
|
|
|
|
{
|
|
|
|
NSMutableDictionary* info;
|
|
|
|
|
|
|
|
[self checkWrite];
|
|
|
|
|
|
|
|
info = [[NSMutableDictionary alloc] initWithCapacity: 4];
|
|
|
|
[info setObject: item forKey: NSFileHandleNotificationDataItem];
|
|
|
|
[info setObject: GSFileHandleWriteCompletionNotification
|
|
|
|
forKey: NotificationKey];
|
|
|
|
if (modes != nil)
|
|
|
|
{
|
|
|
|
[info setObject: modes forKey: NSFileHandleNotificationMonitorModes];
|
|
|
|
}
|
|
|
|
[writeInfo addObject: info];
|
|
|
|
RELEASE(info);
|
|
|
|
[self watchWriteDescriptor];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) writeInBackgroundAndNotify: (NSData*)item;
|
|
|
|
{
|
|
|
|
[self writeInBackgroundAndNotify: item forModes: nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) postReadNotification
|
|
|
|
{
|
|
|
|
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];
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
if (name == nil)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
n = [NSNotification notificationWithName: name object: self userInfo: info];
|
|
|
|
|
|
|
|
RELEASE(info); /* Retained by the notification. */
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
[self ignoreWriteDescriptor];
|
|
|
|
modes = (NSArray*)[info objectForKey: NSFileHandleNotificationMonitorModes];
|
|
|
|
name = (NSString*)[info objectForKey: NotificationKey];
|
|
|
|
|
|
|
|
n = [NSNotification notificationWithName: name object: self userInfo: info];
|
|
|
|
|
|
|
|
writePos = 0;
|
|
|
|
[writeInfo removeObjectAtIndex: 0]; /* Retained by notification. */
|
|
|
|
|
|
|
|
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. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) readInProgress
|
|
|
|
{
|
|
|
|
if (readInfo)
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) writeInProgress
|
|
|
|
{
|
|
|
|
if ([writeInfo count] > 0)
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) ignoreReadDescriptor
|
|
|
|
{
|
|
|
|
NSRunLoop *l;
|
|
|
|
NSArray *modes;
|
|
|
|
|
|
|
|
if (descriptor < 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
l = [NSRunLoop currentRunLoop];
|
|
|
|
modes = nil;
|
|
|
|
|
|
|
|
if (readInfo)
|
|
|
|
{
|
|
|
|
modes = (NSArray*)[readInfo objectForKey:
|
|
|
|
NSFileHandleNotificationMonitorModes];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (modes && [modes count])
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
for (i = 0; i < [modes count]; i++)
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
[l removeEvent: (void*)(uintptr_t)descriptor
|
2002-06-30 09:19:30 +00:00
|
|
|
type: ET_RDESC
|
|
|
|
forMode: [modes objectAtIndex: i]
|
|
|
|
all: YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
[l removeEvent: (void*)(uintptr_t)descriptor
|
2002-06-30 09:19:30 +00:00
|
|
|
type: ET_RDESC
|
|
|
|
forMode: NSDefaultRunLoopMode
|
|
|
|
all: YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) ignoreWriteDescriptor
|
|
|
|
{
|
|
|
|
NSRunLoop *l;
|
|
|
|
NSArray *modes;
|
|
|
|
|
|
|
|
if (descriptor < 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
l = [NSRunLoop currentRunLoop];
|
|
|
|
modes = nil;
|
|
|
|
|
|
|
|
if ([writeInfo count] > 0)
|
|
|
|
{
|
2010-08-31 14:32:40 +00:00
|
|
|
NSMutableDictionary *info = [writeInfo objectAtIndex: 0];
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2010-08-31 14:32:40 +00:00
|
|
|
modes = [info objectForKey: NSFileHandleNotificationMonitorModes];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (modes && [modes count])
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
for (i = 0; i < [modes count]; i++)
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
[l removeEvent: (void*)(uintptr_t)descriptor
|
2002-06-30 09:19:30 +00:00
|
|
|
type: ET_WDESC
|
|
|
|
forMode: [modes objectAtIndex: i]
|
|
|
|
all: YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
[l removeEvent: (void*)(uintptr_t)descriptor
|
2002-06-30 09:19:30 +00:00
|
|
|
type: ET_WDESC
|
|
|
|
forMode: NSDefaultRunLoopMode
|
|
|
|
all: YES];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) watchReadDescriptorForModes: (NSArray*)modes;
|
|
|
|
{
|
|
|
|
NSRunLoop *l;
|
|
|
|
|
|
|
|
if (descriptor < 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2005-02-23 16:05:09 +00:00
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
l = [NSRunLoop currentRunLoop];
|
|
|
|
[self setNonBlocking: YES];
|
|
|
|
if (modes && [modes count])
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
for (i = 0; i < [modes count]; i++)
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
[l addEvent: (void*)(uintptr_t)descriptor
|
2002-06-30 09:19:30 +00:00
|
|
|
type: ET_RDESC
|
|
|
|
watcher: self
|
|
|
|
forMode: [modes objectAtIndex: i]];
|
|
|
|
}
|
|
|
|
[readInfo setObject: modes forKey: NSFileHandleNotificationMonitorModes];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
[l addEvent: (void*)(uintptr_t)descriptor
|
2002-06-30 09:19:30 +00:00
|
|
|
type: ET_RDESC
|
|
|
|
watcher: self
|
|
|
|
forMode: NSDefaultRunLoopMode];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) watchWriteDescriptor
|
|
|
|
{
|
|
|
|
if (descriptor < 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ([writeInfo count] > 0)
|
|
|
|
{
|
|
|
|
NSMutableDictionary *info = [writeInfo objectAtIndex: 0];
|
|
|
|
NSRunLoop *l = [NSRunLoop currentRunLoop];
|
|
|
|
NSArray *modes = nil;
|
|
|
|
|
|
|
|
modes = [info objectForKey: NSFileHandleNotificationMonitorModes];
|
|
|
|
|
|
|
|
[self setNonBlocking: YES];
|
|
|
|
if (modes && [modes count])
|
|
|
|
{
|
2003-01-03 20:14:47 +00:00
|
|
|
unsigned int i;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
|
|
|
for (i = 0; i < [modes count]; i++)
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
[l addEvent: (void*)(uintptr_t)descriptor
|
2002-06-30 09:19:30 +00:00
|
|
|
type: ET_WDESC
|
|
|
|
watcher: self
|
|
|
|
forMode: [modes objectAtIndex: i]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-01-11 09:32:13 +00:00
|
|
|
[l addEvent: (void*)(uintptr_t)descriptor
|
2002-06-30 09:19:30 +00:00
|
|
|
type: ET_WDESC
|
|
|
|
watcher: self
|
|
|
|
forMode: NSDefaultRunLoopMode];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
- (void) receivedEventRead
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
|
|
|
NSString *operation;
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
operation = [readInfo objectForKey: NotificationKey];
|
|
|
|
if (operation == NSFileHandleConnectionAcceptedNotification)
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2011-10-03 16:03:19 +00:00
|
|
|
struct sockaddr buf;
|
2005-02-23 16:05:09 +00:00
|
|
|
int desc;
|
2005-07-08 11:48:37 +00:00
|
|
|
unsigned int blen = sizeof(buf);
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
desc = accept(descriptor, &buf, &blen);
|
2005-11-14 09:28:37 +00:00
|
|
|
if (desc == -1)
|
2005-02-23 16:05:09 +00:00
|
|
|
{
|
|
|
|
NSString *s;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2006-10-09 14:00:01 +00:00
|
|
|
s = [NSString stringWithFormat: @"Accept attempt failed - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2005-02-23 16:05:09 +00:00
|
|
|
[readInfo setObject: s forKey: GSFileHandleNotificationError];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // Accept attempt completed.
|
|
|
|
GSFileHandle *h;
|
2011-10-03 16:03:19 +00:00
|
|
|
struct sockaddr sin;
|
2005-07-08 11:48:37 +00:00
|
|
|
unsigned int size = sizeof(sin);
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2013-11-23 14:46:48 +00:00
|
|
|
[tune tune: (void*)(intptr_t)desc];
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
h = [[[self class] alloc] initWithFileDescriptor: desc
|
2013-11-23 14:46:48 +00:00
|
|
|
closeOnDealloc: YES];
|
2005-02-23 16:05:09 +00:00
|
|
|
h->isSocket = YES;
|
2018-02-04 10:10:57 +00:00
|
|
|
if (getpeername(desc, &sin, &size) >= 0)
|
|
|
|
{
|
|
|
|
[h setAddr: &sin];
|
|
|
|
}
|
2005-02-23 16:05:09 +00:00
|
|
|
[readInfo setObject: h
|
2018-02-04 10:10:57 +00:00
|
|
|
forKey: NSFileHandleNotificationFileHandleItem];
|
2005-02-23 16:05:09 +00:00
|
|
|
RELEASE(h);
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
2005-02-23 16:05:09 +00:00
|
|
|
[self postReadNotification];
|
|
|
|
}
|
|
|
|
else if (operation == NSFileHandleDataAvailableNotification)
|
|
|
|
{
|
|
|
|
[self postReadNotification];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSMutableData *item;
|
|
|
|
int length;
|
|
|
|
int received = 0;
|
2013-11-23 14:46:48 +00:00
|
|
|
int rmax = [tune recvSize];
|
|
|
|
char buf[rmax];
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
item = [readInfo objectForKey: NSFileHandleNotificationDataItem];
|
|
|
|
/*
|
|
|
|
* We may have a maximum data size set...
|
|
|
|
*/
|
|
|
|
if (readMax > 0)
|
|
|
|
{
|
|
|
|
length = (unsigned int)readMax - [item length];
|
|
|
|
if (length > (int)sizeof(buf))
|
|
|
|
{
|
2002-06-30 09:19:30 +00:00
|
|
|
length = sizeof(buf);
|
|
|
|
}
|
2005-02-23 16:05:09 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
length = sizeof(buf);
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
received = [self read: buf length: length];
|
|
|
|
if (received == 0)
|
|
|
|
{ // Read up to end of file.
|
|
|
|
[self postReadNotification];
|
|
|
|
}
|
|
|
|
else if (received < 0)
|
|
|
|
{
|
|
|
|
if (errno != EAGAIN && errno != EINTR)
|
|
|
|
{
|
|
|
|
NSString *s;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2006-10-09 14:00:01 +00:00
|
|
|
s = [NSString stringWithFormat: @"Read attempt failed - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2005-02-23 16:05:09 +00:00
|
|
|
[readInfo setObject: s forKey: GSFileHandleNotificationError];
|
|
|
|
[self postReadNotification];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
2005-02-23 16:05:09 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[item appendBytes: buf length: received];
|
|
|
|
if (readMax < 0 || (readMax > 0 && (int)[item length] == readMax))
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2005-02-23 16:05:09 +00:00
|
|
|
// Read a single chunk of data
|
|
|
|
[self postReadNotification];
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-02-23 16:05:09 +00:00
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
- (void) receivedEventWrite
|
|
|
|
{
|
|
|
|
NSString *operation;
|
|
|
|
NSMutableDictionary *info;
|
|
|
|
|
|
|
|
info = [writeInfo objectAtIndex: 0];
|
|
|
|
operation = [info objectForKey: NotificationKey];
|
|
|
|
if (operation == GSFileHandleConnectCompletionNotification
|
|
|
|
|| operation == GSSOCKSConnect)
|
|
|
|
{ // Connection attempt completed.
|
|
|
|
int result;
|
2006-11-29 19:57:38 +00:00
|
|
|
int rval;
|
2005-07-08 11:48:37 +00:00
|
|
|
unsigned len = sizeof(result);
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2006-11-29 19:57:38 +00:00
|
|
|
rval = getsockopt(descriptor, SOL_SOCKET, SO_ERROR, (char*)&result, &len);
|
|
|
|
if (rval != 0)
|
2005-02-23 16:05:09 +00:00
|
|
|
{
|
|
|
|
NSString *s;
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2006-10-09 14:00:01 +00:00
|
|
|
s = [NSString stringWithFormat: @"Connect attempt failed - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
[NSError _last]];
|
2005-02-23 16:05:09 +00:00
|
|
|
[info setObject: s forKey: GSFileHandleNotificationError];
|
2006-11-29 19:57:38 +00:00
|
|
|
}
|
|
|
|
else if (result != 0)
|
|
|
|
{
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
s = [NSString stringWithFormat: @"Connect attempt failed - %@",
|
2006-11-29 20:04:09 +00:00
|
|
|
[NSError _systemError: result]];
|
2006-11-29 19:57:38 +00:00
|
|
|
[info setObject: s forKey: GSFileHandleNotificationError];
|
2005-02-23 16:05:09 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
readOK = YES;
|
|
|
|
writeOK = YES;
|
|
|
|
}
|
|
|
|
connectOK = NO;
|
|
|
|
[self postWriteNotification];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSData *item;
|
|
|
|
int length;
|
|
|
|
const void *ptr;
|
|
|
|
|
|
|
|
item = [info objectForKey: NSFileHandleNotificationDataItem];
|
|
|
|
length = [item length];
|
|
|
|
ptr = [item bytes];
|
|
|
|
if (writePos < length)
|
|
|
|
{
|
|
|
|
int written;
|
|
|
|
|
|
|
|
written = [self write: (char*)ptr+writePos
|
2013-11-22 16:33:46 +00:00
|
|
|
length: length-writePos];
|
2005-02-23 16:05:09 +00:00
|
|
|
if (written <= 0)
|
|
|
|
{
|
|
|
|
if (written < 0 && errno != EAGAIN && errno != EINTR)
|
|
|
|
{
|
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
s = [NSString stringWithFormat:
|
2006-10-20 10:56:27 +00:00
|
|
|
@"Write attempt failed - %@", [NSError _last]];
|
2005-02-23 16:05:09 +00:00
|
|
|
[info setObject: s forKey: GSFileHandleNotificationError];
|
|
|
|
[self postWriteNotification];
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
else
|
2005-02-23 16:05:09 +00:00
|
|
|
{
|
|
|
|
writePos += written;
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
}
|
2005-02-23 16:05:09 +00:00
|
|
|
if (writePos >= length)
|
|
|
|
{ // Write operation completed.
|
|
|
|
[self postWriteNotification];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
- (void) receivedEvent: (void*)data
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
extra: (void*)extra
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
{
|
|
|
|
NSDebugMLLog(@"NSFileHandle", @"%@ event: %d", self, type);
|
|
|
|
|
|
|
|
if (isNonBlocking == NO)
|
|
|
|
{
|
|
|
|
[self setNonBlocking: YES];
|
|
|
|
}
|
|
|
|
if (type == ET_RDESC)
|
|
|
|
{
|
|
|
|
[self receivedEventRead];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self receivedEventWrite];
|
|
|
|
}
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
- (void) setAddr: (struct sockaddr *)sin
|
2002-06-30 09:19:30 +00:00
|
|
|
{
|
2011-10-03 16:03:19 +00:00
|
|
|
NSString *s;
|
|
|
|
|
|
|
|
ASSIGN(address, GSPrivateSockaddrHost(sin));
|
|
|
|
s = [NSString stringWithFormat: @"%d", GSPrivateSockaddrPort(sin)];
|
|
|
|
ASSIGN(service, s);
|
2002-06-30 09:19:30 +00:00
|
|
|
protocol = @"tcp";
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setNonBlocking: (BOOL)flag
|
|
|
|
{
|
|
|
|
if (descriptor < 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (isStandardFile == YES)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (isNonBlocking == flag)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int e;
|
|
|
|
|
|
|
|
if ((e = fcntl(descriptor, F_GETFL, 0)) >= 0)
|
|
|
|
{
|
|
|
|
if (flag == YES)
|
|
|
|
{
|
|
|
|
e |= NBLK_OPT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
e &= ~NBLK_OPT;
|
|
|
|
}
|
|
|
|
if (fcntl(descriptor, F_SETFL, e) < 0)
|
|
|
|
{
|
2006-10-09 14:00:01 +00:00
|
|
|
NSLog(@"unable to set non-blocking mode for %d - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
descriptor, [NSError _last]);
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
isNonBlocking = flag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-10-09 14:00:01 +00:00
|
|
|
NSLog(@"unable to get non-blocking mode for %d - %@",
|
2006-10-20 10:56:27 +00:00
|
|
|
descriptor, [NSError _last]);
|
2002-06-30 09:19:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) socketAddress
|
|
|
|
{
|
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
2004-08-18 10:12:13 +00:00
|
|
|
- (NSString*) socketLocalAddress
|
|
|
|
{
|
|
|
|
NSString *str = nil;
|
2011-10-03 16:03:19 +00:00
|
|
|
struct sockaddr sin;
|
2005-07-08 11:48:37 +00:00
|
|
|
unsigned size = sizeof(sin);
|
2004-08-18 10:12:13 +00:00
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
if (getsockname(descriptor, &sin, &size) == -1)
|
2004-08-18 10:12:13 +00:00
|
|
|
{
|
2006-10-20 10:56:27 +00:00
|
|
|
NSLog(@"unable to get socket name - %@", [NSError _last]);
|
2004-08-18 10:12:13 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-10-03 16:03:19 +00:00
|
|
|
str = GSPrivateSockaddrHost(&sin);
|
2004-08-18 10:12:13 +00:00
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) socketLocalService
|
|
|
|
{
|
|
|
|
NSString *str = nil;
|
2011-10-03 16:03:19 +00:00
|
|
|
struct sockaddr sin;
|
2005-07-08 11:48:37 +00:00
|
|
|
unsigned size = sizeof(sin);
|
2005-02-22 11:22:44 +00:00
|
|
|
|
2011-10-03 16:03:19 +00:00
|
|
|
if (getsockname(descriptor, &sin, &size) == -1)
|
2004-08-18 10:12:13 +00:00
|
|
|
{
|
2006-10-20 10:56:27 +00:00
|
|
|
NSLog(@"unable to get socket name - %@", [NSError _last]);
|
2004-08-18 10:12:13 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-10-03 16:03:19 +00:00
|
|
|
str = [NSString stringWithFormat: @"%d", GSPrivateSockaddrPort(&sin)];
|
2004-08-18 10:12:13 +00:00
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2002-06-30 09:19:30 +00:00
|
|
|
- (NSString*) socketProtocol
|
|
|
|
{
|
|
|
|
return protocol;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString*) socketService
|
|
|
|
{
|
|
|
|
return service;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) useCompression
|
|
|
|
{
|
|
|
|
#if USE_ZLIB
|
|
|
|
int d;
|
|
|
|
|
|
|
|
if (gzDescriptor != 0)
|
|
|
|
{
|
|
|
|
return YES; // Already open
|
|
|
|
}
|
|
|
|
if (descriptor < 0)
|
|
|
|
{
|
|
|
|
return NO; // No descriptor available.
|
|
|
|
}
|
|
|
|
if (readOK == YES && writeOK == YES)
|
|
|
|
{
|
|
|
|
return NO; // Can't both read and write.
|
|
|
|
}
|
2018-01-30 07:36:53 +00:00
|
|
|
d = dup(descriptor); // d is closed by gzclose() later.
|
2002-06-30 09:19:30 +00:00
|
|
|
if (d < 0)
|
|
|
|
{
|
|
|
|
return NO; // No descriptor available.
|
|
|
|
}
|
|
|
|
if (readOK == YES)
|
|
|
|
{
|
|
|
|
gzDescriptor = gzdopen(d, "rb");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gzDescriptor = gzdopen(d, "wb");
|
|
|
|
}
|
|
|
|
if (gzDescriptor == 0)
|
|
|
|
{
|
|
|
|
close(d);
|
|
|
|
return NO; // Open attempt failed.
|
|
|
|
}
|
|
|
|
return YES;
|
|
|
|
#endif
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|