diff --git a/ChangeLog b/ChangeLog index 14448ac2b..8c235e506 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-01-04 Richard Frith-Macdonald + + * Source/GSSocketStream.h: derived from GSStream.h + * Source/GSSocketStream.m: derived from GSStream.m + * Source/GNUmakefile: use new files + * Source/GSStream.h: remove code to GSSocketStream.h + * Source/GSStream.m: remove code to GSSocketStream.m + * Source/unix/NSStream.m: include GSSocketStream.h + Minor restructuring for clarity. + 2007-01-04 Richard Frith-Macdonald * Headers/Foundation/NSStream.h: Tweak comments. diff --git a/Source/GNUmakefile b/Source/GNUmakefile index 687f67780..33c048435 100644 --- a/Source/GNUmakefile +++ b/Source/GNUmakefile @@ -150,6 +150,7 @@ GSHTTPAuthentication.m \ GSHTTPURLHandle.m \ GSRunLoopWatcher.m \ GSSet.m \ +GSSocketStream.m \ GSStream.m \ GSString.m \ GSValue.m \ diff --git a/Source/GSSocketStream.h b/Source/GSSocketStream.h new file mode 100644 index 000000000..78c16b837 --- /dev/null +++ b/Source/GSSocketStream.h @@ -0,0 +1,234 @@ +#ifndef INCLUDED_GSSOCKETSTREAM_H +#define INCLUDED_GSSOCKETSTREAM_H + +/** Implementation for GSSocketStream for GNUStep + Copyright (C) 2006-2008 Free Software Foundation, Inc. + + Written by: Derek Zhou + Written by: Richard Frith-Macdonald + Date: 2006 + + 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. + +*/ + +/* You should have included GSStream.h before this */ + +#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/GSSocketStream.m b/Source/GSSocketStream.m new file mode 100644 index 000000000..9942ccbbf --- /dev/null +++ b/Source/GSSocketStream.m @@ -0,0 +1,1895 @@ +/** Implementation for GSSocketStream for GNUStep + Copyright (C) 2006-2008 Free Software Foundation, Inc. + + Written by: Derek Zhou + Written by: Richard Frith-Macdonald + Date: 2006 + + 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. + + */ + +#include "config.h" + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "GSStream.h" +#import "GSSocketStream.h" +#import "GSPrivate.h" + +/* + * States for socks connection negotiation + */ +static NSString * const GSSOCKSOfferAuth = @"GSSOCKSOfferAuth"; +static NSString * const GSSOCKSRecvAuth = @"GSSOCKSRecvAuth"; +static NSString * const GSSOCKSSendAuth = @"GSSOCKSSendAuth"; +static NSString * const GSSOCKSAckAuth = @"GSSOCKSAckAuth"; +static NSString * const GSSOCKSSendConn = @"GSSOCKSSendConn"; +static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; + +@interface GSSOCKS +{ + NSString *state; + NSString *addr; + int port; + int roffset; + int woffset; + int rwant; + unsigned char rbuffer[128]; + NSInputStream *istream; + NSOutputStream *ostream; +} +- (NSString*) addr; +- (id) initToAddr: (NSString*)_addr port: (int)_port; +- (int) port; +- (NSString*) stream: (NSStream*)stream SOCKSEvent: (NSStreamEvent)event; +@end + +@implementation GSSOCKS +- (NSString*) addr +{ + return addr; +} + +- (id) initToAddr: (NSString*)_addr port: (int)_port +{ + ASSIGNCOPY(addr, _addr); + port = _port; + state = GSSOCKSOfferAuth; + return self; +} + +- (int) port +{ + return port; +} + +- (NSString*) stream: (NSStream*)stream SOCKSEvent: (NSStreamEvent)event +{ + NSString *error = nil; + NSDictionary *conf; + NSString *user; + NSString *pass; + + if (event == NSStreamEventErrorOccurred + || [stream streamStatus] == NSStreamStatusError + || [stream streamStatus] == NSStreamStatusClosed) + { + return @"SOCKS errur during negotiation"; + } + + conf = [stream propertyForKey: NSStreamSOCKSProxyConfigurationKey]; + user = [conf objectForKey: NSStreamSOCKSProxyUserKey]; + pass = [conf objectForKey: NSStreamSOCKSProxyPasswordKey]; + if ([[conf objectForKey: NSStreamSOCKSProxyVersionKey] + isEqual: NSStreamSOCKSProxyVersion4] == YES) + { + } + else + { + again: + + if (state == GSSOCKSOfferAuth) + { + int result; + int want; + unsigned char buf[4]; + + /* + * Authorisation record is at least three bytes - + * socks version (5) + * authorisation method bytes to follow (1) + * say we do no authorisation (0) + * say we do user/pass authorisation (2) + */ + buf[0] = 5; + if (user && pass) + { + buf[1] = 2; + buf[2] = 2; + buf[3] = 0; + want = 4; + } + else + { + buf[1] = 1; + buf[2] = 0; + want = 3; + } + + result = [ostream write: buf + woffset maxLength: 4 - woffset]; + if (result == 0) + { + error = @"end-of-file during SOCKS negotiation"; + } + else if (result > 0) + { + woffset += result; + if (woffset == want) + { + woffset = 0; + state = GSSOCKSRecvAuth; + goto again; + } + } + } + else if (state == GSSOCKSRecvAuth) + { + int result; + + result = [istream read: rbuffer + roffset maxLength: 2 - roffset]; + if (result == 0) + { + error = @"SOCKS end-of-file during negotiation"; + } + else if (result > 0) + { + roffset += result; + if (roffset == 2) + { + roffset = 0; + if (rbuffer[0] != 5) + { + error = @"SOCKS authorisation response had wrong version"; + } + else if (rbuffer[1] == 0) + { + state = GSSOCKSSendConn; + goto again; + } + else if (rbuffer[1] == 2) + { + state = GSSOCKSSendAuth; + goto again; + } + else + { + error = @"SOCKS authorisation response had wrong method"; + } + } + } + } + else if (state == GSSOCKSSendAuth) + { + NSData *u = [user dataUsingEncoding: NSUTF8StringEncoding]; + unsigned ul = [u length]; + NSData *p = [pass dataUsingEncoding: NSUTF8StringEncoding]; + unsigned pl = [p length]; + + if (ul < 1 || ul > 255) + { + error = @"NSStreamSOCKSProxyUserKey value too long"; + } + else if (ul < 1 || ul > 255) + { + error = @"NSStreamSOCKSProxyPasswordKey value too long"; + } + else + { + int want = ul + pl + 3; + unsigned char buf[want]; + int result; + + buf[0] = 5; + buf[1] = ul; + memcpy(buf + 2, [u bytes], ul); + buf[ul + 2] = pl; + memcpy(buf + ul + 3, [p bytes], pl); + result = [ostream write: buf + woffset maxLength: want - woffset]; + if (result == 0) + { + error = @"SOCKS end-of-file during negotiation"; + } + else if (result > 0) + { + woffset += result; + if (woffset == want) + { + state = GSSOCKSAckAuth; + goto again; + } + } + } + } + else if (state == GSSOCKSAckAuth) + { + int result; + + result = [istream read: rbuffer + roffset maxLength: 2 - roffset]; + if (result == 0) + { + error = @"SOCKS end-of-file during negotiation"; + } + else if (result > 0) + { + roffset += result; + if (roffset == 2) + { + roffset = 0; + if (rbuffer[0] != 5) + { + error = @"SOCKS authorisation response had wrong version"; + } + else if (rbuffer[1] == 0) + { + state = GSSOCKSSendConn; + goto again; + } + else if (rbuffer[1] == 2) + { + error = @"SOCKS authorisation failed"; + } + } + } + } + else if (state == GSSOCKSSendConn) + { + unsigned char buf[10]; + int want = 10; + int result; + const char *ptr; + + /* + * Connect command is ten bytes - + * socks version + * connect command + * reserved byte + * address type + * address 4 bytes (big endian) + * port 2 bytes (big endian) + */ + buf[0] = 5; // Socks version number + buf[1] = 1; // Connect command + buf[2] = 0; // Reserved + buf[3] = 1; // Address type (IPV4) + ptr = [addr lossyCString]; + buf[4] = atoi(ptr); + while (isdigit(*ptr)) + ptr++; + ptr++; + buf[5] = atoi(ptr); + while (isdigit(*ptr)) + ptr++; + ptr++; + buf[6] = atoi(ptr); + while (isdigit(*ptr)) + ptr++; + ptr++; + buf[7] = atoi(ptr); + buf[8] = ((port & 0xff00) >> 8); + buf[9] = (port & 0xff); + + result = [ostream write: buf + woffset maxLength: want - woffset]; + if (result == 0) + { + error = @"SOCKS end-of-file during negotiation"; + } + else if (result > 0) + { + woffset += result; + if (woffset == want) + { + rwant = 5; + state = GSSOCKSAckConn; + goto again; + } + } + } + else if (state == GSSOCKSAckConn) + { + int result; + + result = [istream read: rbuffer + roffset maxLength: rwant - roffset]; + if (result == 0) + { + error = @"SOCKS end-of-file during negotiation"; + } + else if (result > 0) + { + roffset += result; + if (roffset == rwant) + { + if (rbuffer[0] != 5) + { + error = @"connect response from SOCKS had wrong version"; + } + else if (rbuffer[1] != 0) + { + switch (rbuffer[1]) + { + case 1: + error = @"SOCKS server general failure"; + break; + case 2: + error = @"SOCKS server says permission denied"; + break; + case 3: + error = @"SOCKS server says network unreachable"; + break; + case 4: + error = @"SOCKS server says host unreachable"; + break; + case 5: + error = @"SOCKS server says connection refused"; + break; + case 6: + error = @"SOCKS server says connection timed out"; + break; + case 7: + error = @"SOCKS server says command not supported"; + break; + case 8: + error = @"SOCKS server says address not supported"; + break; + default: + error = @"connect response from SOCKS was failure"; + break; + } + } + else if (rbuffer[3] == 1) + { + rwant = 10; // Fixed size (IPV4) address + } + else if (rbuffer[3] == 3) + { + rwant = 7 + rbuffer[4]; // Domain name leading length + } + else if (rbuffer[3] == 4) + { + rwant = 22; // Fixed size (IPV6) address + } + else + { + error = @"SOCKS server returned unknown address type"; + } + if (error == nil) + { + if (roffset < rwant) + { + goto again; // Need address/port bytes + } + else + { + NSString *a; + + error = @""; // success + if (rbuffer[3] == 1) + { + a = [NSString stringWithFormat: @"%d.%d.%d.%d", + rbuffer[4], rbuffer[5], rbuffer[6], rbuffer[7]]; + } + else if (rbuffer[3] == 3) + { + rbuffer[rwant] = '\0'; + a = [NSString stringWithUTF8String: + (const char*)rbuffer]; + } + else + { + unsigned char buf[40]; + int i = 4; + int j = 0; + + while (i < rwant) + { + int val = rbuffer[i++] * 256 + rbuffer[i++]; + + if (i > 4) + { + buf[j++] = ':'; + } + sprintf((char*)&buf[j], "%04x", val); + j += 4; + } + a = [NSString stringWithUTF8String: + (const char*)buf]; + } + ASSIGN(addr, a); + port = rbuffer[rwant-1] * 256 * rbuffer[rwant-2]; + } + } + } + } + } + } + + return error; +} + +@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 + +@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/GSStream.h b/Source/GSStream.h index 994b02ab1..bf02ffe7f 100644 --- a/Source/GSStream.h +++ b/Source/GSStream.h @@ -192,208 +192,5 @@ 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 398a580e9..11b66e3cd 100644 --- a/Source/GSStream.m +++ b/Source/GSStream.m @@ -902,1291 +902,12 @@ static RunLoopEventType typeForStream(NSStream *aStream) @end -/* - * States for socks connection negotiation - */ -static NSString * const GSSOCKSOfferAuth = @"GSSOCKSOfferAuth"; -static NSString * const GSSOCKSRecvAuth = @"GSSOCKSRecvAuth"; -static NSString * const GSSOCKSSendAuth = @"GSSOCKSSendAuth"; -static NSString * const GSSOCKSAckAuth = @"GSSOCKSAckAuth"; -static NSString * const GSSOCKSSendConn = @"GSSOCKSSendConn"; -static NSString * const GSSOCKSAckConn = @"GSSOCKSAckConn"; - -@interface GSSOCKS -{ - NSString *state; - NSString *addr; - int port; - int roffset; - int woffset; - int rwant; - unsigned char rbuffer[128]; - NSInputStream *istream; - NSOutputStream *ostream; -} -- (NSString*) addr; -- (id) initToAddr: (NSString*)_addr port: (int)_port; -- (int) port; -- (NSString*) stream: (NSStream*)stream SOCKSEvent: (NSStreamEvent)event; -@end - -@implementation GSSOCKS -- (NSString*) addr -{ - return addr; -} - -- (id) initToAddr: (NSString*)_addr port: (int)_port -{ - ASSIGNCOPY(addr, _addr); - port = _port; - state = GSSOCKSOfferAuth; - return self; -} - -- (int) port -{ - return port; -} - -- (NSString*) stream: (NSStream*)stream SOCKSEvent: (NSStreamEvent)event -{ - NSString *error = nil; - NSDictionary *conf; - NSString *user; - NSString *pass; - - if (event == NSStreamEventErrorOccurred - || [stream streamStatus] == NSStreamStatusError - || [stream streamStatus] == NSStreamStatusClosed) - { - return @"SOCKS errur during negotiation"; - } - - conf = [stream propertyForKey: NSStreamSOCKSProxyConfigurationKey]; - user = [conf objectForKey: NSStreamSOCKSProxyUserKey]; - pass = [conf objectForKey: NSStreamSOCKSProxyPasswordKey]; - if ([[conf objectForKey: NSStreamSOCKSProxyVersionKey] - isEqual: NSStreamSOCKSProxyVersion4] == YES) - { - } - else - { - again: - - if (state == GSSOCKSOfferAuth) - { - int result; - int want; - unsigned char buf[4]; - - /* - * Authorisation record is at least three bytes - - * socks version (5) - * authorisation method bytes to follow (1) - * say we do no authorisation (0) - * say we do user/pass authorisation (2) - */ - buf[0] = 5; - if (user && pass) - { - buf[1] = 2; - buf[2] = 2; - buf[3] = 0; - want = 4; - } - else - { - buf[1] = 1; - buf[2] = 0; - want = 3; - } - - result = [ostream write: buf + woffset maxLength: 4 - woffset]; - if (result == 0) - { - error = @"end-of-file during SOCKS negotiation"; - } - else if (result > 0) - { - woffset += result; - if (woffset == want) - { - woffset = 0; - state = GSSOCKSRecvAuth; - goto again; - } - } - } - else if (state == GSSOCKSRecvAuth) - { - int result; - - result = [istream read: rbuffer + roffset maxLength: 2 - roffset]; - if (result == 0) - { - error = @"SOCKS end-of-file during negotiation"; - } - else if (result > 0) - { - roffset += result; - if (roffset == 2) - { - roffset = 0; - if (rbuffer[0] != 5) - { - error = @"SOCKS authorisation response had wrong version"; - } - else if (rbuffer[1] == 0) - { - state = GSSOCKSSendConn; - goto again; - } - else if (rbuffer[1] == 2) - { - state = GSSOCKSSendAuth; - goto again; - } - else - { - error = @"SOCKS authorisation response had wrong method"; - } - } - } - } - else if (state == GSSOCKSSendAuth) - { - NSData *u = [user dataUsingEncoding: NSUTF8StringEncoding]; - unsigned ul = [u length]; - NSData *p = [pass dataUsingEncoding: NSUTF8StringEncoding]; - unsigned pl = [p length]; - - if (ul < 1 || ul > 255) - { - error = @"NSStreamSOCKSProxyUserKey value too long"; - } - else if (ul < 1 || ul > 255) - { - error = @"NSStreamSOCKSProxyPasswordKey value too long"; - } - else - { - int want = ul + pl + 3; - unsigned char buf[want]; - int result; - - buf[0] = 5; - buf[1] = ul; - memcpy(buf + 2, [u bytes], ul); - buf[ul + 2] = pl; - memcpy(buf + ul + 3, [p bytes], pl); - result = [ostream write: buf + woffset maxLength: want - woffset]; - if (result == 0) - { - error = @"SOCKS end-of-file during negotiation"; - } - else if (result > 0) - { - woffset += result; - if (woffset == want) - { - state = GSSOCKSAckAuth; - goto again; - } - } - } - } - else if (state == GSSOCKSAckAuth) - { - int result; - - result = [istream read: rbuffer + roffset maxLength: 2 - roffset]; - if (result == 0) - { - error = @"SOCKS end-of-file during negotiation"; - } - else if (result > 0) - { - roffset += result; - if (roffset == 2) - { - roffset = 0; - if (rbuffer[0] != 5) - { - error = @"SOCKS authorisation response had wrong version"; - } - else if (rbuffer[1] == 0) - { - state = GSSOCKSSendConn; - goto again; - } - else if (rbuffer[1] == 2) - { - error = @"SOCKS authorisation failed"; - } - } - } - } - else if (state == GSSOCKSSendConn) - { - unsigned char buf[10]; - int want = 10; - int result; - const char *ptr; - - /* - * Connect command is ten bytes - - * socks version - * connect command - * reserved byte - * address type - * address 4 bytes (big endian) - * port 2 bytes (big endian) - */ - buf[0] = 5; // Socks version number - buf[1] = 1; // Connect command - buf[2] = 0; // Reserved - buf[3] = 1; // Address type (IPV4) - ptr = [addr lossyCString]; - buf[4] = atoi(ptr); - while (isdigit(*ptr)) - ptr++; - ptr++; - buf[5] = atoi(ptr); - while (isdigit(*ptr)) - ptr++; - ptr++; - buf[6] = atoi(ptr); - while (isdigit(*ptr)) - ptr++; - ptr++; - buf[7] = atoi(ptr); - buf[8] = ((port & 0xff00) >> 8); - buf[9] = (port & 0xff); - - result = [ostream write: buf + woffset maxLength: want - woffset]; - if (result == 0) - { - error = @"SOCKS end-of-file during negotiation"; - } - else if (result > 0) - { - woffset += result; - if (woffset == want) - { - rwant = 5; - state = GSSOCKSAckConn; - goto again; - } - } - } - else if (state == GSSOCKSAckConn) - { - int result; - - result = [istream read: rbuffer + roffset maxLength: rwant - roffset]; - if (result == 0) - { - error = @"SOCKS end-of-file during negotiation"; - } - else if (result > 0) - { - roffset += result; - if (roffset == rwant) - { - if (rbuffer[0] != 5) - { - error = @"connect response from SOCKS had wrong version"; - } - else if (rbuffer[1] != 0) - { - switch (rbuffer[1]) - { - case 1: - error = @"SOCKS server general failure"; - break; - case 2: - error = @"SOCKS server says permission denied"; - break; - case 3: - error = @"SOCKS server says network unreachable"; - break; - case 4: - error = @"SOCKS server says host unreachable"; - break; - case 5: - error = @"SOCKS server says connection refused"; - break; - case 6: - error = @"SOCKS server says connection timed out"; - break; - case 7: - error = @"SOCKS server says command not supported"; - break; - case 8: - error = @"SOCKS server says address not supported"; - break; - default: - error = @"connect response from SOCKS was failure"; - break; - } - } - else if (rbuffer[3] == 1) - { - rwant = 10; // Fixed size (IPV4) address - } - else if (rbuffer[3] == 3) - { - rwant = 7 + rbuffer[4]; // Domain name leading length - } - else if (rbuffer[3] == 4) - { - rwant = 22; // Fixed size (IPV6) address - } - else - { - error = @"SOCKS server returned unknown address type"; - } - if (error == nil) - { - if (roffset < rwant) - { - goto again; // Need address/port bytes - } - else - { - NSString *a; - - error = @""; // success - if (rbuffer[3] == 1) - { - a = [NSString stringWithFormat: @"%d.%d.%d.%d", - rbuffer[4], rbuffer[5], rbuffer[6], rbuffer[7]]; - } - else if (rbuffer[3] == 3) - { - rbuffer[rwant] = '\0'; - a = [NSString stringWithUTF8String: - (const char*)rbuffer]; - } - else - { - unsigned char buf[40]; - int i = 4; - int j = 0; - - while (i < rwant) - { - int val = rbuffer[i++] * 256 + rbuffer[i++]; - - if (i > 4) - { - buf[j++] = ':'; - } - sprintf((char*)&buf[j], "%04x", val); - j += 4; - } - a = [NSString stringWithUTF8String: - (const char*)buf]; - } - ASSIGN(addr, a); - port = rbuffer[rwant-1] * 256 * rbuffer[rwant-2]; - } - } - } - } - } - } - - return error; -} - -@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 GSInetInputStream; +@class GSInet6InputStream; +@class GSInetOutputStream; +@class GSInet6OutputStream; +@class GSInetServerStream; +@class GSInet6ServerStream; @class GSLocalServerStream; @implementation GSServerStream @@ -2243,573 +964,3 @@ setNonBlocking(SOCKET fd) @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 bfccce786..1ce0ec141 100644 --- a/Source/unix/NSStream.m +++ b/Source/unix/NSStream.m @@ -36,6 +36,7 @@ #include #include "../GSStream.h" +#include "../GSSocketStream.h" /** * The concrete subclass of NSInputStream that reads from a file