From d15ec8ba01b710868c350b0cd738f2cd17aa1111 Mon Sep 17 00:00:00 2001 From: rfm Date: Fri, 4 Jan 2008 06:59:49 +0000 Subject: [PATCH] Restructuring of socket streams for maintainability and consistency between windows and unix git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@25854 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 17 + Headers/Foundation/NSStream.h | 12 +- Source/GSNetwork.h | 83 ++ Source/GSStream.h | 226 ++++- Source/GSStream.m | 1542 ++++++++++++++++++++++++++++++++- Source/unix/NSStream.m | 1232 ++------------------------ Source/win32/NSStream.m | 1160 +------------------------ 7 files changed, 1925 insertions(+), 2347 deletions(-) create mode 100644 Source/GSNetwork.h diff --git a/ChangeLog b/ChangeLog index d986c4f47..14448ac2b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2007-01-04 Richard Frith-Macdonald + + * Headers/Foundation/NSStream.h: Tweak comments. + * Source/GSNetwork.h: New file for common networking declarations. + * Source/GSStream.h: + * Source/GSStream.m: + * Source/unix/NSStream.m: + * Source/win32/NSStream.m: + Extract socket networking code for both unix and windows into + semi-abstract superclasses for input/output/server streams so + that we avoid huge duplication of code and can maintain the + socket code more easily. + Alter the behavior of the code on windows so that when the + other end closes the connection we should be able to read all + data in transit in the same way as on unix (ie don't close + the local end until a read/write actually fails). + 2007-01-03 Richard Frith-Macdonald * Source/inet_pton.c: Add mswindows implementation of diff --git a/Headers/Foundation/NSStream.h b/Headers/Foundation/NSStream.h index 36913f7bd..8d1983950 100644 --- a/Headers/Foundation/NSStream.h +++ b/Headers/Foundation/NSStream.h @@ -254,18 +254,18 @@ typedef enum { /** * Creates and returns by reference an NSInputStream object and - * NSOutputStream object for a local socket connection with the - * specified path. To use them you need to open them and wait - * on the NSStreamEventOpenCompleted event on one of them + * NSOutputStream object for a local socket or named pipe connection + * with the specified path. To use them you need to open them and wait + * on the NSStreamEventOpenCompleted event on one of them. */ + (void) getLocalStreamsToPath: (NSString *)path inputStream: (NSInputStream **)inputStream outputStream: (NSOutputStream **)outputStream; /** * Creates and returns by reference an NSInputStream object and NSOutputStream - * object for a anonymous local socket. Although you still need to open them, - * the open will be instantanious, and no NSStreamEventOpenCompleted event - * will be delivered. + * object for a anonymous local socket or pipe. Although you still need to + * open them, the open will be instantaneous, and no NSStreamEventOpenCompleted + * event will be delivered. */ + (void) pipeWithInputStream: (NSInputStream **)inputStream outputStream: (NSOutputStream **)outputStream; diff --git a/Source/GSNetwork.h b/Source/GSNetwork.h new file mode 100644 index 000000000..56e182b64 --- /dev/null +++ b/Source/GSNetwork.h @@ -0,0 +1,83 @@ +#ifndef INCLUDED_GSNETWORK_H +#define INCLUDED_GSNETWORK_H 1 + +/* GSNetwork.h - This collects the system header files needed for + networking code. In future it may also contain internal wrappers + to standardise networking operations. + + Copyright (C) 2008, Free Software Foundation, Inc. + + Written by: Richard Frith-Macdonald + Created: Jan 2008 + + This file is part of the GNUstep Base Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + 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. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02111 USA. + */ + + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include + +#if defined(__MINGW32__) + +#include +#include +#include +#include +#if !defined(EAFNOSUPPORT) +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif +#define BADSOCKET(X) ((X) == INVALID_SOCKET) +#define GSNETERROR WSAGetLastError() +#define GSWOULDBLOCK (GSNETERROR == WSAEWOULDBLOCK || GSNETERROR == WSAEINPROGRESS) + +#else + +#include +#include +#include +#include + +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif +#ifndef PF_LOCAL +#define PF_LOCAL PF_UNIX +#endif + +#define SOCKET int /* Socket type */ +#define BADSOCKET(X) ((X) < 0) +#define GSNETERROR errno +#define GSWOULDBLOCK (errno == EINPROGRESS) + +#endif /* __MINGW32__ */ + +#ifndef INADDRSZ +#define INADDRSZ 4 +#endif + +#ifndef socklen_t +#define socklen_t uint32_t +#endif + +#endif diff --git a/Source/GSStream.h b/Source/GSStream.h index 12d51d36c..994b02ab1 100644 --- a/Source/GSStream.h +++ b/Source/GSStream.h @@ -26,10 +26,13 @@ NSInputStream and NSOutputStream are clusters rather than concrete classes The inherance graph is: NSStream + |-- GSStream + | `--GSSocketStream |-- NSInputStream | `--GSInputStream | |-- GSDataInputStream | |-- GSFileInputStream + | |-- GSPipeInputStream (mswindows only) | `-- GSSocketInputStream | |-- GSInetInputStream | |-- GSLocalInputStream @@ -39,16 +42,18 @@ | |-- GSBufferOutputStream | |-- GSDataOutputStream | |-- GSFileOutputStream + | |-- GSPipeOutputStream (mswindows only) | `-- GSSocketOutputStream | |-- GSInetOutputStream | |-- GSLocalOutputStream | `-- GSInet6InputStream `-- GSServerStream - `-- GSAbstractServerStream - `-- GSSocketServerStream - |-- GSInetServerStream - |-- GSInet6ServerStream - `-- GSLocalServerStream + `-- GSAbstractServerStream + |-- GSLocalServerStream (mswindows) + `-- GSSocketServerStream + |-- GSInetServerStream + |-- GSInet6ServerStream + `-- GSLocalServerStream (gnu/linux) */ #include @@ -86,6 +91,10 @@ IVARS @end +@interface GSAbstractServerStream : GSServerStream +IVARS +@end + @interface NSStream(Private) /** @@ -149,10 +158,6 @@ IVARS IVARS @end -@interface GSAbstractServerStream : GSServerStream -IVARS -@end - /** * The concrete subclass of NSInputStream that reads from the memory */ @@ -187,5 +192,208 @@ IVARS } @end +#include "GSNetwork.h" + + +#define SOCKIVARS \ +{ \ + id _sibling; /* For bidirectional traffic. */\ + BOOL _passive; /* YES means already connected. */\ + BOOL _closing; /* Must close on next failure. */\ + SOCKET _sock; /* Needed for ms-windows. */\ +} + +/* The semi-abstract GSSocketStream class is not intended to be subclassed + * but is used to add behaviors to other socket based classes. + */ +@interface GSSocketStream : GSStream +SOCKIVARS + +/** + * get the sockaddr + */ +- (struct sockaddr*) _peerAddr; + +/** + * setter for closing flag ... the remote end has stopped either sending + * or receiving, so any I/O operation which would block means that the + * connection is no longer operable in that direction. + */ +- (void) _setClosing: (BOOL)passive; + +/** + * setter for passive (the underlying socket connection is already open and + * doesw not need to be re-opened). + */ +- (void) _setPassive: (BOOL)passive; + +/** + * setter for sibling + */ +- (void) _setSibling: (GSSocketStream*)sibling; + +/* + * Set the socket used for this stream. + */ +- (void) _setSock: (SOCKET)sock; + +/* Return the socket + */ +- (SOCKET) _sock; + +/** + * Get the length of the socket addr + */ +- (socklen_t) _sockLen; + +@end + +/** + * The abstract subclass of NSInputStream that reads from a socket. + * It inherits from GSInputStream and adds behaviors from GSSocketStream + * so it must have the same instance variable layout as GSSocketStream. + */ +@interface GSSocketInputStream : GSInputStream +SOCKIVARS +@end +@interface GSSocketInputStream (AddedBehaviors) +- (struct sockaddr*) _peerAddr; +- (void) _setClosing: (BOOL)passive; +- (void) _setPassive: (BOOL)passive; +- (void) _setSibling: (GSSocketStream*)sibling; +- (void) _setSock: (SOCKET)sock; +- (SOCKET) _sock; +- (socklen_t) _sockLen; +@end + +@interface GSInetInputStream : GSSocketInputStream +{ + @private + struct sockaddr_in _peerAddr; +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr port: (int)port; + +@end + + +@interface GSInet6InputStream : GSSocketInputStream +{ + @private +#if defined(AF_INET6) + struct sockaddr_in6 _peerAddr; +#endif +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr port: (int)port; + +@end + +/** + * The abstract subclass of NSOutputStream that writes to a socket. + * It inherits from GSOutputStream and adds behaviors from GSSocketStream + * so it must have the same instance variable layout as GSSocketStream. + */ +@interface GSSocketOutputStream : GSOutputStream +SOCKIVARS +@end +@interface GSSocketOutputStream (AddedBehaviors) +- (struct sockaddr*) _peerAddr; +- (void) _setClosing: (BOOL)passive; +- (void) _setPassive: (BOOL)passive; +- (void) _setSibling: (GSSocketStream*)sibling; +- (void) _setSock: (SOCKET)sock; +- (SOCKET) _sock; +- (socklen_t) _sockLen; +@end + +@interface GSInetOutputStream : GSSocketOutputStream +{ + @private + struct sockaddr_in _peerAddr; +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr port: (int)port; + +@end + +@interface GSInet6OutputStream : GSSocketOutputStream +{ + @private +#if defined(AF_INET6) + struct sockaddr_in6 _peerAddr; +#endif +} + +/** + * the designated initializer + */ +- (id) initToAddr: (NSString*)addr port: (int)port; + +@end + + +/** + * The subclass of NSStream that accepts connections from a socket. + * It inherits from GSAbstractServerStream and adds behaviors from + * GSSocketStream so it must have the same instance variable layout + * as GSSocketStream. + */ +@interface GSSocketServerStream : GSAbstractServerStream +SOCKIVARS + +/** + * Return the class of the inputStream associated with this + * type of serverStream. + */ +- (Class) _inputStreamClass; + +/** + * Return the class of the outputStream associated with this + * type of serverStream. + */ +- (Class) _outputStreamClass; + +/** + * Return the sockaddr for this server + */ +- (struct sockaddr*) _serverAddr; + +@end +@interface GSSocketServerStream (AddedBehaviors) +- (struct sockaddr*) _peerAddr; +- (void) _setClosing: (BOOL)passive; +- (void) _setPassive: (BOOL)passive; +- (void) _setSibling: (GSSocketStream*)sibling; +- (void) _setSock: (SOCKET)sock; +- (SOCKET) _sock; +- (socklen_t) _sockLen; +@end + +@interface GSInetServerStream : GSSocketServerStream +{ + @private + struct sockaddr_in _serverAddr; +} +@end + +@interface GSInet6ServerStream : GSSocketServerStream +{ + @private +#if defined(AF_INET6) + struct sockaddr_in6 _serverAddr; +#endif +} +@end + #endif diff --git a/Source/GSStream.m b/Source/GSStream.m index 9108faf23..398a580e9 100644 --- a/Source/GSStream.m +++ b/Source/GSStream.m @@ -21,18 +21,19 @@ Boston, MA 02111 USA. */ -#include -#include -#import +#include "config.h" + #import +#import +#import +#import #import #import -#import #import -#import #import -#import +#import +#import #import "GSStream.h" #import "GSPrivate.h" @@ -379,13 +380,8 @@ static RunLoopEventType typeForStream(NSStream *aStream) { NSError *theError; -#if defined(__MINGW32__) - errno = GetLastError(); -#endif - theError = [NSError errorWithDomain: NSPOSIXErrorDomain - code: errno - userInfo: nil]; - NSLog(@"%@ error(%d): - %@", self, errno, [NSError _last]); + theError = [NSError _last]; +// NSLog(@"%@ - %@", self, theError); ASSIGN(_lastError, theError); _currentStatus = NSStreamStatusError; } @@ -658,15 +654,6 @@ static RunLoopEventType typeForStream(NSStream *aStream) } @end -@implementation GSAbstractServerStream -+ (void) initialize -{ - if (self == [GSAbstractServerStream class]) - { - GSObjCAddClassBehavior(self, [GSStream class]); - } -} -@end @implementation GSDataInputStream @@ -1315,3 +1302,1514 @@ static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; } @end + + +static inline BOOL +socketError(int result) +{ +#if defined(__MINGW32__) + return (result == SOCKET_ERROR) ? YES : NO; +#else + return (result < 0) ? YES : NO; +#endif +} + +static inline BOOL +socketWouldBlock() +{ +#if defined(__MINGW32__) + int e = WSAGetLastError(); + return (e == WSAEWOULDBLOCK || e == WSAEINPROGRESS) ? YES : NO; +#else + return (errno == EWOULDBLOCK || errno == EINPROGRESS) ? YES : NO; +#endif +} + + +static void +setNonBlocking(SOCKET fd) +{ +#if defined(__MINGW32__) + unsigned long dummy = 1; + + if (ioctlsocket(fd, FIONBIO, &dummy) == SOCKET_ERROR) + { + NSLog(@"unable to set non-blocking mode - %@", [NSError _last]); + } +#else + int flags = fcntl(fd, F_GETFL, 0); + + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) + { + NSLog(@"unable to set non-blocking mode - %@", + [NSError _last]); + } +#endif +} + +@implementation GSSocketStream + +- (void) dealloc +{ + if ([self _isOpened]) + { + [self close]; + } + [_sibling _setSibling: nil]; + _sibling = nil; + [super dealloc]; +} + +- (id) init +{ + if ((self = [super init]) != nil) + { + // so that unopened access will fail + _sibling = nil; + _closing = NO; + _passive = NO; +#if defined(__MINGW32__) + _loopID = WSA_INVALID_EVENT; + _sock = INVALID_SOCKET; +#else + _loopID = (void*)(intptr_t)-1; + _sock = -1; +#endif + } + return self; +} + +- (struct sockaddr*) _peerAddr +{ + [self subclassResponsibility: _cmd]; + return NULL; +} + +- (void) _setLoopID: (void *)ref +{ +#if !defined(__MINGW32__) + _sock = (SOCKET)(intptr_t)ref; // On gnu/linux _sock is _loopID +#endif + _loopID = ref; +} + +- (void) _setClosing: (BOOL)closing +{ + _closing = closing; +} + +- (void) _setPassive: (BOOL)passive +{ + _passive = passive; +} + +- (void) _setSibling: (GSSocketStream*)sibling +{ + _sibling = sibling; +} + +- (void) _setSock: (SOCKET)sock +{ + setNonBlocking(sock); + _sock = sock; + + /* As well as recording the socket, we set up the stream for monitoring it. + * On unix style systems we set the socket descriptor as the _loopID to be + * monitored, and on mswindows systems we create an event object to be + * monitored (the socket events are assoociated with this object later). + */ +#if defined(__MINGW32__) + _loopID = CreateEvent(NULL, NO, NO, NULL); +#else + _loopID = (void*)(intptr_t)sock; // On gnu/linux _sock is _loopID +#endif +} + +- (SOCKET) _sock +{ + return _sock; +} + +- (socklen_t) _sockLen +{ + [self subclassResponsibility: _cmd]; + return 0; +} + +@end + + +@implementation GSSocketInputStream + ++ (void) initialize +{ + if (self == [GSSocketInputStream class]) + { + GSObjCAddClassBehavior(self, [GSSocketStream class]); + } +} + +- (void) open +{ + // could be opened because of sibling + if ([self _isOpened]) + return; + if (_passive || (_sibling && [_sibling _isOpened])) + goto open_ok; + // check sibling status, avoid double connect + if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening) + { + [self _setStatus: NSStreamStatusOpening]; + return; + } + else + { + int result; + + result = connect([self _sock], [self _peerAddr], [self _sockLen]); + if (socketError(result)) + { + if (!socketWouldBlock()) + { + [self _recordError]; + return; + } + /* + * Need to set the status first, so that the run loop can tell + * it needs to add the stream as waiting on writable, as an + * indication of opened + */ + [self _setStatus: NSStreamStatusOpening]; +#if defined(__MINGW32__) + WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); +#endif + if (NSCountMapTable(_loops) > 0) + { + [self _schedule]; + return; + } + else + { + NSRunLoop *r; + NSDate *d; + + /* The stream was not scheduled in any run loop, so we + * implement a blocking connect by running in the default + * run loop mode. + */ + r = [NSRunLoop currentRunLoop]; + d = [NSDate distantFuture]; + [r addStream: self mode: NSDefaultRunLoopMode]; + while ([r runMode: NSDefaultRunLoopMode beforeDate: d] == YES) + { + if (_currentStatus != NSStreamStatusOpening) + { + break; + } + } + [r removeStream: self mode: NSDefaultRunLoopMode]; + return; + } + } + } + + open_ok: +#if defined(__MINGW32__) + WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); +#endif + [super open]; +} + +- (void) close +{ + if (_currentStatus == NSStreamStatusNotOpen) + { + NSDebugMLog(@"Attempt to close unopened stream %@", self); + return; + } + if (_currentStatus == NSStreamStatusClosed) + { + NSDebugMLog(@"Attempt to close already closed stream %@", self); + return; + } + +#if defined(__MINGW32__) + if (_sibling && [_sibling streamStatus] != NSStreamStatusClosed) + { + /* + * Windows only permits a single event to be associated with a socket + * at any time, but the runloop system only allows an event handle to + * be added to the loop once, and we have two streams for each socket. + * So we use two events, one for each stream, and when one stream is + * closed, we must call WSAEventSelect to ensure that the event handle + * of the sibling is used to signal events from now on. + */ + WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); + shutdown(_sock, SD_RECEIVE); + WSAEventSelect(_sock, [_sibling _loopID], FD_ALL_EVENTS); + } + else + { + closesocket(_sock); + } + WSACloseEvent(_loopID); + [super close]; + _sock = INVALID_SOCKET; + _loopID = WSA_INVALID_EVENT; +#else + // read shutdown is ignored, because the other side may shutdown first. + if (!_sibling || [_sibling streamStatus] == NSStreamStatusClosed) + close((intptr_t)_loopID); + else + shutdown((intptr_t)_loopID, SHUT_RD); + [super close]; + _sock = -1; + _loopID = (void*)(intptr_t)-1; +#endif +} + +- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len +{ + int readLen; + + if (buffer == 0) + { + [NSException raise: NSInvalidArgumentException + format: @"null pointer for buffer"]; + } + if (len == 0) + { + [NSException raise: NSInvalidArgumentException + format: @"zero byte read requested"]; + } + + _events &= ~NSStreamEventHasBytesAvailable; + + if ([self streamStatus] == NSStreamStatusClosed) + { + return 0; + } + if ([self streamStatus] == NSStreamStatusAtEnd) + { + readLen = 0; + } + else + { +#if defined(__MINGW32__) + readLen = recv([self _sock], buffer, len, 0); +#else + readLen = read([self _sock], buffer, len); +#endif + } + if (socketError(readLen)) + { + if (_closing == YES) + { + /* If a read fails on a closing socket, + * we have reached the end of all data sent by + * the remote end before it shut down. + */ + [self _setClosing: NO]; + [self _setStatus: NSStreamStatusAtEnd]; + [self _sendEvent: NSStreamEventEndEncountered]; + readLen = 0; + } + else + { + if (socketWouldBlock()) + { + /* We need an event from the operating system + * to tell us we can start reading again. + */ + [self _setStatus: NSStreamStatusReading]; + } + else + { + [self _recordError]; + } + readLen = -1; + } + } + else if (readLen == 0) + { + [self _setStatus: NSStreamStatusAtEnd]; + [self _sendEvent: NSStreamEventEndEncountered]; + } + else + { + [self _setStatus: NSStreamStatusOpen]; + } + return readLen; +} + +- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len +{ + return NO; +} + +- (void) _dispatch +{ +#if defined(__MINGW32__) + AUTORELEASE(RETAIN(self)); + /* + * Windows only permits a single event to be associated with a socket + * at any time, but the runloop system only allows an event handle to + * be added to the loop once, and we have two streams for each socket. + * So we use two events, one for each stream, and the _dispatch method + * must handle things for both streams. + */ + if ([self streamStatus] == NSStreamStatusClosed) + { + /* + * It is possible the stream is closed yet recieving event because + * of not closed sibling + */ + NSAssert([_sibling streamStatus] != NSStreamStatusClosed, + @"Received event for closed stream"); + [_sibling _dispatch]; + } + else + { + WSANETWORKEVENTS events; + int error = 0; + int getReturn = -1; + + if (WSAEnumNetworkEvents(_sock, _loopID, &events) == SOCKET_ERROR) + { + error = WSAGetLastError(); + } +// else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self); + + if ([self streamStatus] == NSStreamStatusOpening) + { + [self _unschedule]; + if (error == 0) + { + unsigned len = sizeof(error); + + getReturn = getsockopt(_sock, SOL_SOCKET, SO_ERROR, + (char*)&error, &len); + } + + if (getReturn >= 0 && error == 0 + && (events.lNetworkEvents & FD_CONNECT)) + { // finish up the opening + _passive = YES; + [self open]; + // notify sibling + if (_sibling) + { + [_sibling open]; + [_sibling _sendEvent: NSStreamEventOpenCompleted]; + } + [self _sendEvent: NSStreamEventOpenCompleted]; + } + } + + if (error != 0) + { + errno = error; + [self _recordError]; + [_sibling _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + [_sibling _sendEvent: NSStreamEventErrorOccurred]; + } + else + { + if (events.lNetworkEvents & FD_WRITE) + { + NSAssert([_sibling _isOpened], NSInternalInconsistencyException); + /* Clear NSStreamStatusWriting if it was set */ + [_sibling _setStatus: NSStreamStatusOpen]; + } + + /* On winsock a socket is always writable unless it has had + * failure/closure or a write blocked and we have not been + * signalled again. + */ + while ([_sibling _unhandledData] == NO + && [_sibling hasSpaceAvailable]) + { + [_sibling _sendEvent: NSStreamEventHasSpaceAvailable]; + } + + if (events.lNetworkEvents & FD_READ) + { + [self _setStatus: NSStreamStatusOpen]; + while ([self hasBytesAvailable] + && [self _unhandledData] == NO) + { + [self _sendEvent: NSStreamEventHasBytesAvailable]; + } + } + + if (events.lNetworkEvents & FD_CLOSE) + { + [self _setClosing: YES]; + [_sibling _setClosing: YES]; + while ([self hasBytesAvailable] + && [self _unhandledData] == NO) + { + [self _sendEvent: NSStreamEventHasBytesAvailable]; + } + } + if (events.lNetworkEvents == 0) + { + [self _sendEvent: NSStreamEventHasBytesAvailable]; + } + } + } +#else + NSStreamEvent myEvent; + + if ([self streamStatus] == NSStreamStatusOpening) + { + int error; + int result; + socklen_t len = sizeof(error); + + AUTORELEASE(RETAIN(self)); + [self _unschedule]; + result = getsockopt([self _sock], SOL_SOCKET, SO_ERROR, &error, &len); + + if (result >= 0 && !error) + { // finish up the opening + myEvent = NSStreamEventOpenCompleted; + _passive = YES; + [self open]; + // notify sibling + [_sibling open]; + [_sibling _sendEvent: myEvent]; + } + else // must be an error + { + if (error) + errno = error; + [self _recordError]; + myEvent = NSStreamEventErrorOccurred; + [_sibling _recordError]; + [_sibling _sendEvent: myEvent]; + } + } + else if ([self streamStatus] == NSStreamStatusAtEnd) + { + myEvent = NSStreamEventEndEncountered; + } + else + { + [self _setStatus: NSStreamStatusOpen]; + myEvent = NSStreamEventHasBytesAvailable; + } + [self _sendEvent: myEvent]; +#endif +} + +#if defined(__MINGW32__) +- (BOOL) runLoopShouldBlock: (BOOL*)trigger +{ + *trigger = YES; + return YES; +} +#endif + +@end + + +@implementation GSSocketOutputStream + ++ (void) initialize +{ + if (self == [GSSocketOutputStream class]) + { + GSObjCAddClassBehavior(self, [GSSocketStream class]); + } +} + +- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len +{ + int writeLen; + + if (buffer == 0) + { + [NSException raise: NSInvalidArgumentException + format: @"null pointer for buffer"]; + } + if (len == 0) + { + [NSException raise: NSInvalidArgumentException + format: @"zero byte length write requested"]; + } + + _events &= ~NSStreamEventHasSpaceAvailable; + + if ([self streamStatus] == NSStreamStatusClosed) + { + return 0; + } + if ([self streamStatus] == NSStreamStatusAtEnd) + { + [self _sendEvent: NSStreamEventEndEncountered]; + return 0; + } + +#if defined(__MINGW32__) + writeLen = send([self _sock], buffer, len, 0); +#else + writeLen = write([self _sock], buffer, len); +#endif + + if (socketError(writeLen)) + { + if (_closing == YES) + { + /* If a write fails on a closing socket, + * we know the other end is no longer reading. + */ + [self _setClosing: NO]; + [self _setStatus: NSStreamStatusAtEnd]; + [self _sendEvent: NSStreamEventEndEncountered]; + writeLen = 0; + } + else + { + if (socketWouldBlock()) + { + /* We need an event from the operating system + * to tell us we can start writing again. + */ + [self _setStatus: NSStreamStatusWriting]; + } + else + { + [self _recordError]; + } + writeLen = -1; + } + } + else + { + [self _setStatus: NSStreamStatusOpen]; + } + return writeLen; +} + +- (void) open +{ + // could be opened because of sibling + if ([self _isOpened]) + return; + if (_passive || (_sibling && [_sibling _isOpened])) + goto open_ok; + // check sibling status, avoid double connect + if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening) + { + [self _setStatus: NSStreamStatusOpening]; + return; + } + else + { + int result; + + result = connect([self _sock], [self _peerAddr], [self _sockLen]); + if (socketError(result)) + { + if (!socketWouldBlock()) + { + [self _recordError]; + return; + } + /* + * Need to set the status first, so that the run loop can tell + * it needs to add the stream as waiting on writable, as an + * indication of opened + */ + [self _setStatus: NSStreamStatusOpening]; +#if defined(__MINGW32__) + WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); +#endif + if (NSCountMapTable(_loops) > 0) + { + [self _schedule]; + return; + } + else + { + NSRunLoop *r; + NSDate *d; + + /* The stream was not scheduled in any run loop, so we + * implement a blocking connect by running in the default + * run loop mode. + */ + r = [NSRunLoop currentRunLoop]; + d = [NSDate distantFuture]; + [r addStream: self mode: NSDefaultRunLoopMode]; + while ([r runMode: NSDefaultRunLoopMode beforeDate: d] == YES) + { + if (_currentStatus != NSStreamStatusOpening) + { + break; + } + } + [r removeStream: self mode: NSDefaultRunLoopMode]; + return; + } + } + } + + open_ok: +#if defined(__MINGW32__) + WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); +#endif + [super open]; +} + + +- (void) close +{ + if (_currentStatus == NSStreamStatusNotOpen) + { + NSDebugMLog(@"Attempt to close unopened stream %@", self); + return; + } + if (_currentStatus == NSStreamStatusClosed) + { + NSDebugMLog(@"Attempt to close already closed stream %@", self); + return; + } + +#if defined(__MINGW32__) + if (_sibling && [_sibling streamStatus] != NSStreamStatusClosed) + { + /* + * Windows only permits a single event to be associated with a socket + * at any time, but the runloop system only allows an event handle to + * be added to the loop once, and we have two streams for each socket. + * So we use two events, one for each stream, and when one stream is + * closed, we must call WSAEventSelect to ensure that the event handle + * of the sibling is used to signal events from now on. + */ + WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); + shutdown(_sock, SD_SEND); + WSAEventSelect(_sock, [_sibling _loopID], FD_ALL_EVENTS); + } + else + { + closesocket(_sock); + } + WSACloseEvent(_loopID); + [super close]; + _sock = INVALID_SOCKET; + _loopID = WSA_INVALID_EVENT; +#else + // read shutdown is ignored, because the other side may shutdown first. + if (!_sibling || [_sibling streamStatus] == NSStreamStatusClosed) + close((intptr_t)_loopID); + else + shutdown((intptr_t)_loopID, SHUT_WR); + [super close]; + _loopID = (void*)(intptr_t)-1; + _sock = -1; +#endif +} + +- (void) _dispatch +{ +#if defined(__MINGW32__) + AUTORELEASE(RETAIN(self)); + /* + * Windows only permits a single event to be associated with a socket + * at any time, but the runloop system only allows an event handle to + * be added to the loop once, and we have two streams for each socket. + * So we use two events, one for each stream, and the _dispatch method + * must handle things for both streams. + */ + if ([self streamStatus] == NSStreamStatusClosed) + { + /* + * It is possible the stream is closed yet recieving event because + * of not closed sibling + */ + NSAssert([_sibling streamStatus] != NSStreamStatusClosed, + @"Received event for closed stream"); + [_sibling _dispatch]; + } + else + { + WSANETWORKEVENTS events; + int error = 0; + int getReturn = -1; + + if (WSAEnumNetworkEvents(_sock, _loopID, &events) == SOCKET_ERROR) + { + error = WSAGetLastError(); + } +// else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self); + + if ([self streamStatus] == NSStreamStatusOpening) + { + [self _unschedule]; + if (error == 0) + { + unsigned len = sizeof(error); + + getReturn = getsockopt(_sock, SOL_SOCKET, SO_ERROR, + (char*)&error, &len); + } + + if (getReturn >= 0 && error == 0 + && (events.lNetworkEvents & FD_CONNECT)) + { // finish up the opening + events.lNetworkEvents ^= FD_CONNECT; + _passive = YES; + [self open]; + // notify sibling + if (_sibling) + { + [_sibling open]; + [_sibling _sendEvent: NSStreamEventOpenCompleted]; + } + [self _sendEvent: NSStreamEventOpenCompleted]; + } + } + + if (error != 0) + { + errno = error; + [self _recordError]; + [_sibling _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + [_sibling _sendEvent: NSStreamEventErrorOccurred]; + } + else + { + if (events.lNetworkEvents & FD_WRITE) + { + /* Clear NSStreamStatusWriting if it was set */ + [self _setStatus: NSStreamStatusOpen]; + } + + /* On winsock a socket is always writable unless it has had + * failure/closure or a write blocked and we have not been + * signalled again. + */ + while ([self _unhandledData] == NO && [self hasSpaceAvailable]) + { + [self _sendEvent: NSStreamEventHasSpaceAvailable]; + } + + if (events.lNetworkEvents & FD_READ) + { + [_sibling _setStatus: NSStreamStatusOpen]; + while ([_sibling hasBytesAvailable] + && [_sibling _unhandledData] == NO) + { + [_sibling _sendEvent: NSStreamEventHasBytesAvailable]; + } + } + if (events.lNetworkEvents & FD_CLOSE) + { + [self _setClosing: YES]; + [_sibling _setClosing: YES]; + while ([_sibling hasBytesAvailable] + && [_sibling _unhandledData] == NO) + { + [_sibling _sendEvent: NSStreamEventHasBytesAvailable]; + } + } + if (events.lNetworkEvents == 0) + { + [self _sendEvent: NSStreamEventHasSpaceAvailable]; + } + } + } +#else + NSStreamEvent myEvent; + + if ([self streamStatus] == NSStreamStatusOpening) + { + int error; + socklen_t len = sizeof(error); + int result; + + AUTORELEASE(RETAIN(self)); + [self _schedule]; + result + = getsockopt((intptr_t)_loopID, SOL_SOCKET, SO_ERROR, &error, &len); + if (result >= 0 && !error) + { // finish up the opening + myEvent = NSStreamEventOpenCompleted; + _passive = YES; + [self open]; + // notify sibling + [_sibling open]; + [_sibling _sendEvent: myEvent]; + } + else // must be an error + { + if (error) + errno = error; + [self _recordError]; + myEvent = NSStreamEventErrorOccurred; + [_sibling _recordError]; + [_sibling _sendEvent: myEvent]; + } + } + else if ([self streamStatus] == NSStreamStatusAtEnd) + { + myEvent = NSStreamEventEndEncountered; + } + else + { + [self _setStatus: NSStreamStatusOpen]; + myEvent = NSStreamEventHasSpaceAvailable; + } + [self _sendEvent: myEvent]; +#endif +} + +#if defined(__MINGW32__) +- (BOOL) runLoopShouldBlock: (BOOL*)trigger +{ + *trigger = YES; + if ([self _unhandledData] == YES && [self streamStatus] == NSStreamStatusOpen) + { + /* In winsock, a writable status is only signalled if an earlier + * write failed (because it would block), so we must simulate the + * writable event by having the run loop trigger without blocking. + */ + return NO; + } + return YES; +} +#endif + +@end + + +@class GSLocalServerStream; + +@implementation GSServerStream + ++ (id) serverStreamToAddr: (NSString*)addr port: (int)port +{ + GSServerStream *s; + + // try inet first, then inet6 + s = [[GSInetServerStream alloc] initToAddr: addr port: port]; + if (!s) + s = [[GSInet6ServerStream alloc] initToAddr: addr port: port]; + return AUTORELEASE(s); +} + ++ (id) serverStreamToAddr: (NSString*)addr +{ + return AUTORELEASE([[GSLocalServerStream alloc] initToAddr: addr]); +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + RELEASE(self); + // try inet first, then inet6 + self = [[GSInetServerStream alloc] initToAddr: addr port: port]; + if (!self) + self = [[GSInet6ServerStream alloc] initToAddr: addr port: port]; + return self; +} + +- (id) initToAddr: (NSString*)addr +{ + RELEASE(self); + return [[GSLocalServerStream alloc] initToAddr: addr]; +} + +- (void) acceptWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + [self subclassResponsibility: _cmd]; +} + +@end + +@implementation GSAbstractServerStream + ++ (void) initialize +{ + if (self == [GSAbstractServerStream class]) + { + GSObjCAddClassBehavior(self, [GSStream class]); + } +} + +@end + +@implementation GSSocketServerStream + ++ (void) initialize +{ + if (self == [GSSocketServerStream class]) + { + GSObjCAddClassBehavior(self, [GSSocketStream class]); + } +} + +- (Class) _inputStreamClass +{ + [self subclassResponsibility: _cmd]; + return Nil; +} + +- (Class) _outputStreamClass +{ + [self subclassResponsibility: _cmd]; + return Nil; +} + +- (struct sockaddr*) _serverAddr +{ + [self subclassResponsibility: _cmd]; + return 0; +} + +#define SOCKET_BACKLOG 256 + +- (void) open +{ + int bindReturn; + int listenReturn; + +#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! + */ + int status = 1; + + setsockopt([self _sock], SOL_SOCKET, SO_REUSEADDR, + (char *)&status, sizeof(status)); +#endif + + bindReturn = bind([self _sock], [self _serverAddr], [self _sockLen]); + if (socketError(bindReturn)) + { + [self _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + return; + } + listenReturn = listen([self _sock], SOCKET_BACKLOG); + if (socketError(listenReturn)) + { + [self _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + return; + } +#if defined(__MINGW32__) + WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); +#endif + [super open]; +} + +- (void) close +{ +#if defined(__MINGW32__) + if (_loopID != WSA_INVALID_EVENT) + { + WSACloseEvent(_loopID); + } + if (_sock != INVALID_SOCKET) + { + closesocket(_sock); + _sock = INVALID_SOCKET; + [super close]; + _loopID = WSA_INVALID_EVENT; + } +#else + if (_loopID != (void*)(intptr_t)-1) + { + close((intptr_t)_loopID); + [super close]; + _loopID = (void*)(intptr_t)-1; + } +#endif +} + +- (void) acceptWithInputStream: (NSInputStream **)inputStream + outputStream: (NSOutputStream **)outputStream +{ + GSSocketStream *ins = AUTORELEASE([[self _inputStreamClass] new]); + GSSocketStream *outs = AUTORELEASE([[self _outputStreamClass] new]); + socklen_t len = [ins _sockLen]; + int acceptReturn = accept([self _sock], [ins _peerAddr], &len); + + _events &= ~NSStreamEventHasBytesAvailable; + if (socketError(acceptReturn)) + { // test for real error + if (!socketWouldBlock()) + { + [self _recordError]; + } + ins = nil; + outs = nil; + } + else + { + // no need to connect again + [ins _setPassive: YES]; + [outs _setPassive: YES]; + // copy the addr to outs + memcpy([outs _peerAddr], [ins _peerAddr], len); + [ins _setSock: acceptReturn]; + [outs _setSock: acceptReturn]; + } + if (inputStream) + { + [ins _setSibling: outs]; + *inputStream = (NSInputStream*)ins; + } + if (outputStream) + { + [outs _setSibling: ins]; + *outputStream = (NSOutputStream*)outs; + } +} + +- (void) _dispatch +{ +#if defined(__MINGW32__) + WSANETWORKEVENTS events; + + if (WSAEnumNetworkEvents(_sock, _loopID, &events) == SOCKET_ERROR) + { + errno = WSAGetLastError(); + [self _recordError]; + [self _sendEvent: NSStreamEventErrorOccurred]; + } + else if (events.lNetworkEvents & FD_ACCEPT) + { + events.lNetworkEvents ^= FD_ACCEPT; + [self _setStatus: NSStreamStatusReading]; + [self _sendEvent: NSStreamEventHasBytesAvailable]; + } +#else + NSStreamEvent myEvent; + + [self _setStatus: NSStreamStatusOpen]; + myEvent = NSStreamEventHasBytesAvailable; + [self _sendEvent: myEvent]; +#endif +} + +@end + + + + + + +static id propertyForInet4Stream(int descriptor, NSString *key) +{ + struct sockaddr_in sin; + unsigned size = sizeof(sin); + id result = nil; + + if ([key isEqualToString: GSStreamLocalAddressKey]) + { + if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) + { + result = [NSString stringWithUTF8String: + (char*)inet_ntoa(sin.sin_addr)]; + } + } + else if ([key isEqualToString: GSStreamLocalPortKey]) + { + if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) + { + result = [NSString stringWithFormat: @"%d", + (int)GSSwapBigI16ToHost(sin.sin_port)]; + } + } + else if ([key isEqualToString: GSStreamRemoteAddressKey]) + { + if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) + { + result = [NSString stringWithUTF8String: + (char*)inet_ntoa(sin.sin_addr)]; + } + } + else if ([key isEqualToString: GSStreamRemotePortKey]) + { + if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) + { + result = [NSString stringWithFormat: @"%d", + (int)GSSwapBigI16ToHost(sin.sin_port)]; + } + } + return result; +} +#if defined(AF_INET6) +static id propertyForInet6Stream(int descriptor, NSString *key) +{ + struct sockaddr_in6 sin; + unsigned size = sizeof(sin); + id result = nil; + + if ([key isEqualToString: GSStreamLocalAddressKey]) + { + if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) + { + char buf[INET6_ADDRSTRLEN+1]; + + if (inet_ntop(AF_INET6, &(sin.sin6_addr), buf, INET6_ADDRSTRLEN) == 0) + { + buf[INET6_ADDRSTRLEN] = '\0'; + result = [NSString stringWithUTF8String: buf]; + } + } + } + else if ([key isEqualToString: GSStreamLocalPortKey]) + { + if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) + { + result = [NSString stringWithFormat: @"%d", + (int)GSSwapBigI16ToHost(sin.sin6_port)]; + } + } + else if ([key isEqualToString: GSStreamRemoteAddressKey]) + { + if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) + { + char buf[INET6_ADDRSTRLEN+1]; + + if (inet_ntop(AF_INET6, &(sin.sin6_addr), buf, INET6_ADDRSTRLEN) == 0) + { + buf[INET6_ADDRSTRLEN] = '\0'; + result = [NSString stringWithUTF8String: buf]; + } + } + } + else if ([key isEqualToString: GSStreamRemotePortKey]) + { + if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) + { + result = [NSString stringWithFormat: @"%d", + (int)GSSwapBigI16ToHost(sin.sin6_port)]; + } + } + return result; +} +#endif + +@implementation GSInetInputStream + +- (socklen_t) _sockLen +{ + return sizeof(struct sockaddr_in); +} + +- (struct sockaddr*) _peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + if ((self = [super init]) != nil) + { + _peerAddr.sin_family = AF_INET; + _peerAddr.sin_port = GSSwapHostI16ToBig(port); + ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + } + return self; +} + +- (id) propertyForKey: (NSString *)key +{ + id result = propertyForInet4Stream((intptr_t)_loopID, key); + + if (result == nil) + { + result = [super propertyForKey: key]; + } + return result; +} + +@end + +@implementation GSInet6InputStream +#if defined(AF_INET6) +- (socklen_t) _sockLen +{ + return sizeof(struct sockaddr_in6); +} + +- (struct sockaddr*) _peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + if ((self = [super init]) != nil) + { + _peerAddr.sin6_family = AF_INET6; + _peerAddr.sin6_port = GSSwapHostI16ToBig(port); + ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + } + return self; +} + +- (id) propertyForKey: (NSString *)key +{ + id result = propertyForInet6Stream((intptr_t)_loopID, key); + + if (result == nil) + { + result = [super propertyForKey: key]; + } + return result; +} + +#else +- (id) initToAddr: (NSString*)addr port: (int)port +{ + RELEASE(self); + return nil; +} +#endif +@end + +@implementation GSInetOutputStream + +- (socklen_t) _sockLen +{ + return sizeof(struct sockaddr_in); +} + +- (struct sockaddr*) _peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + if ((self = [super init]) != nil) + { + _peerAddr.sin_family = AF_INET; + _peerAddr.sin_port = GSSwapHostI16ToBig(port); + ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + } + return self; +} + +- (id) propertyForKey: (NSString *)key +{ + id result = propertyForInet4Stream((intptr_t)_loopID, key); + + if (result == nil) + { + result = [super propertyForKey: key]; + } + return result; +} + +@end + +@implementation GSInet6OutputStream +#if defined(AF_INET6) +- (socklen_t) _sockLen +{ + return sizeof(struct sockaddr_in6); +} + +- (struct sockaddr*) _peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + if ((self = [super init]) != nil) + { + _peerAddr.sin6_family = AF_INET6; + _peerAddr.sin6_port = GSSwapHostI16ToBig(port); + ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + } + return self; +} + +- (id) propertyForKey: (NSString *)key +{ + id result = propertyForInet6Stream((intptr_t)_loopID, key); + + if (result == nil) + { + result = [super propertyForKey: key]; + } + return result; +} + +#else +- (id) initToAddr: (NSString*)addr port: (int)port +{ + RELEASE(self); + return nil; +} +#endif +@end + +@implementation GSInetServerStream + +- (Class) _inputStreamClass +{ + return [GSInetInputStream class]; +} + +- (Class) _outputStreamClass +{ + return [GSInetOutputStream class]; +} + +- (socklen_t) _sockLen +{ + return sizeof(struct sockaddr_in); +} + +- (struct sockaddr*) _serverAddr +{ + return (struct sockaddr*)&_serverAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + if ((self = [super init]) != nil) + { + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + _serverAddr.sin_family = AF_INET; + _serverAddr.sin_port = GSSwapHostI16ToBig(port); + if (addr_c == 0) + { + addr_c = "0.0.0.0"; /* Bind on all addresses */ + } + ptonReturn = inet_pton(AF_INET, addr_c, &(_serverAddr.sin_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + else + { + SOCKET s; + + s = socket(AF_INET, SOCK_STREAM, 0); + if (BADSOCKET(s)) + { + DESTROY(self); + } + else + { + [self _setSock: s]; + } + } + } + return self; +} + +@end + +@implementation GSInet6ServerStream +#if defined(AF_INET6) +- (Class) _inputStreamClass +{ + return [GSInet6InputStream class]; +} + +- (Class) _outputStreamClass +{ + return [GSInet6OutputStream class]; +} + +- (socklen_t) _sockLen +{ + return sizeof(struct sockaddr_in6); +} + +- (struct sockaddr*) _serverAddr +{ + return (struct sockaddr*)&_serverAddr; +} + +- (id) initToAddr: (NSString*)addr port: (int)port +{ + if ([super init] != nil) + { + int ptonReturn; + const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; + + _serverAddr.sin6_family = AF_INET6; + _serverAddr.sin6_port = GSSwapHostI16ToBig(port); + if (addr_c == 0) + { + addr_c = "0:0:0:0:0:0:0:0"; /* Bind on all addresses */ + } + ptonReturn = inet_pton(AF_INET6, addr_c, &(_serverAddr.sin6_addr)); + if (ptonReturn == 0) // error + { + DESTROY(self); + } + else + { + SOCKET s; + + s = socket(AF_INET6, SOCK_STREAM, 0); + if (BADSOCKET(s)) + { + DESTROY(self); + } + else + { + [self _setSock: s]; + } + } + } + return self; +} +#else +- (id) initToAddr: (NSString*)addr port: (int)port +{ + RELEASE(self); + return nil; +} +#endif +@end + diff --git a/Source/unix/NSStream.m b/Source/unix/NSStream.m index 24c9af030..bfccce786 100644 --- a/Source/unix/NSStream.m +++ b/Source/unix/NSStream.m @@ -37,119 +37,6 @@ #include "../GSStream.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef AF_LOCAL -#define AF_LOCAL AF_UNIX -#endif -#ifndef PF_LOCAL -#define PF_LOCAL PF_UNIX -#endif - -#ifndef socklen_t -#define socklen_t uint32_t -#endif - -static id propertyForInet4Stream(int descriptor, NSString *key) -{ - struct sockaddr_in sin; - unsigned size = sizeof(sin); - id result = nil; - - if ([key isEqualToString: GSStreamLocalAddressKey]) - { - if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithUTF8String: - (char*)inet_ntoa(sin.sin_addr)]; - } - } - else if ([key isEqualToString: GSStreamLocalPortKey]) - { - if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithFormat: @"%d", - (int)GSSwapBigI16ToHost(sin.sin_port)]; - } - } - else if ([key isEqualToString: GSStreamRemoteAddressKey]) - { - if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithUTF8String: - (char*)inet_ntoa(sin.sin_addr)]; - } - } - else if ([key isEqualToString: GSStreamRemotePortKey]) - { - if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithFormat: @"%d", - (int)GSSwapBigI16ToHost(sin.sin_port)]; - } - } - return result; -} -#if defined(AF_INET6) -static id propertyForInet6Stream(int descriptor, NSString *key) -{ - struct sockaddr_in6 sin; - unsigned size = sizeof(sin); - id result = nil; - - if ([key isEqualToString: GSStreamLocalAddressKey]) - { - if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - char buf[INET6_ADDRSTRLEN+1]; - - if (inet_ntop(AF_INET6, &(sin.sin6_addr), buf, INET6_ADDRSTRLEN) == 0) - { - buf[INET6_ADDRSTRLEN] = '\0'; - result = [NSString stringWithUTF8String: buf]; - } - } - } - else if ([key isEqualToString: GSStreamLocalPortKey]) - { - if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithFormat: @"%d", - (int)GSSwapBigI16ToHost(sin.sin6_port)]; - } - } - else if ([key isEqualToString: GSStreamRemoteAddressKey]) - { - if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - char buf[INET6_ADDRSTRLEN+1]; - - if (inet_ntop(AF_INET6, &(sin.sin6_addr), buf, INET6_ADDRSTRLEN) == 0) - { - buf[INET6_ADDRSTRLEN] = '\0'; - result = [NSString stringWithUTF8String: buf]; - } - } - } - else if ([key isEqualToString: GSStreamRemotePortKey]) - { - if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithFormat: @"%d", - (int)GSSwapBigI16ToHost(sin.sin6_port)]; - } - } - return result; -} -#endif - /** * The concrete subclass of NSInputStream that reads from a file */ @@ -160,66 +47,6 @@ static id propertyForInet6Stream(int descriptor, NSString *key) } @end -/** - * The abstract subclass of NSInputStream that reads from a socket - */ -@interface GSSocketInputStream : GSInputStream -{ -@protected - GSOutputStream *_sibling; - BOOL _passive; /* YES means already connected */ -} - -/** - * get the length of the socket addr - */ -- (socklen_t) sockLen; - -/** - * get the remote sockaddr - */ -- (struct sockaddr*) peerAddr; - -/** - * setter for sibling - */ -- (void) setSibling: (GSOutputStream*)sibling; - -/** - * setter for passive - */ -- (void) setPassive: (BOOL)passive; - -@end - -@interface GSInetInputStream : GSSocketInputStream -{ - @private - struct sockaddr_in _peerAddr; -} - -/** - * the designated initializer - */ -- (id) initToAddr: (NSString*)addr port: (int)port; - -@end - -@interface GSInet6InputStream : GSSocketInputStream -{ - @private -#if defined(AF_INET6) - struct sockaddr_in6 _peerAddr; -#endif -} - -/** - * the designated initializer - */ -- (id) initToAddr: (NSString*)addr port: (int)port; - -@end - @interface GSLocalInputStream : GSSocketInputStream { @private @@ -244,66 +71,6 @@ static id propertyForInet6Stream(int descriptor, NSString *key) } @end -/** - * The concrete subclass of NSOutputStream that writes to a socket - */ -@interface GSSocketOutputStream : GSOutputStream -{ -@protected - GSInputStream *_sibling; - BOOL _passive; /* YES means already connected */ -} - -/** - * get the length of the socket addr - */ -- (socklen_t) sockLen; - -/** - * get the sockaddr - */ -- (struct sockaddr*) peerAddr; - -/** - * setter for sibling - */ -- (void) setSibling: (GSInputStream*)sibling; - -/** - * setter for passive - */ -- (void) setPassive: (BOOL)passive; - -@end - -@interface GSInetOutputStream : GSSocketOutputStream -{ - @private - struct sockaddr_in _peerAddr; -} - -/** - * the designated initializer - */ -- (id) initToAddr: (NSString*)addr port: (int)port; - -@end - -@interface GSInet6OutputStream : GSSocketOutputStream -{ - @private -#if defined(AF_INET6) - struct sockaddr_in6 _peerAddr; -#endif -} - -/** - * the designated initializer - */ -- (id) initToAddr: (NSString*)addr port: (int)port; - -@end - @interface GSLocalOutputStream : GSSocketOutputStream { @private @@ -317,47 +84,6 @@ static id propertyForInet6Stream(int descriptor, NSString *key) @end -/** - * The concrete subclass of NSServerStream that accept connection from a socket - */ -@interface GSSocketServerStream : GSAbstractServerStream -/** - * return the class of the inputStream associated with this - * type of serverStream. - */ -- (Class) _inputStreamClass; -/** - * return the class of the outputStream associated with this - * type of serverStream. - */ -- (Class) _outputStreamClass; -/** - * get the length of the socket addr - */ -- (socklen_t) sockLen; -/** - * get the sockaddr - */ -- (struct sockaddr*) serverAddr; - -@end - -@interface GSInetServerStream : GSSocketServerStream -{ - @private - struct sockaddr_in _serverAddr; -} -@end - -@interface GSInet6ServerStream : GSSocketServerStream -{ - @private -#if defined(AF_INET6) - struct sockaddr_in6 _serverAddr; -#endif -} -@end - @interface GSLocalServerStream : GSSocketServerStream { @private @@ -365,15 +91,6 @@ static id propertyForInet6Stream(int descriptor, NSString *key) } @end -/** - * set the file descriptor to non-blocking - */ -static void setNonblocking(int fd) -{ - int flags = fcntl(fd, F_GETFL, 0); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); -} - @implementation GSFileInputStream - (id) initWithFileAtPath: (NSString *)path @@ -491,309 +208,9 @@ static void setNonblocking(int fd) } @end -@implementation GSSocketInputStream - -- (socklen_t) sockLen -{ - [self subclassResponsibility: _cmd]; - return 0; -} - -- (struct sockaddr*) peerAddr -{ - [self subclassResponsibility: _cmd]; - return NULL; -} - -- (void) setSibling: (GSOutputStream*)sibling -{ - _sibling = sibling; -} - -- (void) setPassive: (BOOL)passive -{ - _passive = passive; -} - -- (id) init -{ - if ((self = [super init]) != nil) - { - // so that unopened access will fail - _sibling = nil; - _passive = NO; - } - return self; -} - -- (void) dealloc -{ - if ([self _isOpened]) - { - [self close]; - } - [(GSSocketOutputStream*)_sibling setSibling: nil]; - _sibling = nil; - [super dealloc]; -} - -- (void) open -{ - // could be opened because of sibling - if ([self _isOpened]) - return; - if (_passive || (_sibling && [_sibling _isOpened])) - goto open_ok; - // check sibling status, avoid double connect - if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening) - { - [self _setStatus: NSStreamStatusOpening]; - return; - } - else - { - int result; - - if (NSCountMapTable(_loops) > 0) - { - setNonblocking((intptr_t)_loopID); - } - result = connect((intptr_t)_loopID, [self peerAddr], [self sockLen]); - if (result < 0) - { - if (errno == EINPROGRESS && NSCountMapTable(_loops) > 0) - { - /* - * Need to set the status first, so that the run loop can tell - * it needs to add the stream as waiting on writable, as an - * indication of opened - */ - [self _setStatus: NSStreamStatusOpening]; - [self _schedule]; - return; - } - [self _recordError]; - return; - } - } - - open_ok: - // put itself to the runloop - [super open]; - setNonblocking((intptr_t)_loopID); -} - -- (void) close -{ - // read shutdown is ignored, because the other side may shutdown first. - if (!_sibling || [_sibling streamStatus] == NSStreamStatusClosed) - close((intptr_t)_loopID); - else - shutdown((intptr_t)_loopID, SHUT_RD); - [super close]; -} - -- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len -{ - int readLen; - - if (buffer == 0) - { - [NSException raise: NSInvalidArgumentException - format: @"null pointer for buffer"]; - } - if (len == 0) - { - [NSException raise: NSInvalidArgumentException - format: @"zero byte read requested"]; - } - - _events &= ~NSStreamEventHasBytesAvailable; - - if ([self streamStatus] == NSStreamStatusClosed) - { - return 0; - } - - readLen = read((intptr_t)_loopID, buffer, len); - if (readLen < 0 && errno != EAGAIN && errno != EINTR) - { - [self _recordError]; - readLen = -1; - } - else if (readLen == 0) - { - [self _setStatus: NSStreamStatusAtEnd]; - } - return readLen; -} - -- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len -{ - return NO; -} - -- (BOOL) hasBytesAvailable -{ - if ([self _isOpened] && [self streamStatus] != NSStreamStatusAtEnd) - return YES; - return NO; -} - -- (void) _dispatch -{ - NSStreamEvent myEvent; - - if ([self streamStatus] == NSStreamStatusOpening) - { - int error; - int result; - socklen_t len = sizeof(error); - - AUTORELEASE(RETAIN(self)); - [self _unschedule]; - result - = getsockopt((intptr_t)_loopID, SOL_SOCKET, SO_ERROR, &error, &len); - - if (result >= 0 && !error) - { // finish up the opening - myEvent = NSStreamEventOpenCompleted; - _passive = YES; - [self open]; - // notify sibling - [_sibling open]; - [_sibling _sendEvent: myEvent]; - } - else // must be an error - { - if (error) - errno = error; - [self _recordError]; - myEvent = NSStreamEventErrorOccurred; - [_sibling _recordError]; - [_sibling _sendEvent: myEvent]; - } - } - else if ([self streamStatus] == NSStreamStatusAtEnd) - { - myEvent = NSStreamEventEndEncountered; - } - else - { - [self _setStatus: NSStreamStatusOpen]; - myEvent = NSStreamEventHasBytesAvailable; - } - [self _sendEvent: myEvent]; -} - -@end - -@implementation GSInetInputStream - -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_in); -} - -- (struct sockaddr*) peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - - int ptonReturn; - const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; - - if ((self = [super init]) != nil) - { - _peerAddr.sin_family = AF_INET; - _peerAddr.sin_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); - if (ptonReturn == 0) // error - { - DESTROY(self); - } - } - return self; -} - -- (id) propertyForKey: (NSString *)key -{ - id result = propertyForInet4Stream((intptr_t)_loopID, key); - - if (result == nil) - { - result = [super propertyForKey: key]; - } - return result; -} - -@end - -@implementation GSInet6InputStream -#if defined(AF_INET6) -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_in6); -} - -- (struct sockaddr*) peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - int ptonReturn; - const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; - - if ((self = [super init]) != nil) - { - _peerAddr.sin6_family = AF_INET6; - _peerAddr.sin6_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); - if (ptonReturn == 0) // error - { - DESTROY(self); - } - } - return self; -} - -- (id) propertyForKey: (NSString *)key -{ - id result = propertyForInet6Stream((intptr_t)_loopID, key); - - if (result == nil) - { - result = [super propertyForKey: key]; - } - return result; -} - -#else -- (id) initToAddr: (NSString*)addr port: (int)port -{ - RELEASE(self); - return nil; -} -#endif -@end @implementation GSLocalInputStream -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_un); -} - -- (struct sockaddr*) peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - - (id) initToAddr: (NSString*)addr { const char* real_addr = [addr fileSystemRepresentation]; @@ -813,6 +230,16 @@ static void setNonblocking(int fd) return self; } +- (struct sockaddr*) _peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (socklen_t) _sockLen +{ + return sizeof(struct sockaddr_un); +} + @end @implementation GSFileOutputStream @@ -927,297 +354,9 @@ static void setNonblocking(int fd) } @end -@implementation GSSocketOutputStream - -- (socklen_t) sockLen -{ - [self subclassResponsibility: _cmd]; - return 0; -} - -- (struct sockaddr*) peerAddr -{ - [self subclassResponsibility: _cmd]; - return NULL; -} - -- (void) setSibling: (GSInputStream*)sibling -{ - _sibling = sibling; -} - -- (void) setPassive: (BOOL)passive -{ - _passive = passive; -} - -- (id) init -{ - if ((self = [super init]) != nil) - { - _sibling = nil; - _passive = NO; - } - return self; -} - -- (void) dealloc -{ - if ([self _isOpened]) - { - [self close]; - } - [(GSSocketInputStream*)_sibling setSibling: nil]; - _sibling = nil; - [super dealloc]; -} - -- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len -{ - int writeLen; - - if (buffer == 0) - { - [NSException raise: NSInvalidArgumentException - format: @"null pointer for buffer"]; - } - if (len == 0) - { - [NSException raise: NSInvalidArgumentException - format: @"zero byte length write requested"]; - } - - _events &= ~NSStreamEventHasSpaceAvailable; - - if ([self streamStatus] == NSStreamStatusClosed) - { - return 0; - } - - writeLen = write((intptr_t)_loopID, buffer, len); - if (writeLen < 0 && errno != EAGAIN && errno != EINTR) - [self _recordError]; - return writeLen; -} - -- (BOOL) hasSpaceAvailable -{ - if ([self _isOpened]) - return YES; - return NO; -} - -- (void) open -{ - // could be opened because of sibling - if ([self _isOpened]) - return; - if (_passive || (_sibling && [_sibling _isOpened])) - goto open_ok; - // check sibling status, avoid double connect - if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening) - { - [self _setStatus: NSStreamStatusOpening]; - return; - } - else - { - int result; - - if (NSCountMapTable(_loops) > 0) - { - setNonblocking((intptr_t)_loopID); - } - result = connect((intptr_t)_loopID, [self peerAddr], [self sockLen]); - if (result < 0) - { - if (errno == EINPROGRESS && NSCountMapTable(_loops) > 0) - { - /* - * Need to set the status first, so that the run loop can tell - * it needs to add the stream as waiting on writable, as an - * indication of opened - */ - [self _setStatus: NSStreamStatusOpening]; - [self _unschedule]; - return; - } - [self _recordError]; - return; - } - } - - open_ok: - // put itself to the runloop - [super open]; - setNonblocking((intptr_t)_loopID); -} - -- (void) close -{ - // shutdown may fail (broken pipe). Record it. - int closeReturn; - if (!_sibling || [_sibling streamStatus]==NSStreamStatusClosed) - closeReturn = close((intptr_t)_loopID); - else - closeReturn = shutdown((intptr_t)_loopID, SHUT_WR); - if (closeReturn < 0) - [self _recordError]; - [super close]; -} - -- (void) _dispatch -{ - NSStreamEvent myEvent; - - if ([self streamStatus] == NSStreamStatusOpening) - { - int error; - socklen_t len = sizeof(error); - int result; - - AUTORELEASE(RETAIN(self)); - [self _schedule]; - result - = getsockopt((intptr_t)_loopID, SOL_SOCKET, SO_ERROR, &error, &len); - if (result >= 0 && !error) - { // finish up the opening - myEvent = NSStreamEventOpenCompleted; - _passive = YES; - [self open]; - // notify sibling - [_sibling open]; - [_sibling _sendEvent: myEvent]; - } - else // must be an error - { - if (error) - errno = error; - [self _recordError]; - myEvent = NSStreamEventErrorOccurred; - [_sibling _recordError]; - [_sibling _sendEvent: myEvent]; - } - } - else if ([self streamStatus] == NSStreamStatusAtEnd) - { - myEvent = NSStreamEventEndEncountered; - } - else - { - [self _setStatus: NSStreamStatusOpen]; - myEvent = NSStreamEventHasSpaceAvailable; - } - [self _sendEvent: myEvent]; -} - -@end - -@implementation GSInetOutputStream - -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_in); -} - -- (struct sockaddr*) peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - int ptonReturn; - const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; - - if ((self = [super init]) != nil) - { - _peerAddr.sin_family = AF_INET; - _peerAddr.sin_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET, addr_c, &(_peerAddr.sin_addr)); - if (ptonReturn == 0) // error - { - DESTROY(self); - } - } - return self; -} - -- (id) propertyForKey: (NSString *)key -{ - id result = propertyForInet4Stream((intptr_t)_loopID, key); - - if (result == nil) - { - result = [super propertyForKey: key]; - } - return result; -} - -@end - -@implementation GSInet6OutputStream -#if defined(AF_INET6) -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_in6); -} - -- (struct sockaddr*) peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - int ptonReturn; - const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; - - if ((self = [super init]) != nil) - { - _peerAddr.sin6_family = AF_INET6; - _peerAddr.sin6_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET6, addr_c, &(_peerAddr.sin6_addr)); - if (ptonReturn == 0) // error - { - DESTROY(self); - } - } - return self; -} - -- (id) propertyForKey: (NSString *)key -{ - id result = propertyForInet6Stream((intptr_t)_loopID, key); - - if (result == nil) - { - result = [super propertyForKey: key]; - } - return result; -} - -#else -- (id) initToAddr: (NSString*)addr port: (int)port -{ - RELEASE(self); - return nil; -} -#endif -@end @implementation GSLocalOutputStream -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_un); -} - -- (struct sockaddr*) peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - - (id) initToAddr: (NSString*)addr { const char* real_addr = [addr fileSystemRepresentation]; @@ -1237,6 +376,16 @@ static void setNonblocking(int fd) return self; } +- (struct sockaddr*) _peerAddr +{ + return (struct sockaddr*)&_peerAddr; +} + +- (socklen_t) sockLen +{ + return sizeof(struct sockaddr_un); +} + @end @implementation NSStream @@ -1247,8 +396,8 @@ static void setNonblocking(int fd) outputStream: (NSOutputStream **)outputStream { NSString *address = host ? (id)[host address] : (id)@"127.0.0.1"; - GSSocketInputStream *ins = nil; - GSSocketOutputStream *outs = nil; + GSSocketStream *ins = nil; + GSSocketStream *outs = nil; int sock; // try ipv4 first @@ -1278,13 +427,13 @@ static void setNonblocking(int fd) [outs _setLoopID: (void*)(intptr_t)sock]; if (inputStream) { - [ins setSibling: outs]; - *inputStream = ins; + [ins _setSibling: outs]; + *inputStream = (NSInputStream*)ins; } if (outputStream) { - [outs setSibling: ins]; - *outputStream = outs; + [outs _setSibling: ins]; + *outputStream = (NSOutputStream*)outs; } } @@ -1292,8 +441,8 @@ static void setNonblocking(int fd) inputStream: (NSInputStream **)inputStream outputStream: (NSOutputStream **)outputStream { - GSSocketInputStream *ins = nil; - GSSocketOutputStream *outs = nil; + GSSocketStream *ins = nil; + GSSocketStream *outs = nil; int sock; ins = AUTORELEASE([[GSLocalInputStream alloc] initToAddr: path]); @@ -1305,13 +454,13 @@ static void setNonblocking(int fd) [outs _setLoopID: (void*)(intptr_t)sock]; if (inputStream) { - [ins setSibling: outs]; - *inputStream = ins; + [ins _setSibling: outs]; + *inputStream = (NSInputStream*)ins; } if (outputStream) { - [outs setSibling: ins]; - *outputStream = outs; + [outs _setSibling: ins]; + *outputStream = (NSOutputStream*)outs; } return; } @@ -1319,8 +468,8 @@ static void setNonblocking(int fd) + (void) pipeWithInputStream: (NSInputStream **)inputStream outputStream: (NSOutputStream **)outputStream { - GSSocketInputStream *ins = nil; - GSSocketOutputStream *outs = nil; + GSSocketStream *ins = nil; + GSSocketStream *outs = nil; int fds[2]; int pipeReturn; @@ -1333,12 +482,12 @@ static void setNonblocking(int fd) [ins _setLoopID: (void*)(intptr_t)fds[0]]; [outs _setLoopID: (void*)(intptr_t)fds[1]]; // no need to connect - [ins setPassive: YES]; - [outs setPassive: YES]; + [ins _setPassive: YES]; + [outs _setPassive: YES]; if (inputStream) - *inputStream = ins; + *inputStream = (NSInputStream*)ins; if (outputStream) - *outputStream = outs; + *outputStream = (NSOutputStream*)outs; return; } @@ -1495,275 +644,6 @@ static void setNonblocking(int fd) @end -@implementation GSServerStream - -+ (id) serverStreamToAddr: (NSString*)addr port: (int)port -{ - GSServerStream *s; - - // try inet first, then inet6 - s = [[GSInetServerStream alloc] initToAddr: addr port: port]; - if (!s) - s = [[GSInet6ServerStream alloc] initToAddr: addr port: port]; - return AUTORELEASE(s); -} - -+ (id) serverStreamToAddr: (NSString*)addr -{ - return AUTORELEASE([[GSLocalServerStream alloc] initToAddr: addr]); -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - RELEASE(self); - // try inet first, then inet6 - self = [[GSInetServerStream alloc] initToAddr: addr port: port]; - if (!self) - self = [[GSInet6ServerStream alloc] initToAddr: addr port: port]; - return self; -} - -- (id) initToAddr: (NSString*)addr -{ - RELEASE(self); - return [[GSLocalServerStream alloc] initToAddr: addr]; -} - -- (void) acceptWithInputStream: (NSInputStream **)inputStream - outputStream: (NSOutputStream **)outputStream -{ - [self subclassResponsibility: _cmd]; -} - -@end - -@implementation GSSocketServerStream - -- (Class) _inputStreamClass -{ - [self subclassResponsibility: _cmd]; - return Nil; -} - -- (Class) _outputStreamClass -{ - [self subclassResponsibility: _cmd]; - return Nil; -} - -- (void) dealloc -{ - if ([self _isOpened]) - { - [self close]; - } - [super dealloc]; -} - -- (socklen_t) sockLen -{ - [self subclassResponsibility: _cmd]; - return 0; -} - -- (struct sockaddr*) serverAddr -{ - [self subclassResponsibility: _cmd]; - return 0; -} - -#define SOCKET_BACKLOG 256 - -- (void) open -{ - int bindReturn; - int listenReturn; - -#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! - */ - int status = 1; - - setsockopt((int)(intptr_t)_loopID, SOL_SOCKET, SO_REUSEADDR, - (char *)&status, sizeof(status)); -#endif - - bindReturn = bind((int)(intptr_t)_loopID, [self serverAddr], [self sockLen]); - listenReturn = listen((intptr_t)_loopID, SOCKET_BACKLOG); - if (bindReturn < 0 || listenReturn < 0) - { - [self _recordError]; - return; - } - setNonblocking((intptr_t)_loopID); - // put itself to the runloop - [super open]; -} - -- (void) close -{ - // close a server socket is safe - close((intptr_t)_loopID); - [super close]; -} - -- (void) acceptWithInputStream: (NSInputStream **)inputStream - outputStream: (NSOutputStream **)outputStream -{ - GSSocketInputStream *ins = AUTORELEASE([[self _inputStreamClass] new]); - GSSocketOutputStream *outs = AUTORELEASE([[self _outputStreamClass] new]); - socklen_t len = [ins sockLen]; - int acceptReturn = accept((intptr_t)_loopID, [ins peerAddr], &len); - - _events &= ~NSStreamEventHasBytesAvailable; - if (acceptReturn < 0) - { // test for real error - if (errno != EWOULDBLOCK -#if defined(EAGAIN) - && errno != EAGAIN -#endif -#if defined(ECONNABORTED) - && errno != ECONNABORTED -#endif -#if defined(EPROTO) - && errno != EPROTO -#endif - && errno != EINTR) - { - [self _recordError]; - } - ins = nil; - outs = nil; - } - else - { - // no need to connect again - [ins setPassive: YES]; - [outs setPassive: YES]; - // copy the addr to outs - memcpy([outs peerAddr], [ins peerAddr], len); - [ins _setLoopID: (void*)(intptr_t)acceptReturn]; - [outs _setLoopID: (void*)(intptr_t)acceptReturn]; - } - if (inputStream) - { - [ins setSibling: outs]; - *inputStream = ins; - } - if (outputStream) - { - [outs setSibling: ins]; - *outputStream = outs; - } -} - -- (void) _dispatch -{ - NSStreamEvent myEvent; - - [self _setStatus: NSStreamStatusOpen]; - myEvent = NSStreamEventHasBytesAvailable; - [self _sendEvent: myEvent]; -} - -@end - -@implementation GSInetServerStream - -- (Class) _inputStreamClass -{ - return [GSInetInputStream class]; -} - -- (Class) _outputStreamClass -{ - return [GSInetOutputStream class]; -} - -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_in); -} - -- (struct sockaddr*) serverAddr -{ - return (struct sockaddr*)&_serverAddr; -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - int ptonReturn; - const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; - - [super init]; - _serverAddr.sin_family = AF_INET; - _serverAddr.sin_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET, addr_c, &(_serverAddr.sin_addr)); - _loopID = (void*)(intptr_t)socket(AF_INET, SOCK_STREAM, 0); - if (ptonReturn == 0 || _loopID < 0) // error - { - RELEASE(self); - return nil; - } - NSAssert(_loopID >= 0, @"cannot open socket"); - return self; -} - -@end - -@implementation GSInet6ServerStream -#if defined(AF_INET6) -- (Class) _inputStreamClass -{ - return [GSInet6InputStream class]; -} - -- (Class) _outputStreamClass -{ - return [GSInet6OutputStream class]; -} - -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_in6); -} - -- (struct sockaddr*) serverAddr -{ - return (struct sockaddr*)&_serverAddr; -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - int ptonReturn; - const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; - - [super init]; - _serverAddr.sin6_family = AF_INET6; - _serverAddr.sin6_port = GSSwapHostI16ToBig(port); - ptonReturn = inet_pton(AF_INET6, addr_c, &(_serverAddr.sin6_addr)); - _loopID = (void*)(intptr_t)socket(AF_INET6, SOCK_STREAM, 0); - if (ptonReturn == 0 || _loopID < 0) // error - { - RELEASE(self); - return nil; - } - NSAssert(_loopID >= 0, @"cannot open socket"); - return self; -} -#else -- (id) initToAddr: (NSString*)addr port: (int)port -{ - RELEASE(self); - return nil; -} -#endif -@end - @implementation GSLocalServerStream - (Class) _inputStreamClass @@ -1776,28 +656,44 @@ static void setNonblocking(int fd) return [GSLocalOutputStream class]; } -- (socklen_t) sockLen +- (socklen_t) _sockLen { return sizeof(struct sockaddr_un); } -- (struct sockaddr*) serverAddr +- (struct sockaddr*) _serverAddr { return (struct sockaddr*)&_serverAddr; } - (id) initToAddr: (NSString*)addr { - const char* real_addr = [addr fileSystemRepresentation]; - [super init]; - _serverAddr.sun_family = AF_LOCAL; - _loopID = (void *)(intptr_t)socket(AF_LOCAL, SOCK_STREAM, 0); - if (strlen(real_addr) > sizeof(_serverAddr.sun_path)-1 || _loopID < 0) + if ((self = [super init]) != nil) { - RELEASE(self); - return nil; + const char* real_addr = [addr fileSystemRepresentation]; + + if (strlen(real_addr) > sizeof(_serverAddr.sun_path)-1) + { + DESTROY(self); + } + else + { + SOCKET s; + + _serverAddr.sun_family = AF_LOCAL; + s = socket(AF_LOCAL, SOCK_STREAM, 0); + if (s < 0) + { + DESTROY(self); + } + else + { + [(GSSocketStream*)self _setSock: s]; + strncpy(_serverAddr.sun_path, real_addr, + sizeof(_serverAddr.sun_path)-1); + } + } } - strncpy(_serverAddr.sun_path, real_addr, sizeof(_serverAddr.sun_path)-1); return self; } diff --git a/Source/win32/NSStream.m b/Source/win32/NSStream.m index 4201274fc..0fc29b8c0 100644 --- a/Source/win32/NSStream.m +++ b/Source/win32/NSStream.m @@ -23,15 +23,6 @@ */ #include "config.h" #include "GNUstepBase/preface.h" -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include -#include -#include -#include #include #include @@ -51,49 +42,6 @@ #define BUFFERSIZE (BUFSIZ*64) -typedef int socklen_t; - -static id propertyForInet4Stream(int descriptor, NSString *key) -{ - struct sockaddr_in sin; - unsigned size = sizeof(sin); - id result = nil; - - if ([key isEqualToString: GSStreamLocalAddressKey]) - { - if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithUTF8String: - (char*)inet_ntoa(sin.sin_addr)]; - } - } - else if ([key isEqualToString: GSStreamLocalPortKey]) - { - if (getsockname(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithFormat: @"%d", - (int)GSSwapBigI16ToHost(sin.sin_port)]; - } - } - else if ([key isEqualToString: GSStreamRemoteAddressKey]) - { - if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithUTF8String: - (char*)inet_ntoa(sin.sin_addr)]; - } - } - else if ([key isEqualToString: GSStreamRemotePortKey]) - { - if (getpeername(descriptor, (struct sockaddr*)&sin, &size) != -1) - { - result = [NSString stringWithFormat: @"%d", - (int)GSSwapBigI16ToHost(sin.sin_port)]; - } - } - return result; -} - /** * The concrete subclass of NSInputStream that reads from a file */ @@ -124,58 +72,7 @@ static id propertyForInet4Stream(int descriptor, NSString *key) - (NSStreamStatus) _check; - (void) _queue; - (void) _setHandle: (HANDLE)h; -- (void) setSibling: (GSPipeOutputStream*)s; -@end - -@class GSSocketOutputStream; - -/** - * The abstract subclass of NSInputStream that reads from a socket - */ -@interface GSSocketInputStream : GSInputStream -{ -@protected - GSSocketOutputStream *_sibling; - BOOL _passive; /* YES means already connected */ - SOCKET _sock; -} - -/** - * get the length of the socket addr - */ -- (socklen_t) sockLen; - -/** - * get the sockaddr - */ -- (struct sockaddr*) peerAddr; - -/** - * setter for sibling - */ -- (void) setSibling: (GSSocketOutputStream*)sibling; - -/** - * setter for passive - */ -- (void) setPassive: (BOOL)passive; - -- (void) setEvent: (WSAEVENT)event; -- (void) setSock: (SOCKET)sock; - -@end - -@interface GSInetInputStream : GSSocketInputStream -{ - @private - struct sockaddr_in _peerAddr; -} - -/** - * the designated initializer - */ -- (id) initToAddr: (NSString*)addr port: (int)port; - +- (void) _setSibling: (GSPipeOutputStream*)s; @end /** @@ -207,94 +104,7 @@ static id propertyForInet4Stream(int descriptor, NSString *key) - (NSStreamStatus) _check; - (void) _queue; - (void) _setHandle: (HANDLE)h; -- (void) setSibling: (GSPipeInputStream*)s; -@end - -/** - * The concrete subclass of NSOutputStream that writes to a socket - */ -@interface GSSocketOutputStream : GSOutputStream -{ -@protected - GSSocketInputStream *_sibling; - BOOL _passive; /* YES means already connected */ - SOCKET _sock; -} - -/** - * get the length of the socket addr - */ -- (socklen_t) sockLen; - -/** - * get the sockaddr - */ -- (struct sockaddr*) peerAddr; - -/** - * setter for sibling - */ -- (void) setSibling: (GSSocketInputStream*)sibling; - -/** - * setter for passive - */ -- (void) setPassive: (BOOL)passive; - -/** - * setter for event - */ -- (void) setEvent: (WSAEVENT)event; -- (void) setSock: (SOCKET)sock; - -@end - -@interface GSInetOutputStream : GSSocketOutputStream -{ - @private - struct sockaddr_in _peerAddr; -} - -/** - * the designated initializer - */ -- (id) initToAddr: (NSString*)addr port: (int)port; - -@end - -/** - * The concrete subclass of NSServerStream that accept connection from a socket - */ -@interface GSSocketServerStream : GSAbstractServerStream -{ - SOCKET _sock; -} -/** - * Return the class of the inputStream associated with this - * type of serverStream. - */ -- (Class) _inputStreamClass; -/** - * Return the class of the outputStream associated with this - * type of serverStream. - */ -- (Class) _outputStreamClass; -/** - * get the length of the socket addr - */ -- (socklen_t) sockLen; -/** - * get the sockaddr - */ -- (struct sockaddr*) serverAddr; - -@end - -@interface GSInetServerStream : GSSocketServerStream -{ - @private - struct sockaddr_in _serverAddr; -} +- (void) _setSibling: (GSPipeInputStream*)s; @end @@ -309,14 +119,6 @@ static id propertyForInet4Stream(int descriptor, NSString *key) } @end -static void setNonblocking(SOCKET fd) -{ - unsigned long dummy = 1; - - if (ioctlsocket(fd, FIONBIO, &dummy) == SOCKET_ERROR) - NSLog(@"unable to set non-blocking mode - %@", [NSError _last]); -} - @implementation GSFileInputStream - (void) close @@ -494,7 +296,7 @@ static void setNonblocking(SOCKET fd) { [self close]; } - [_sibling setSibling: nil]; + [_sibling _setSibling: nil]; _sibling = nil; [super dealloc]; } @@ -681,7 +483,7 @@ static void setNonblocking(SOCKET fd) handle = h; } -- (void) setSibling: (GSPipeOutputStream*)s +- (void) _setSibling: (GSPipeOutputStream*)s { _sibling = s; } @@ -736,335 +538,6 @@ static void setNonblocking(SOCKET fd) } @end -@implementation GSSocketInputStream - -- (socklen_t) sockLen -{ - [self subclassResponsibility: _cmd]; - return 0; -} - -- (struct sockaddr*) peerAddr -{ - [self subclassResponsibility: _cmd]; - return NULL; -} - -- (void) setSibling: (GSSocketOutputStream*)sibling -{ - _sibling = sibling; -} - --(void) setPassive: (BOOL)passive -{ - _passive = passive; -} - -- (void) setEvent: (WSAEVENT)event -{ - _loopID = event; -} - -- (void) setSock: (SOCKET)sock -{ - _sock = sock; -} - -- (id) init -{ - if ((self = [super init]) != nil) - { - _sibling = nil; - _passive = NO; - _loopID = WSA_INVALID_EVENT; - } - return self; -} - -- (void) dealloc -{ - if ([self _isOpened]) - { - [self close]; - } - [_sibling setSibling: nil]; - _sibling = nil; - [super dealloc]; -} - -- (void) open -{ - // could be opened because of sibling - if ([self _isOpened]) - return; - if (_passive || (_sibling && [_sibling _isOpened])) - goto open_ok; - // check sibling status, avoid double connect - if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening) - { - [self _setStatus: NSStreamStatusOpening]; - return; - } - else - { - int connectReturn = connect(_sock, [self peerAddr], [self sockLen]); - - if (connectReturn == SOCKET_ERROR - && WSAGetLastError() != WSAEWOULDBLOCK) - {// make an error - [self _recordError]; - return; - } - // waiting on writable, as an indication of opened - if (NSCountMapTable(_loops) > 0) - { - WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); - [self _schedule]; - } - [self _setStatus: NSStreamStatusOpening]; - return; - } - - open_ok: - [super open]; - setNonblocking(_sock); - WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); -} - -- (void) close -{ - if (_loopID != WSA_INVALID_EVENT) - { - WSACloseEvent(_loopID); - } - if (_sock != INVALID_SOCKET) - { - if (_sibling && [_sibling streamStatus] != NSStreamStatusClosed) - { - /* - * Windows only permits a single event to be associated with a socket - * at any time, but the runloop system only allows an event handle to - * be added to the loop once, and we have two streams for each socket. - * So we use two events, one for each stream, and when one stream is - * closed, we must call WSAEventSelect to ensure that the event handle - * of the sibling is used to signal events from now on. - */ - WSAEventSelect(_sock, [_sibling _loopID], FD_ALL_EVENTS); - shutdown(_sock, SD_RECEIVE); - } - else - { - closesocket(_sock); - } - _sock = INVALID_SOCKET; - } - [super close]; - _loopID = WSA_INVALID_EVENT; -} - -- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len -{ - int readLen; - - if (buffer == 0) - { - [NSException raise: NSInvalidArgumentException - format: @"null pointer for buffer"]; - } - if (len == 0) - { - [NSException raise: NSInvalidArgumentException - format: @"zero byte length read requested"]; - } - - _events &= ~NSStreamEventHasBytesAvailable; - - if ([self streamStatus] == NSStreamStatusClosed) - { - return 0; - } - - readLen = recv(_sock, buffer, len, 0); - if (readLen == SOCKET_ERROR) - { - errno = WSAGetLastError(); - if (errno == WSAEINPROGRESS || errno == WSAEWOULDBLOCK) - { - [self _setStatus: NSStreamStatusReading]; - } - else if (errno != WSAEINTR) - { - [self _recordError]; - } - readLen = -1; - } - else if (readLen == 0) - { - [self _setStatus: NSStreamStatusAtEnd]; - } - else - { - [self _setStatus: NSStreamStatusOpen]; - } - return readLen; -} - -- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len -{ - return NO; -} - -- (void) _dispatch -{ - AUTORELEASE(RETAIN(self)); - /* - * Windows only permits a single event to be associated with a socket - * at any time, but the runloop system only allows an event handle to - * be added to the loop once, and we have two streams for each socket. - * So we use two events, one for each stream, and the _dispatch method - * must handle things for both streams. - */ - if ([self streamStatus] == NSStreamStatusClosed) - { - /* - * It is possible the stream is closed yet recieving event because - * of not closed sibling - */ - NSAssert([_sibling streamStatus] != NSStreamStatusClosed, - @"Received event for closed stream"); - [_sibling _dispatch]; - } - else - { - WSANETWORKEVENTS events; - int error = 0; - int getReturn = -1; - - if (WSAEnumNetworkEvents(_sock, _loopID, &events) == SOCKET_ERROR) - { - error = WSAGetLastError(); - } -//else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self); - - if ([self streamStatus] == NSStreamStatusOpening) - { - [self _unschedule]; - if (error == 0) - { - unsigned len = sizeof(error); - - getReturn = getsockopt(_sock, SOL_SOCKET, SO_ERROR, - (char*)&error, &len); - } - - if (getReturn >= 0 && error == 0 - && (events.lNetworkEvents & FD_CONNECT)) - { // finish up the opening - _passive = YES; - [self open]; - // notify sibling - if (_sibling) - { - [_sibling open]; - [_sibling _sendEvent: NSStreamEventOpenCompleted]; - } - [self _sendEvent: NSStreamEventOpenCompleted]; - } - } - - if (error != 0) - { - errno = error; - [self _recordError]; - [_sibling _recordError]; - [self _sendEvent: NSStreamEventErrorOccurred]; - [_sibling _sendEvent: NSStreamEventErrorOccurred]; - } - else - { - if (events.lNetworkEvents & FD_WRITE) - { - NSAssert([_sibling _isOpened], NSInternalInconsistencyException); - /* Clear NSStreamStatusWriting if it was set */ - [_sibling _setStatus: NSStreamStatusOpen]; - } - /* On winsock a socket is always writable unless it has had - * failure/closure or a write blocked and we have not been - * signalled again. - */ - while ([_sibling _unhandledData] == NO - && [_sibling hasSpaceAvailable]) - { - [_sibling _sendEvent: NSStreamEventHasSpaceAvailable]; - } - - if (events.lNetworkEvents & FD_READ) - { - [self _setStatus: NSStreamStatusOpen]; - while ([self hasBytesAvailable] - && [self _unhandledData] == NO) - { - [self _sendEvent: NSStreamEventHasBytesAvailable]; - } - } - if (events.lNetworkEvents & FD_CLOSE) - { - if ([_sibling _isOpened]) - { - [_sibling _setStatus: NSStreamStatusAtEnd]; - [_sibling _sendEvent: NSStreamEventEndEncountered]; - } - while ([self hasBytesAvailable] - && [self _unhandledData] == NO) - { - [self _sendEvent: NSStreamEventHasBytesAvailable]; - } - if ([self _isOpened]) - { - [self _setStatus: NSStreamStatusAtEnd]; - } - } - } - } -} - -- (BOOL) runLoopShouldBlock: (BOOL*)trigger -{ - *trigger = YES; - return YES; -} -@end - -@implementation GSInetInputStream - -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_in); -} - -- (struct sockaddr*) peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; - - if ((self = [super init]) != nil) - { - _peerAddr.sin_family = AF_INET; - _peerAddr.sin_port = htons(port); - _peerAddr.sin_addr.s_addr = addr_c ? inet_addr(addr_c) : INADDR_NONE; - if (_peerAddr.sin_addr.s_addr == INADDR_NONE) // error - { - DESTROY(self); - } - } - return self; -} - -@end @implementation GSFileOutputStream @@ -1261,7 +734,7 @@ static void setNonblocking(SOCKET fd) { [self close]; } - [_sibling setSibling: nil]; + [_sibling _setSibling: nil]; _sibling = nil; [super dealloc]; } @@ -1404,7 +877,7 @@ static void setNonblocking(SOCKET fd) handle = h; } -- (void) setSibling: (GSPipeInputStream*)s +- (void) _setSibling: (GSPipeInputStream*)s { _sibling = s; } @@ -1459,350 +932,6 @@ static void setNonblocking(SOCKET fd) } @end -@implementation GSSocketOutputStream - -- (socklen_t) sockLen -{ - [self subclassResponsibility: _cmd]; - return 0; -} - -- (struct sockaddr*) peerAddr -{ - [self subclassResponsibility: _cmd]; - return NULL; -} - -- (void) setSibling: (GSSocketInputStream*)sibling -{ - _sibling = sibling; -} - --(void) setPassive: (BOOL)passive -{ - _passive = passive; -} - -- (void) setEvent: (WSAEVENT)event -{ - _loopID = event; -} - -- (void) setSock: (SOCKET)sock -{ - _sock = sock; -} - -- (id) init -{ - if ((self = [super init]) != nil) - { - _sibling = nil; - _passive = NO; - _loopID = WSA_INVALID_EVENT; - } - return self; -} - -- (void) dealloc -{ - if ([self _isOpened]) - { - [self close]; - } - [_sibling setSibling: nil]; - _sibling = nil; - [super dealloc]; -} - -- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len -{ - int writeLen; - - if (buffer == 0) - { - [NSException raise: NSInvalidArgumentException - format: @"null pointer for buffer"]; - } - if (len == 0) - { - [NSException raise: NSInvalidArgumentException - format: @"zero byte length write requested"]; - } - - _events &= ~NSStreamEventHasSpaceAvailable; - - if ([self streamStatus] == NSStreamStatusClosed) - { - return 0; - } - - writeLen = send(_sock, buffer, len, 0); - if (writeLen == SOCKET_ERROR) - { - errno = WSAGetLastError(); - if (errno == WSAEINPROGRESS || errno == WSAEWOULDBLOCK) - { - [self _setStatus: NSStreamStatusWriting]; - } - else if (errno != WSAEINTR) - { - [self _recordError]; - } - writeLen = -1; - } - else - { - [self _setStatus: NSStreamStatusOpen]; - } - return writeLen; -} - -- (void) open -{ - // could be opened because of sibling - if ([self _isOpened]) - return; - if (_passive || (_sibling && [_sibling _isOpened])) - goto open_ok; - // check sibling status, avoid double connect - if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening) - { - [self _setStatus: NSStreamStatusOpening]; - return; - } - else - { - int connectReturn = connect(_sock, [self peerAddr], [self sockLen]); - - if (connectReturn == SOCKET_ERROR - && WSAGetLastError() != WSAEWOULDBLOCK) - {// make an error - [self _recordError]; - return; - } - // waiting on writable, as an indication of opened - if (NSCountMapTable(_loops) > 0) - { - WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); - [self _schedule]; - } - [self _setStatus: NSStreamStatusOpening]; - return; - } - - open_ok: - setNonblocking(_sock); - WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); - [super open]; -} - -- (void) close -{ - if (_loopID != WSA_INVALID_EVENT) - { - WSACloseEvent(_loopID); - } - - if (_sock != INVALID_SOCKET) - { - int closeReturn; // shutdown may fail (broken pipe). Record it. - - if (_sibling && [_sibling streamStatus] != NSStreamStatusClosed) - { - /* - * Windows only permits a single event to be associated with a socket - * at any time, but the runloop system only allows an event handle to - * be added to the loop once, and we have two streams for each socket. - * So we use two events, one for each stream, and when one stream is - * closed, we must call WSAEventSelect to ensure that the event handle - * of the sibling is used to signal events from now on. - */ - WSAEventSelect(_sock, [_sibling _loopID], FD_ALL_EVENTS); - closeReturn = shutdown(_sock, SD_SEND); - } - else - { - closeReturn = closesocket(_sock); - } - _sock = INVALID_SOCKET; - if (closeReturn < 0) - { - [self _recordError]; - } - } - [super close]; - _loopID = WSA_INVALID_EVENT; -} - -- (void) _dispatch -{ - AUTORELEASE(RETAIN(self)); - /* - * Windows only permits a single event to be associated with a socket - * at any time, but the runloop system only allows an event handle to - * be added to the loop once, and we have two streams for each socket. - * So we use two events, one for each stream, and the _dispatch method - * must handle things for both streams. - */ - if ([self streamStatus] == NSStreamStatusClosed) - { - /* - * It is possible the stream is closed yet recieving event because - * of not closed sibling - */ - NSAssert([_sibling streamStatus] != NSStreamStatusClosed, - @"Received event for closed stream"); - [_sibling _dispatch]; - } - else - { - WSANETWORKEVENTS events; - int error = 0; - int getReturn = -1; - - if (WSAEnumNetworkEvents(_sock, _loopID, &events) == SOCKET_ERROR) - { - error = WSAGetLastError(); - } -//else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self); - - if ([self streamStatus] == NSStreamStatusOpening) - { - [self _unschedule]; - if (error == 0) - { - unsigned len = sizeof(error); - - getReturn = getsockopt(_sock, SOL_SOCKET, SO_ERROR, - (char*)&error, &len); - } - - if (getReturn >= 0 && error == 0 - && (events.lNetworkEvents & FD_CONNECT)) - { // finish up the opening - events.lNetworkEvents ^= FD_CONNECT; - _passive = YES; - [self open]; - // notify sibling - if (_sibling) - { - [_sibling open]; - [_sibling _sendEvent: NSStreamEventOpenCompleted]; - } - [self _sendEvent: NSStreamEventOpenCompleted]; - } - } - - if (error != 0) - { - errno = error; - [self _recordError]; - [_sibling _recordError]; - [self _sendEvent: NSStreamEventErrorOccurred]; - [_sibling _sendEvent: NSStreamEventErrorOccurred]; - } - else - { - if (events.lNetworkEvents & FD_WRITE) - { - /* Clear NSStreamStatusWriting if it was set */ - [self _setStatus: NSStreamStatusOpen]; - } - - /* On winsock a socket is always writable unless it has had - * failure/closure or a write blocked and we have not been - * signalled again. - */ - while ([self _unhandledData] == NO && [self hasSpaceAvailable]) - { - [self _sendEvent: NSStreamEventHasSpaceAvailable]; - } - - if (events.lNetworkEvents & FD_READ) - { - [_sibling _setStatus: NSStreamStatusOpen]; - while ([_sibling hasBytesAvailable] - && [_sibling _unhandledData] == NO) - { - [_sibling _sendEvent: NSStreamEventHasBytesAvailable]; - } - } - if (events.lNetworkEvents & FD_CLOSE) - { - [self _setStatus: NSStreamStatusAtEnd]; - [self _sendEvent: NSStreamEventEndEncountered]; - while ([_sibling hasBytesAvailable] - && [_sibling _unhandledData] == NO) - { - [_sibling _sendEvent: NSStreamEventHasBytesAvailable]; - } - if ([_sibling _isOpened]) - { - [_sibling _setStatus: NSStreamStatusAtEnd]; - [_sibling _sendEvent: NSStreamEventEndEncountered]; - } - } - } - } -} - -- (BOOL) runLoopShouldBlock: (BOOL*)trigger -{ - *trigger = YES; - if ([self _unhandledData] == NO && [self streamStatus] == NSStreamStatusOpen) - { - /* In winsock, a writable status is only signalled if an earlier - * write failed (because it would block), so we must simulate the - * writable event by having the run loop trigger without blocking. - */ - return NO; - } - return YES; -} -@end - -@implementation GSInetOutputStream - -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_in); -} - -- (struct sockaddr*) peerAddr -{ - return (struct sockaddr*)&_peerAddr; -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; - - if ((self = [super init]) != nil) - { - _peerAddr.sin_family = AF_INET; - _peerAddr.sin_port = htons(port); - _peerAddr.sin_addr.s_addr = addr_c ? inet_addr(addr_c) : INADDR_NONE; - if (_peerAddr.sin_addr.s_addr == INADDR_NONE) // error - { - DESTROY(self); - } - } - return self; -} - -- (id) propertyForKey: (NSString *)key -{ - id result = propertyForInet4Stream((intptr_t)_loopID, key); - - if (result == nil) - { - result = [super propertyForKey: key]; - } - return result; -} - -@end @implementation NSStream @@ -1812,11 +941,9 @@ static void setNonblocking(SOCKET fd) outputStream: (NSOutputStream **)outputStream { NSString *address = host ? (id)[host address] : (id)@"127.0.0.1"; - GSSocketInputStream *ins = nil; - GSSocketOutputStream *outs = nil; + GSSocketStream *ins = nil; + GSSocketStream *outs = nil; int sock; - WSAEVENT ievent; - WSAEVENT oevent; ins = AUTORELEASE([[GSInetInputStream alloc] initToAddr: address port: port]); @@ -1834,24 +961,19 @@ static void setNonblocking(SOCKET fd) * streams so that whichever stream gets signalled, the correct * actions are taken. */ - ievent = CreateEvent(NULL, NO, NO, NULL); - oevent = CreateEvent(NULL, NO, NO, NULL); - NSAssert(sock != INVALID_SOCKET, @"Cannot open socket"); - [ins setSock: sock]; - [outs setSock: sock]; - [ins setEvent: ievent]; - [outs setEvent: oevent]; + [ins _setSock: sock]; + [outs _setSock: sock]; if (inputStream) { - [ins setSibling: outs]; - *inputStream = ins; + [ins _setSibling: outs]; + *inputStream = (NSInputStream*)ins; } if (outputStream) { - [outs setSibling: ins]; - *outputStream = outs; + [outs _setSibling: ins]; + *outputStream = (NSOutputStream*)outs; } return; } @@ -1916,9 +1038,9 @@ static void setNonblocking(SOCKET fd) outs = AUTORELEASE([GSPipeOutputStream new]); [ins _setHandle: handle]; - [ins setSibling: outs]; + [ins _setSibling: outs]; [outs _setHandle: handle]; - [outs setSibling: ins]; + [outs _setSibling: ins]; done: if (inputStream) @@ -2165,252 +1287,6 @@ done: @end -@implementation GSServerStream - -+ (id) serverStreamToAddr: (NSString*)addr port: (int)port -{ - GSServerStream *s; - - s = [[GSInetServerStream alloc] initToAddr: addr port: port]; - return AUTORELEASE(s); -} - -+ (id) serverStreamToAddr: (NSString*)addr -{ - GSServerStream *s; - - s = [[GSLocalServerStream alloc] initToAddr: addr]; - return AUTORELEASE(s); -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - RELEASE(self); - self = [[GSInetServerStream alloc] initToAddr: addr port: port]; - return self; -} - -- (id) initToAddr: (NSString*)addr -{ - RELEASE(self); - self = [[GSLocalServerStream alloc] initToAddr: addr]; - return self; -} - -- (void) acceptWithInputStream: (NSInputStream **)inputStream - outputStream: (NSOutputStream **)outputStream -{ - [self subclassResponsibility: _cmd]; -} - -@end - -@implementation GSSocketServerStream - -- (Class) _inputStreamClass -{ - [self subclassResponsibility: _cmd]; - return Nil; -} - -- (Class) _outputStreamClass -{ - [self subclassResponsibility: _cmd]; - return Nil; -} - -- (id) init -{ - if ((self = [super init]) != nil) - { - _loopID = WSA_INVALID_EVENT; - } - return self; -} - -- (void) dealloc -{ - if ([self _isOpened]) - { - [self close]; - } - [super dealloc]; -} - -- (socklen_t) sockLen -{ - [self subclassResponsibility: _cmd]; - return 0; -} - -- (struct sockaddr*) serverAddr -{ - [self subclassResponsibility: _cmd]; - return 0; -} - -#define SOCKET_BACKLOG 255 - -- (void) open -{ - int bindReturn; - int listenReturn; -#ifndef BROKEN_SO_REUSEADDR - int status = 1; - - /* - * 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! - */ - setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&status, sizeof(status)); -#endif - bindReturn = bind(_sock, [self serverAddr], [self sockLen]); - listenReturn = listen(_sock, SOCKET_BACKLOG); - if (bindReturn < 0 || listenReturn < 0) - { - [self _recordError]; - return; - } - setNonblocking(_sock); - _loopID = CreateEvent(NULL, NO, NO, NULL); - WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS); - [super open]; -} - -- (void) close -{ - if (_loopID != WSA_INVALID_EVENT) - { - WSACloseEvent(_loopID); - } - if (_sock != INVALID_SOCKET) - { - // close a server socket is safe - closesocket(_sock); - _sock = INVALID_SOCKET; - } - [super close]; - _loopID = WSA_INVALID_EVENT; -} - -- (void) acceptWithInputStream: (NSInputStream **)inputStream - outputStream: (NSOutputStream **)outputStream -{ - GSSocketInputStream *ins = AUTORELEASE([[self _inputStreamClass] new]); - GSSocketOutputStream *outs = AUTORELEASE([[self _outputStreamClass] new]); - socklen_t len = [ins sockLen]; - int acceptReturn = accept(_sock, [ins peerAddr], &len); - - _events &= ~NSStreamEventHasBytesAvailable; - if (acceptReturn == INVALID_SOCKET) - { - errno = WSAGetLastError();// test for real error - if (errno != WSAEWOULDBLOCK && errno != WSAECONNRESET && - errno != WSAEINPROGRESS && errno != WSAEINTR) - { - [self _recordError]; - } - ins = nil; - outs = nil; - } - else - { - /* - * Windows only permits a single event to be associated with a socket - * at any time, but the runloop system only allows an event handle to - * be added to the loop once, and we have two streams. - * So we create two events, one for each stream, so that we can have - * both streams scheduled in the run loop, but we make sure that the - * _dispatch method in each stream actually handles things for both - * streams so that whichever stream gets signalled, the correct - * actions are taken. - */ - WSAEVENT ievent = CreateEvent(NULL, NO, NO, NULL); - WSAEVENT oevent = CreateEvent(NULL, NO, NO, NULL); - // no need to connect again - [ins setPassive: YES]; - [outs setPassive: YES]; - // copy the addr to outs - memcpy([outs peerAddr], [ins peerAddr], len); - [ins setSock: acceptReturn]; - [outs setSock: acceptReturn]; - [ins setEvent: ievent]; - [outs setEvent: oevent]; - } - if (inputStream) - { - [ins setSibling: outs]; - *inputStream = ins; - } - if (outputStream) - { - [outs setSibling: ins]; - *outputStream = outs; - } -} - -- (void) _dispatch -{ - WSANETWORKEVENTS events; - - if (WSAEnumNetworkEvents(_sock, _loopID, &events) == SOCKET_ERROR) - { - errno = WSAGetLastError(); - [self _recordError]; - [self _sendEvent: NSStreamEventErrorOccurred]; - } - else if (events.lNetworkEvents & FD_ACCEPT) - { - events.lNetworkEvents ^= FD_ACCEPT; - [self _setStatus: NSStreamStatusReading]; - [self _sendEvent: NSStreamEventHasBytesAvailable]; - } -} - -@end - -@implementation GSInetServerStream - -- (Class) _inputStreamClass -{ - return [GSInetInputStream class]; -} - -- (Class) _outputStreamClass -{ - return [GSInetOutputStream class]; -} - -- (socklen_t) sockLen -{ - return sizeof(struct sockaddr_in); -} - -- (struct sockaddr*) serverAddr -{ - return (struct sockaddr*)&_serverAddr; -} - -- (id) initToAddr: (NSString*)addr port: (int)port -{ - const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding]; - - [super init]; - _serverAddr.sin_family = AF_INET; - _serverAddr.sin_port = htons(port); - _serverAddr.sin_addr.s_addr = addr_c ? inet_addr(addr_c) : INADDR_NONE; - _sock = socket(AF_INET, SOCK_STREAM, 0); - if (_serverAddr.sin_addr.s_addr == INADDR_NONE || _loopID < 0) // error - { - RELEASE(self); - return nil; - } - return self; -} - -@end @implementation GSLocalServerStream @@ -2559,12 +1435,12 @@ done: if (inputStream) { - [ins setSibling: outs]; + [ins _setSibling: outs]; *inputStream = ins; } if (outputStream) { - [outs setSibling: ins]; + [outs _setSibling: ins]; *outputStream = outs; } }