/** Implementation of network port object based on TCP sockets Copyright (C) 2000 Free Software Foundation, Inc. Written by: Richard Frith-Macdonald Based on code by: Andrew Kachites McCallum 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 2 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. */ #import "common.h" #define EXPOSE_NSPort_IVARS 1 #define EXPOSE_NSSocketPort_IVARS 1 #import "GNUstepBase/GSLock.h" #import "Foundation/NSArray.h" #import "Foundation/NSNotification.h" #import "Foundation/NSNotificationQueue.h" #import "Foundation/NSException.h" #import "Foundation/NSRunLoop.h" #import "Foundation/NSByteOrder.h" #import "Foundation/NSData.h" #import "Foundation/NSDate.h" #import "Foundation/NSHost.h" #import "Foundation/NSMapTable.h" #import "Foundation/NSPortMessage.h" #import "Foundation/NSPortNameServer.h" #import "Foundation/NSLock.h" #import "Foundation/NSHost.h" #import "Foundation/NSThread.h" #import "Foundation/NSConnection.h" #import "GSNetwork.h" #import "GSPortPrivate.h" #import "GSPrivate.h" #include #ifdef HAVE_UNISTD_H #include #endif #ifdef __MINGW__ #define close closesocket #else #include /* for MAXHOSTNAMELEN */ #include #include #include /* for inet_ntoa() */ #endif /* !__MINGW__ */ #include #include /* for strchr() */ #include /* for strchr() */ #include #ifdef __MINGW__ #include #include #include #include #else #include #include #include #include #include /* * Stuff for setting the sockets into non-blocking mode. */ #ifdef __POSIX_SOURCE #define NBLK_OPT O_NONBLOCK #else #define NBLK_OPT FNDELAY #endif #include #include #if !defined(SIOCGIFCONF) || defined(__CYGWIN__) #include #ifndef SIOCGIFCONF #include #endif #endif #if defined(__svr4__) #include #endif #define SOCKET int #define SOCKET_ERROR -1 #define INVALID_SOCKET -1 #endif /* !__MINGW__ */ /* * Largest chunk of data possible in DO */ static uint32_t maxDataLength = 32 * 1024 * 1024; #if 0 #define M_LOCK(X) {NSDebugMLLog(@"GSTcpHandleLock",@"lock %@ in %@",X,[NSThread currentThread]); [X lock];} #define M_UNLOCK(X) {NSDebugMLLog(@"GSTcpHandleLock",@"unlock %@ in %@",X,[NSThread currentThread]); [X unlock];} #else #define M_LOCK(X) {[X lock];} #define M_UNLOCK(X) {[X unlock];} #endif #define GS_CONNECTION_MSG 0 #define NETBLOCK 8192 #ifndef INADDR_NONE #define INADDR_NONE -1 #endif /* * Theory of operation * * */ /* Private interfaces */ /* * The GSPortItemType constant is used to identify the type of data in * each packet read. All data transmitted is in a packet, each packet * has an initial packet type and packet length. */ typedef enum { GSP_NONE, GSP_PORT, /* Simple port item. */ GSP_DATA, /* Simple data item. */ GSP_HEAD /* Port message header + initial data. */ } GSPortItemType; /* * The GSPortItemHeader structure defines the header for each item transmitted. * Its contents are transmitted in network byte order. */ typedef struct { uint32_t type; /* A GSPortItemType as a 4-byte number. */ uint32_t length; /* The length of the item (excluding header). */ } GSPortItemHeader; /* * The GSPortMsgHeader structure is at the start of any item of type GSP_HEAD. * Its contents are transmitted in network byte order. * Any additional data in the item is an NSData object. * NB. additional data counts as part of the same item. */ typedef struct { uint32_t mId; /* The ID for the message starting with this. */ uint32_t nItems; /* Number of items (including this one). */ } GSPortMsgHeader; typedef struct { uint16_t num; /* TCP port num */ char addr[0]; /* host address */ } GSPortInfo; /* * Here is how data is transmitted over a socket - * Initially the process making the connection sends an item of type * GSP_PORT to tell the remote end what port is connecting to it. * Therafter, all communication is via port messages. Each port message * consists of an item of type GSP_HEAD followed by zero or more items * of type GSP_PORT or GSP_DATA. The number of items in a port message * is encoded in the 'nItems' field of the header. */ typedef enum { GS_H_UNCON = 0, // Currently idle and unconnected. GS_H_TRYCON, // Trying connection (outgoing). GS_H_ACCEPT, // Making initial connection (incoming). GS_H_CONNECTED // Currently connected. } GSHandleState; @interface GSTcpHandle : NSObject { SOCKET desc; /* File descriptor for I/O. */ unsigned wItem; /* Index of item being written. */ NSMutableData *wData; /* Data object being written. */ unsigned wLength; /* Ammount written so far. */ NSMutableArray *wMsgs; /* Message in progress. */ NSMutableData *rData; /* Buffer for incoming data */ uint32_t rLength; /* Amount read so far. */ uint32_t rWant; /* Amount desired. */ NSMutableArray *rItems; /* Message in progress. */ GSPortItemType rType; /* Type of data being read. */ uint32_t rId; /* Id of incoming message. */ unsigned nItems; /* Number of items to be read. */ GSHandleState state; /* State of the handle. */ unsigned int addrNum; /* Address number within host. */ #ifdef __MINGW__ WSAEVENT event; /* Win32 event associated to socket */ WSAEVENT eventTemp; /* Win32 event for asynchronous */ @public BOOL inReplyMode; /* Indicate when have addEvent self */ BOOL readyToSend; /* Indicate when send */ #endif @public NSRecursiveLock *myLock; /* Lock for this handle. */ BOOL caller; /* Did we connect to other end? */ BOOL valid; NSSocketPort *recvPort; NSSocketPort *sendPort; struct sockaddr_in sockAddr; /* Far end of connection. */ NSString *defaultAddress; } + (GSTcpHandle*) handleWithDescriptor: (SOCKET)d; - (BOOL) connectToPort: (NSSocketPort*)aPort beforeDate: (NSDate*)when; - (int) descriptor; #if defined(__MINGW__) - (int) eventHandle; #endif - (void) invalidate; - (BOOL) isValid; - (void) receivedEvent: (void*)data type: (RunLoopEventType)type extra: (void*)extra forMode: (NSString*)mode; - (void) receivedEventRead; - (void) receivedEventWrite; - (NSSocketPort*) recvPort; - (BOOL) sendMessage: (NSArray*)components beforeDate: (NSDate*)when; - (NSSocketPort*) sendPort; - (void) setState: (GSHandleState)s; - (GSHandleState) state; @end /* * Utility functions for encoding and decoding ports. */ static NSSocketPort* decodePort(NSData *data, NSString *defaultAddress) { GSPortItemHeader *pih; GSPortInfo *pi; NSString *addr; uint16_t pnum; uint32_t length; NSHost *host; unichar c; pih = (GSPortItemHeader*)[data bytes]; NSCAssert(GSSwapBigI32ToHost(pih->type) == GSP_PORT, NSInternalInconsistencyException); length = GSSwapBigI32ToHost(pih->length); pi = (GSPortInfo*)&pih[1]; pnum = GSSwapBigI16ToHost(pi->num); if (strncmp(pi->addr, "VER", 3) == 0) { NSLog(@"Remote version of GNUstep at %s:%d is more recent than this one", pi->addr, pnum); return nil; } addr = [NSString stringWithUTF8String: pi->addr]; NSDebugFLLog(@"NSPort", @"Decoded port as '%@:%d'", addr, pnum); /* * Special case - the encoded port was on the host from which the * message was sent. */ if ([addr length] == 0) { addr = defaultAddress; } c = [addr characterAtIndex: 0]; if (c >= '0' && c <= '9') { host = [NSHost hostWithAddress: addr]; } else { host = [NSHost hostWithName: addr]; } return [NSSocketPort portWithNumber: pnum onHost: host forceAddress: nil listener: NO]; } static NSData* newDataWithEncodedPort(NSSocketPort *port) { GSPortItemHeader *pih; GSPortInfo *pi; NSMutableData *data; unsigned plen; NSString *addr; uint16_t pnum; pnum = [port portNumber]; addr = [port address]; if (addr == nil) { static NSHost *local = nil; /* * If the port is not forced to use a specific address ... * 1. see if it is on the local host, and if so, encode as "". * 2. see if it s a hostname and if so, use the name. * 3. pick one of the host addresses. * 4. use the localhost address. */ if (local == nil) { local = RETAIN([NSHost localHost]); } if ([[port host] isEqual: local] == YES) { addr = @""; } else if ((addr = [[port host] name]) == nil) { addr = [[port host] address]; if (addr == nil) { addr = @"127.0.0.1"; /* resign ourselves to this */ } } } plen = [addr cStringLength] + 3; data = [[NSMutableData alloc] initWithLength: sizeof(GSPortItemHeader)+plen]; pih = (GSPortItemHeader*)[data mutableBytes]; pih->type = GSSwapHostI32ToBig(GSP_PORT); pih->length = GSSwapHostI32ToBig(plen); pi = (GSPortInfo*)&pih[1]; pi->num = GSSwapHostI16ToBig(pnum); [addr getCString: pi->addr]; NSDebugFLLog(@"NSPort", @"Encoded port as '%@:%d'", addr, pnum); return data; } @implementation GSTcpHandle static Class mutableArrayClass; static Class mutableDataClass; static Class portMessageClass; static Class runLoopClass; + (id) allocWithZone: (NSZone*)zone { [NSException raise: NSGenericException format: @"attempt to alloc a GSTcpHandle!"]; return nil; } + (GSTcpHandle*) handleWithDescriptor: (SOCKET)d { GSTcpHandle *handle; #ifdef __MINGW__ unsigned long dummy; #else int e; #endif /* __MINGW__ */ #ifdef __MINGW__ WSAEVENT ev; int rc; #endif if (d == INVALID_SOCKET) { NSLog(@"illegal descriptor (%d) for Tcp Handle", d); return nil; } #ifdef __MINGW__ dummy = 1; if (ioctlsocket(d, FIONBIO, &dummy) == SOCKET_ERROR) { NSLog(@"unable to set non-blocking mode on %d - %@", d, [NSError _last]); return nil; } #else /* !__MINGW__ */ if ((e = fcntl(d, F_GETFL, 0)) >= 0) { e |= NBLK_OPT; if (fcntl(d, F_SETFL, e) < 0) { NSLog(@"unable to set non-blocking mode on %d - %@", d, [NSError _last]); return nil; } } else { NSLog(@"unable to get non-blocking mode on %d - %@", d, [NSError _last]); return nil; } #endif handle = (GSTcpHandle*)NSAllocateObject(self, 0, NSDefaultMallocZone()); handle->desc = d; handle->wMsgs = [NSMutableArray new]; handle->myLock = [GSLazyRecursiveLock new]; handle->valid = YES; #if defined(__MINGW__) ev = (WSAEVENT)CreateEvent(NULL,NO,NO,NULL); if (ev == WSA_INVALID_EVENT) { NSLog(@"Invalid Event - '%d'", WSAGetLastError()); return nil; } rc = WSAEventSelect(handle->desc, ev, FD_ALL_EVENTS); NSAssert(rc == 0, @"WSAEventSelect failed!"); handle->event = ev; handle->inReplyMode = NO; handle->readyToSend = YES; #endif return AUTORELEASE(handle); } + (void) initialize { if (self == [GSTcpHandle class]) { #ifdef __MINGW__ WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD(2, 0); WSAStartup(wVersionRequested, &wsaData); #endif mutableArrayClass = [NSMutableArray class]; mutableDataClass = [NSMutableData class]; portMessageClass = [NSPortMessage class]; runLoopClass = [NSRunLoop class]; } } - (BOOL) connectToPort: (NSSocketPort*)aPort beforeDate: (NSDate*)when { NSArray *addrs; BOOL gotAddr = NO; NSRunLoop *l; M_LOCK(myLock); NSDebugMLLog(@"GSTcpHandle", @"Connecting on 0x%x before %@", self, when); if (state != GS_H_UNCON) { BOOL result; if (state == GS_H_CONNECTED) /* Already connected. */ { NSLog(@"attempting connect on connected handle"); result = YES; } else if (state == GS_H_ACCEPT) /* Impossible. */ { NSLog(@"attempting connect with accepting handle"); result = NO; } else /* Already connecting. */ { NSLog(@"attempting connect while connecting"); result = NO; } M_UNLOCK(myLock); return result; } if (recvPort == nil || aPort == nil) { NSLog(@"attempting connect with port(s) unset"); M_UNLOCK(myLock); return NO; /* impossible. */ } /* * Get an IP address to try to connect to. * If the port has a 'forced' address, just use that. Otherwise we try * each of the addresses for the host in turn. */ if ([aPort address] != nil) { addrs = [NSArray arrayWithObject: [aPort address]]; } else { addrs = [[aPort host] addresses]; } while (gotAddr == NO) { const char *addr; if (addrNum >= [addrs count]) { NSLog(@"run out of addresses to try (tried %d) for port %@", addrNum, aPort); M_UNLOCK(myLock); return NO; } addr = [[addrs objectAtIndex: addrNum++] cString]; memset(&sockAddr, '\0', sizeof(sockAddr)); sockAddr.sin_family = AF_INET; #ifndef HAVE_INET_ATON sockAddr.sin_addr.s_addr = inet_addr(addr); if (sockAddr.sin_addr.s_addr == INADDR_NONE) #else if (inet_aton(addr, &sockAddr.sin_addr) == 0) #endif { NSLog(@"bad ip address - '%s'", addr); } else { gotAddr = YES; NSDebugMLLog(@"GSTcpHandle", @"Connecting to %s:%d using desc %d", addr, [aPort portNumber], desc); } } sockAddr.sin_port = GSSwapHostI16ToBig([aPort portNumber]); if (connect(desc, (struct sockaddr*)&sockAddr, sizeof(sockAddr)) == SOCKET_ERROR) { #ifdef __MINGW__ if (WSAGetLastError() != WSAEWOULDBLOCK) #else if (errno != EINPROGRESS) #endif { NSLog(@"unable to make connection to %s:%d - %@", inet_ntoa(sockAddr.sin_addr), GSSwapBigI16ToHost(sockAddr.sin_port), [NSError _last]); if (addrNum < [addrs count]) { BOOL result; result = [self connectToPort: aPort beforeDate: when]; M_UNLOCK(myLock); return result; } else { M_UNLOCK(myLock); return NO; /* Tried all addresses */ } } } state = GS_H_TRYCON; l = [NSRunLoop currentRunLoop]; #if defined(__MINGW__) NSAssert(event != WSA_INVALID_EVENT, @"Socket without win32 event!"); [l addEvent: (void*)(uintptr_t)event type: ET_HANDLE watcher: self forMode: NSConnectionReplyMode]; [l addEvent: (void*)(uintptr_t)event type: ET_HANDLE watcher: self forMode: NSDefaultRunLoopMode]; inReplyMode = YES; #else [l addEvent: (void*)(uintptr_t)desc type: ET_WDESC watcher: self forMode: NSConnectionReplyMode]; [l addEvent: (void*)(uintptr_t)desc type: ET_EDESC watcher: self forMode: NSConnectionReplyMode]; [l addEvent: (void*)(uintptr_t)desc type: ET_WDESC watcher: self forMode: NSDefaultRunLoopMode]; [l addEvent: (void*)(uintptr_t)desc type: ET_EDESC watcher: self forMode: NSDefaultRunLoopMode]; #endif while (valid == YES && state == GS_H_TRYCON && [when timeIntervalSinceNow] > 0) { M_UNLOCK(myLock); [l runMode: NSConnectionReplyMode beforeDate: when]; M_LOCK(myLock); } #if defined(__MINGW__) [l removeEvent: (void*)(uintptr_t)event type: ET_HANDLE forMode: NSConnectionReplyMode all: NO]; [l removeEvent: (void*)(uintptr_t)event type: ET_HANDLE forMode: NSDefaultRunLoopMode all: NO]; inReplyMode = NO; #else [l removeEvent: (void*)(uintptr_t)desc type: ET_WDESC forMode: NSConnectionReplyMode all: NO]; [l removeEvent: (void*)(uintptr_t)desc type: ET_EDESC forMode: NSConnectionReplyMode all: NO]; [l removeEvent: (void*)(uintptr_t)desc type: ET_WDESC forMode: NSDefaultRunLoopMode all: NO]; [l removeEvent: (void*)(uintptr_t)desc type: ET_EDESC forMode: NSDefaultRunLoopMode all: NO]; #endif if (state == GS_H_TRYCON) { state = GS_H_UNCON; addrNum = 0; M_UNLOCK(myLock); return NO; /* Timed out */ } else if (state == GS_H_UNCON) { if (addrNum < [addrs count] && [when timeIntervalSinceNow] > 0) { BOOL result; /* * The connection attempt failed, but there are still IP addresses * that we haven't tried. */ result = [self connectToPort: aPort beforeDate: when]; M_UNLOCK(myLock); return result; } addrNum = 0; state = GS_H_UNCON; M_UNLOCK(myLock); return NO; /* connection failed */ } else { int status = 1; setsockopt(desc, SOL_SOCKET, SO_KEEPALIVE, (char*)&status, sizeof(status)); addrNum = 0; caller = YES; [aPort addHandle: self forSend: YES]; M_UNLOCK(myLock); return YES; } } - (void) dealloc { [self finalize]; DESTROY(defaultAddress); DESTROY(rData); DESTROY(rItems); DESTROY(wMsgs); DESTROY(myLock); [super dealloc]; } - (NSString*) description { return [NSString stringWithFormat: @"Handle (%d) to %s:%d", desc, inet_ntoa(sockAddr.sin_addr), ntohs(sockAddr.sin_port)]; } - (int) descriptor { return desc; } #if defined(__MINGW__) - (int) eventHandle { return (int) (size_t) event; } #endif - (void) finalize { [self invalidate]; (void)close(desc); desc = -1; } - (void) invalidate { if (valid == YES) { M_LOCK(myLock); if (valid == YES) { NSRunLoop *l; valid = NO; l = [runLoopClass currentRunLoop]; #if defined(__MINGW__) [l removeEvent: (void*)(uintptr_t)event type: ET_HANDLE forMode: nil all: YES]; #else [l removeEvent: (void*)(uintptr_t)desc type: ET_RDESC forMode: nil all: YES]; [l removeEvent: (void*)(uintptr_t)desc type: ET_WDESC forMode: nil all: YES]; [l removeEvent: (void*)(uintptr_t)desc type: ET_EDESC forMode: nil all: YES]; #endif NSDebugMLLog(@"GSTcpHandle", @"invalidated 0x%x", self); [[self recvPort] removeHandle: self]; [[self sendPort] removeHandle: self]; #if defined(__MINGW__) WSACloseEvent(event); event = WSA_INVALID_EVENT; #endif } M_UNLOCK(myLock); } } - (BOOL) isValid { return valid; } - (NSSocketPort*) recvPort { if (recvPort == nil) return nil; else return GS_GC_UNHIDE(recvPort); } - (void) receivedEventRead { unsigned want; void *bytes; int res; /* * Make sure we have a buffer big enough to hold all the data we are * expecting, or NETBLOCK bytes, whichever is greater. */ if (rData == nil) { rData = [[mutableDataClass alloc] initWithLength: NETBLOCK]; rWant = sizeof(GSPortItemHeader); rLength = 0; want = NETBLOCK; } else { want = [rData length]; if (want < rWant) { want = rWant; [rData setLength: want]; } if (want < NETBLOCK) { want = NETBLOCK; [rData setLength: want]; } } /* * Now try to fill the buffer with data. */ bytes = [rData mutableBytes]; res = recv(desc, bytes + rLength, want - rLength, 0); if (res <= 0) { if (res == 0) { NSDebugMLLog(@"GSTcpHandle", @"read eof on 0x%x", self); [self invalidate]; return; } #ifdef __MINGW__ else if (WSAGetLastError()!= WSAEINTR && WSAGetLastError()!= WSAEWOULDBLOCK) #else else if (errno != EINTR && errno != EAGAIN) #endif /* !__MINGW__ */ { NSDebugMLLog(@"GSTcpHandle", @"read failed - %@ on 0x%p", [NSError _last], self); [self invalidate]; return; } res = 0; /* Interrupted - continue */ } NSDebugMLLog(@"GSTcpHandle", @"read %d bytes on 0x%x", res, self); rLength += res; while (valid == YES && rLength >= rWant) { BOOL shouldDispatch = NO; switch (rType) { case GSP_NONE: { GSPortItemHeader *h; unsigned l; /* * We have read an item header - set up to read the * remainder of the item. */ h = (GSPortItemHeader*)bytes; rType = GSSwapBigI32ToHost(h->type); l = GSSwapBigI32ToHost(h->length); if (rType == GSP_PORT) { if (l > 128) { NSLog(@"%@ - unreasonable length (%u) for port", self, l); [self invalidate]; return; } /* * For a port, we leave the item header in the data * so that our decode function can check length info. */ rWant += l; } else if (rType == GSP_DATA) { if (l == 0) { NSData *d; /* * For a zero-length data chunk, we create an empty * data object and add it to the current message. */ rType = GSP_NONE; /* ready for a new item */ rLength -= rWant; if (rLength > 0) { memmove(bytes, bytes + rWant, rLength); } rWant = sizeof(GSPortItemHeader); d = [mutableDataClass new]; [rItems addObject: d]; RELEASE(d); if (nItems == [rItems count]) { shouldDispatch = YES; } } else { if (l > maxDataLength) { NSLog(@"%@ - unreasonable length (%u) for data", self, l); [self invalidate]; return; } /* * If not a port or zero length data, * we discard the data read so far and fill the * data object with the data item from the msg. */ rLength -= rWant; if (rLength > 0) { memmove(bytes, bytes + rWant, rLength); } rWant = l; } } else if (rType == GSP_HEAD) { if (l > maxDataLength) { NSLog(@"%@ - unreasonable length (%u) for data", self, l); [self invalidate]; return; } /* * If not a port or zero length data, * we discard the data read so far and fill the * data object with the data item from the msg. */ rLength -= rWant; if (rLength > 0) { memmove(bytes, bytes + rWant, rLength); } rWant = l; } else { NSLog(@"%@ - bad data received on port handle", self); [self invalidate]; return; } } break; case GSP_HEAD: { GSPortMsgHeader *h; rType = GSP_NONE; /* ready for a new item */ /* * We have read a message header - set up to read the * remainder of the message. */ h = (GSPortMsgHeader*)bytes; rId = GSSwapBigI32ToHost(h->mId); nItems = GSSwapBigI32ToHost(h->nItems); NSAssert(nItems >0, NSInternalInconsistencyException); rItems = [mutableArrayClass allocWithZone: NSDefaultMallocZone()]; rItems = [rItems initWithCapacity: nItems]; if (rWant > sizeof(GSPortMsgHeader)) { NSData *d; /* * The first data item of the message was included in * the header - so add it to the rItems array. */ rWant -= sizeof(GSPortMsgHeader); d = [mutableDataClass alloc]; d = [d initWithBytes: bytes + sizeof(GSPortMsgHeader) length: rWant]; [rItems addObject: d]; RELEASE(d); rWant += sizeof(GSPortMsgHeader); rLength -= rWant; if (rLength > 0) { memmove(bytes, bytes + rWant, rLength); } rWant = sizeof(GSPortItemHeader); if (nItems == 1) { shouldDispatch = YES; } } else { /* * want to read another item */ rLength -= rWant; if (rLength > 0) { memmove(bytes, bytes + rWant, rLength); } rWant = sizeof(GSPortItemHeader); } } break; case GSP_DATA: { NSData *d; rType = GSP_NONE; /* ready for a new item */ d = [mutableDataClass allocWithZone: NSDefaultMallocZone()]; d = [d initWithBytes: bytes length: rWant]; [rItems addObject: d]; RELEASE(d); rLength -= rWant; if (rLength > 0) { memmove(bytes, bytes + rWant, rLength); } rWant = sizeof(GSPortItemHeader); if (nItems == [rItems count]) { shouldDispatch = YES; } } break; case GSP_PORT: { NSSocketPort *p; rType = GSP_NONE; /* ready for a new item */ p = decodePort(rData, defaultAddress); if (p == nil) { NSLog(@"%@ - unable to decode remote port", self); [self invalidate]; return; } /* * Set up to read another item header. */ rLength -= rWant; if (rLength > 0) { memmove(bytes, bytes + rWant, rLength); } rWant = sizeof(GSPortItemHeader); if (state == GS_H_ACCEPT) { /* * This is the initial port information on a new * connection - set up port relationships. */ state = GS_H_CONNECTED; [p addHandle: self forSend: YES]; } else { /* * This is a port within a port message - add * it to the message components. */ [rItems addObject: p]; if (nItems == [rItems count]) { shouldDispatch = YES; } } } break; } if (shouldDispatch == YES) { NSPortMessage *pm; NSSocketPort *rp = [self recvPort]; pm = [portMessageClass allocWithZone: NSDefaultMallocZone()]; pm = [pm initWithSendPort: [self sendPort] receivePort: rp components: rItems]; [pm setMsgid: rId]; rId = 0; DESTROY(rItems); NSDebugMLLog(@"GSTcpHandle", @"got message %@ on 0x%x", pm, self); IF_NO_GC(RETAIN(rp);) M_UNLOCK(myLock); NS_DURING { [rp handlePortMessage: pm]; } NS_HANDLER { M_LOCK(myLock); RELEASE(pm); RELEASE(rp); [localException raise]; } NS_ENDHANDLER M_LOCK(myLock); RELEASE(pm); RELEASE(rp); bytes = [rData mutableBytes]; } } } - (void) receivedEventWrite { if (state == GS_H_TRYCON) /* Connection attempt. */ { int res = 0; socklen_t len = sizeof(res); if (getsockopt(desc, SOL_SOCKET, SO_ERROR, (char*)&res, &len) != 0) { state = GS_H_UNCON; NSLog(@"connect attempt failed - %@", [NSError _last]); } else if (res != 0) { state = GS_H_UNCON; NSLog(@"connect attempt failed - %@", [NSError _systemError: res]); } else { NSData *d = newDataWithEncodedPort([self recvPort]); len = send(desc, [d bytes], [d length], 0); if (len == (int)[d length]) { RELEASE(defaultAddress); defaultAddress = RETAIN([NSString stringWithUTF8String: inet_ntoa(sockAddr.sin_addr)]); NSDebugMLLog(@"GSTcpHandle", @"wrote %d bytes on 0x%x", len, self); state = GS_H_CONNECTED; } else { state = GS_H_UNCON; NSLog(@"connect write attempt failed - %@", [NSError _last]); } RELEASE(d); } } else { int res; unsigned l; const void *b; if (wData == nil) { if ([wMsgs count] > 0) { NSArray *components = [wMsgs objectAtIndex: 0]; wData = [components objectAtIndex: wItem++]; wLength = 0; } else { // NSLog(@"No messages to write on 0x%x.", self); return; } } b = [wData bytes]; l = [wData length]; res = send(desc, b + wLength, l - wLength, 0); if (res < 0) { #ifdef __MINGW__ if (WSAGetLastError()!= WSAEINTR && WSAGetLastError()!= WSAEWOULDBLOCK) #else if (errno != EINTR && errno != EAGAIN) #endif /* !__MINGW__ */ { NSLog(@"write attempt failed - %@", [NSError _last]); [self invalidate]; return; } #ifdef __MINGW__ if (WSAGetLastError()== WSAEWOULDBLOCK) { readyToSend = NO; } #endif /* !__MINGW__ */ } else { NSDebugMLLog(@"GSTcpHandle", @"wrote %d bytes on 0x%x", res, self); wLength += res; if (wLength == l) { NSArray *components; /* * We have completed a data item so see what is * left of the message components. */ components = [wMsgs objectAtIndex: 0]; wLength = 0; if ([components count] > wItem) { /* * More to write - get next item. */ wData = [components objectAtIndex: wItem++]; } else { /* * message completed - remove from list. */ NSDebugMLLog(@"GSTcpHandle", @"completed 0x%x on 0x%x", components, self); wData = nil; wItem = 0; [wMsgs removeObjectAtIndex: 0]; } } } } } - (void) receivedEvent: (void*)data type: (RunLoopEventType)type extra: (void*)extra forMode: (NSString*)mode { #if defined(__MINGW__) WSANETWORKEVENTS ocurredEvents; #else #endif /* * If we have been invalidated (desc < 0) then we should ignore this * event and remove ourself from the runloop. */ if (desc == INVALID_SOCKET) { NSRunLoop *l = [runLoopClass currentRunLoop]; #if defined(__MINGW__) [l removeEvent: data type: ET_HANDLE forMode: mode all: YES]; #else [l removeEvent: data type: ET_WDESC forMode: mode all: YES]; [l removeEvent: data type: ET_EDESC forMode: mode all: YES]; #endif return; } M_LOCK(myLock); #if defined(__MINGW__) if (WSAEnumNetworkEvents(desc, event, &ocurredEvents)==SOCKET_ERROR) { NSLog(@"Error getting event type %d", WSAGetLastError()); abort(); } if (ocurredEvents.lNetworkEvents & FD_CONNECT) { [self receivedEventWrite]; GSPrivateNotifyASAP(mode); if (desc == INVALID_SOCKET) { M_UNLOCK(myLock); return; } ocurredEvents.lNetworkEvents ^= FD_CONNECT; } if (ocurredEvents.lNetworkEvents & FD_READ) { [self receivedEventRead]; GSPrivateNotifyASAP(mode); if (desc == INVALID_SOCKET) { M_UNLOCK(myLock); return; } ocurredEvents.lNetworkEvents ^= FD_READ; } if (ocurredEvents.lNetworkEvents & FD_OOB) { [self receivedEventRead]; GSPrivateNotifyASAP(mode); if (desc == INVALID_SOCKET) { M_UNLOCK(myLock); return; } ocurredEvents.lNetworkEvents ^= FD_OOB; } if (ocurredEvents.lNetworkEvents & FD_WRITE) { readyToSend = YES; [self receivedEventWrite]; GSPrivateNotifyASAP(mode); if (desc == INVALID_SOCKET) { M_UNLOCK(myLock); return; } ocurredEvents.lNetworkEvents ^= FD_WRITE; } if (ocurredEvents.lNetworkEvents & FD_CLOSE) { [self receivedEventRead]; GSPrivateNotifyASAP(mode); if (desc == INVALID_SOCKET) { M_UNLOCK(myLock); return; } ocurredEvents.lNetworkEvents ^= FD_CLOSE; } if (ocurredEvents.lNetworkEvents) { NSLog(@"Event not get %d", ocurredEvents.lNetworkEvents); abort(); } #else if (type != ET_WDESC) { [self receivedEventRead]; } else { [self receivedEventWrite]; } #endif M_UNLOCK(myLock); } - (BOOL) sendMessage: (NSArray*)components beforeDate: (NSDate*)when { NSRunLoop *l; BOOL sent = NO; NSAssert([components count] > 0, NSInternalInconsistencyException); NSDebugMLLog(@"GSTcpHandle", @"Sending message 0x%x %@ on 0x%x(%d) before %@", components, components, self, desc, when); M_LOCK(myLock); [wMsgs addObject: components]; l = [runLoopClass currentRunLoop]; IF_NO_GC(RETAIN(self);) #if defined(__MINGW__) NSAssert(event != WSA_INVALID_EVENT, @"Socket without win32 event!"); [l addEvent: (void*)(uintptr_t)event type: ET_HANDLE watcher: self forMode: NSConnectionReplyMode]; [l addEvent: (void*)(uintptr_t)event type: ET_HANDLE watcher: self forMode: NSDefaultRunLoopMode]; inReplyMode = YES; #else [l addEvent: (void*)(uintptr_t)desc type: ET_WDESC watcher: self forMode: NSConnectionReplyMode]; [l addEvent: (void*)(uintptr_t)desc type: ET_EDESC watcher: self forMode: NSConnectionReplyMode]; [l addEvent: (void*)(uintptr_t)desc type: ET_WDESC watcher: self forMode: NSDefaultRunLoopMode]; [l addEvent: (void*)(uintptr_t)desc type: ET_EDESC watcher: self forMode: NSDefaultRunLoopMode]; #endif while (valid == YES && [wMsgs indexOfObjectIdenticalTo: components] != NSNotFound && [when timeIntervalSinceNow] > 0) { M_UNLOCK(myLock); #if defined(__MINGW__) if (readyToSend) { [self receivedEventWrite]; } else { [l runMode: NSConnectionReplyMode beforeDate: when]; } #else [l runMode: NSConnectionReplyMode beforeDate: when]; #endif M_LOCK(myLock); } #if defined(__MINGW__) [l removeEvent: (void*)(uintptr_t)event type: ET_HANDLE forMode: NSConnectionReplyMode all: NO]; [l removeEvent: (void*)(uintptr_t)event type: ET_HANDLE forMode: NSDefaultRunLoopMode all: NO]; inReplyMode = NO; #else [l removeEvent: (void*)(uintptr_t)desc type: ET_WDESC forMode: NSConnectionReplyMode all: NO]; [l removeEvent: (void*)(uintptr_t)desc type: ET_EDESC forMode: NSConnectionReplyMode all: NO]; [l removeEvent: (void*)(uintptr_t)desc type: ET_WDESC forMode: NSDefaultRunLoopMode all: NO]; [l removeEvent: (void*)(uintptr_t)desc type: ET_EDESC forMode: NSDefaultRunLoopMode all: NO]; #endif if ([wMsgs indexOfObjectIdenticalTo: components] == NSNotFound) { sent = YES; } M_UNLOCK(myLock); NSDebugMLLog(@"GSTcpHandle", @"Message send 0x%x on 0x%x status %d", components, self, sent); RELEASE(self); return sent; } - (NSSocketPort*) sendPort { if (sendPort == nil) return nil; else if (caller == YES) return GS_GC_UNHIDE(sendPort); // We called, so port is not retained. else return sendPort; // Retained port. } - (void) setState: (GSHandleState)s { state = s; } - (GSHandleState) state { return state; } @end @interface NSSocketPort (RunLoop) - (void) receivedEvent: (void*)data type: (RunLoopEventType)type extra: (void*)extra forMode: (NSString*)mode; @end @implementation NSSocketPort static NSRecursiveLock *tcpPortLock = nil; static NSMapTable *tcpPortMap = 0; static Class tcpPortClass; + (void) initialize { if (self == [NSSocketPort class]) { tcpPortClass = self; tcpPortMap = NSCreateMapTable(NSIntMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0); tcpPortLock = [GSLazyRecursiveLock new]; } } + (id) new { return RETAIN([self portWithNumber: 0 onHost: nil forceAddress: nil listener: YES]); } /* * Look up an existing NSSocketPort given a host and number */ + (NSSocketPort*) existingPortWithNumber: (uint16_t)number onHost: (NSHost*)aHost { NSSocketPort *port = nil; NSMapTable *thePorts; M_LOCK(tcpPortLock); /* * Get the map table of ports with the specified number. */ thePorts = (NSMapTable*)NSMapGet(tcpPortMap, (void*)(uintptr_t)number); if (thePorts != 0) { port = (NSSocketPort*)NSMapGet(thePorts, (void*)aHost); IF_NO_GC(AUTORELEASE(RETAIN(port))); } M_UNLOCK(tcpPortLock); return port; } /* * This is the preferred initialisation method for NSSocketPort * * 'number' should be a TCP/IP port number or may be zero for a port on * the local host. * 'aHost' should be the host for the port or may be nil for the local * host. * 'addr' is the IP address that MUST be used for this port - if it is nil * then, for the local host, the port uses ALL IP addresses, and for a * remote host, the port will use the first address that works. */ + (NSSocketPort*) portWithNumber: (uint16_t)number onHost: (NSHost*)aHost forceAddress: (NSString*)addr listener: (BOOL)shouldListen { socklen_t slen; NSSocketPort *port = nil; NSHost *thisHost = [NSHost localHost]; NSMapTable *thePorts; if (thisHost == nil) { NSLog(@"attempt to create port on host without networking set up!"); return nil; } if (aHost == nil) { aHost = thisHost; } if (addr != nil && [[aHost addresses] containsObject: addr] == NO) { NSLog(@"attempt to use address '%@' on host without that address", addr); return nil; } if (number == 0 && [thisHost isEqual: aHost] == NO) { NSLog(@"attempt to get port zero on remote host"); return nil; } M_LOCK(tcpPortLock); /* * First try to find a pre-existing port. */ thePorts = (NSMapTable*)NSMapGet(tcpPortMap, (void*)(uintptr_t)number); if (thePorts != 0) { port = (NSSocketPort*)NSMapGet(thePorts, (void*)aHost); } if (port == nil) { port = (NSSocketPort*)NSAllocateObject(self, 0, NSDefaultMallocZone()); port->listener = -1; port->host = RETAIN(aHost); port->address = [addr copy]; port->handles = NSCreateMapTable(NSIntMapKeyCallBacks, NSObjectMapValueCallBacks, 0); #if defined(__MINGW__) port->eventListener = WSA_INVALID_EVENT; port->events = NSCreateMapTable(NSIntMapKeyCallBacks, NSIntMapValueCallBacks, 0); #endif port->myLock = [GSLazyRecursiveLock new]; port->_is_valid = YES; if (shouldListen == YES && [thisHost isEqual: aHost]) { #ifndef BROKEN_SO_REUSEADDR int reuse = 1; /* Should we re-use ports? */ #endif SOCKET desc; BOOL addrOk = YES; struct sockaddr_in sockaddr; /* * Creating a new port on the local host - so we must create a * listener socket to accept incoming connections. */ memset(&sockaddr, '\0', sizeof(sockaddr)); sockaddr.sin_family = AF_INET; if (addr == nil) { sockaddr.sin_addr.s_addr = GSSwapHostI32ToBig(INADDR_ANY); } else { #ifndef HAVE_INET_ATON sockaddr.sin_addr.s_addr = inet_addr([addr cString]); if (sockaddr.sin_addr.s_addr == INADDR_NONE) #else if (inet_aton([addr cString], &sockaddr.sin_addr) == 0) #endif { addrOk = NO; } } sockaddr.sin_port = GSSwapHostI16ToBig(number); /* * Need size of buffer for getsockbyname() later. */ slen = sizeof(sockaddr); if (addrOk == NO) { NSLog(@"Bad address (%@) specified for listening port", addr); DESTROY(port); } else if ((desc = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) == INVALID_SOCKET) { NSLog(@"unable to create socket - %@", [NSError _last]); DESTROY(port); } #ifndef BROKEN_SO_REUSEADDR /* * Under decent systems, SO_REUSEADDR means that the port can be * reused immediately that this porcess exits. Under some it means * that multiple processes can serve the same port simultaneously. * We don't want that broken behavior! */ else if (setsockopt(desc, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0) { (void) close(desc); NSLog(@"unable to set reuse on socket - %@", [NSError _last]); DESTROY(port); } #endif else if (bind(desc, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) { NSLog(@"unable to bind to port %s:%d - %@", inet_ntoa(sockaddr.sin_addr), number, [NSError _last]); (void) close(desc); DESTROY(port); } else if (listen(desc, GSBACKLOG) == SOCKET_ERROR) { NSLog(@"unable to listen on port - %@", [NSError _last]); (void) close(desc); DESTROY(port); } else if (getsockname(desc, (struct sockaddr*)&sockaddr, &slen) == SOCKET_ERROR) { NSLog(@"unable to get socket name - %@", [NSError _last]); (void) close(desc); DESTROY(port); } else { #if defined(__MINGW__) int rc; #endif /* * Set up the listening descriptor and the actual TCP port * number (which will have been set to a real port number when * we did the 'bind' call. */ port->listener = desc; port->portNum = GSSwapBigI16ToHost(sockaddr.sin_port); #if defined(__MINGW__) port->eventListener = (WSAEVENT)CreateEvent(NULL,NO,NO,NULL); if (port->eventListener == WSA_INVALID_EVENT) { NSLog(@"Invalid Event - '%d'", WSAGetLastError()); abort(); } rc = WSAEventSelect(port->listener, port->eventListener, FD_ACCEPT); NSAssert(rc == 0, @"WSAEventSelect failed!"); #endif /* * Make sure we have the map table for this port. */ thePorts = (NSMapTable*)NSMapGet(tcpPortMap, (void*)(uintptr_t)port->portNum); if (thePorts == 0) { /* * No known ports with this port number - * create the map table to add the new port to. */ thePorts = NSCreateMapTable(NSObjectMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0); NSMapInsert(tcpPortMap, (void*)(uintptr_t)port->portNum, (void*)thePorts); } /* * Ok - now add the port for the host */ NSMapInsert(thePorts, (void*)aHost, (void*)port); NSDebugMLLog(@"NSPort", @"Created listening port: %@", port); } } else { /* * Make sure we have the map table for this port. */ port->portNum = number; thePorts = (NSMapTable*)NSMapGet(tcpPortMap, (void*)(uintptr_t)number); if (thePorts == 0) { /* * No known ports within this port number - * create the map table to add the new port to. */ thePorts = NSCreateMapTable(NSIntMapKeyCallBacks, NSNonOwnedPointerMapValueCallBacks, 0); NSMapInsert(tcpPortMap, (void*)(uintptr_t)number, (void*)thePorts); } /* * Record the port by host. */ NSMapInsert(thePorts, (void*)aHost, (void*)port); NSDebugMLLog(@"NSPort", @"Created speaking port: %@", port); } } else { IF_NO_GC(RETAIN(port);) NSDebugMLLog(@"NSPort", @"Using pre-existing port: %@", port); } IF_NO_GC(AUTORELEASE(port)); M_UNLOCK(tcpPortLock); return port; } - (void) addHandle: (GSTcpHandle*)handle forSend: (BOOL)send { M_LOCK(myLock); if (send == YES) { if (handle->caller == YES) handle->sendPort = GS_GC_HIDE(self); else ASSIGN(handle->sendPort, self); } else { handle->recvPort = GS_GC_HIDE(self); } NSMapInsert(handles, (void*)(uintptr_t)[handle descriptor], (void*)handle); #if defined(__MINGW__) NSMapInsert(events, (void*)(uintptr_t)[handle eventHandle], (void*)(uintptr_t)[handle descriptor]); #endif M_UNLOCK(myLock); } - (NSString*) address { return address; } - (id) copyWithZone: (NSZone*)zone { return RETAIN(self); } - (void) dealloc { [self finalize]; [super dealloc]; } - (NSString*) description { NSString *desc; desc = [NSString stringWithFormat: @"<%s %p on IP %@,%d>", GSClassNameFromObject(self), self, ((address == nil) ? (id)@"any" : (id)address), portNum]; return desc; } - (void) finalize { NSDebugMLLog(@"NSPort", @"NSSocketPort 0x%x finalized", self); [self invalidate]; if (handles != 0) { NSFreeMapTable(handles); handles = 0; } DESTROY(host); TEST_RELEASE(address); DESTROY(myLock); } /* * This is a callback method used by the NSRunLoop class to determine which * descriptors to watch for the port. */ #if defined(__MINGW__) - (void) getFds: (NSInteger*)fds count: (NSInteger*)count { NSMapEnumerator me; void *event; SOCKET fd; GSTcpHandle *handle; id recvSelf; M_LOCK(myLock); /* * Make sure there is enough room in the provided array. */ NSAssert(*count > (int)NSCountMapTable(events), NSInternalInconsistencyException); /* * Put in our listening socket. */ *count = 0; if (eventListener != WSA_INVALID_EVENT) { fds[(*count)++] = (uintptr_t)eventListener; } /* * Enumerate all our socket handles, and put them in as long as they * are to be used for receiving. */ recvSelf = GS_GC_HIDE(self); me = NSEnumerateMapTable(events); while (NSNextMapEnumeratorPair(&me, &event, (void**)&fd)) { handle = (GSTcpHandle*)NSMapGet(handles, (void*)(uintptr_t)fd); if (handle->recvPort == recvSelf && handle->inReplyMode == NO) { fds[(*count)++] = (uintptr_t)event; NSDebugMLLog(@"NSPort", @"Add event %p", event); } } NSEndMapTableEnumeration(&me); M_UNLOCK(myLock); } #else - (void) getFds: (NSInteger*)fds count: (NSInteger*)count { NSMapEnumerator me; void *sock; GSTcpHandle *handle; id recvSelf; M_LOCK(myLock); /* * Make sure there is enough room in the provided array. */ NSAssert(*count > (int)NSCountMapTable(handles), NSInternalInconsistencyException); /* * Put in our listening socket. */ *count = 0; if (listener >= 0) { fds[(*count)++] = listener; } /* * Enumerate all our socket handles, and put them in as long as they * are to be used for receiving. */ recvSelf = GS_GC_HIDE(self); me = NSEnumerateMapTable(handles); while (NSNextMapEnumeratorPair(&me, &sock, (void**)&handle)) { if (handle->recvPort == recvSelf) { fds[(*count)++] = (int)(intptr_t)sock; } } NSEndMapTableEnumeration(&me); M_UNLOCK(myLock); } #endif - (id) conversation: (NSPort*)recvPort { NSMapEnumerator me; void *dummy; GSTcpHandle *handle = nil; M_LOCK(myLock); /* * Enumerate all our socket handles, and look for one with port. */ me = NSEnumerateMapTable(handles); while (NSNextMapEnumeratorPair(&me, &dummy, (void**)&handle)) { if ((NSPort*) [handle recvPort] == recvPort) { IF_NO_GC(RETAIN(handle);) NSEndMapTableEnumeration(&me); M_UNLOCK(myLock); return AUTORELEASE(handle); } } NSEndMapTableEnumeration(&me); M_UNLOCK(myLock); return nil; } - (GSTcpHandle*) handleForPort: (NSSocketPort*)recvPort beforeDate: (NSDate*)when { NSMapEnumerator me; SOCKET sock; void *dummy; #ifndef BROKEN_SO_REUSEADDR int opt = 1; #endif GSTcpHandle *handle = nil; M_LOCK(myLock); /* * Enumerate all our socket handles, and look for one with port. */ me = NSEnumerateMapTable(handles); while (NSNextMapEnumeratorPair(&me, &dummy, (void**)&handle)) { if ([handle recvPort] == recvPort) { M_UNLOCK(myLock); NSEndMapTableEnumeration(&me); return handle; } } NSEndMapTableEnumeration(&me); /* * Not found ... create a new handle. */ handle = nil; if ((sock = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) == INVALID_SOCKET) { NSLog(@"unable to create socket - %@", [NSError _last]); } #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! */ else if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0) { (void)close(sock); NSLog(@"unable to set reuse on socket - %@", [NSError _last]); } #endif else if ((handle = [GSTcpHandle handleWithDescriptor: sock]) == nil) { (void)close(sock); NSLog(@"unable to create GSTcpHandle - %@", [NSError _last]); } else { [recvPort addHandle: handle forSend: NO]; } M_UNLOCK(myLock); /* * If we succeeded in creating a new handle - connect to remote host. */ if (handle != nil) { if ([handle connectToPort: self beforeDate: when] == NO) { [handle invalidate]; handle = nil; } } return handle; } - (void) handlePortMessage: (NSPortMessage*)m { id d = [self delegate]; if (d == nil) { NSDebugMLLog(@"NSPort", @"No delegate to handle incoming message", 0); return; } if ([d respondsToSelector: @selector(handlePortMessage:)] == NO) { NSDebugMLLog(@"NSPort", @"delegate doesn't handle messages", 0); return; } [d handlePortMessage: m]; } - (NSUInteger) hash { return (unsigned)portNum; } - (NSHost*) host { return host; } - (id) init { DESTROY(self); self = [tcpPortClass new]; return self; } - (void) invalidate { if ([self isValid] == YES) { IF_NO_GC(RETAIN(self);) M_LOCK(myLock); if ([self isValid] == YES) { NSMapTable *thePorts; NSArray *handleArray; unsigned i; M_LOCK(tcpPortLock); thePorts = NSMapGet(tcpPortMap, (void*)(uintptr_t)portNum); if (thePorts != 0) { NSMapRemove(thePorts, (void*)host); } M_UNLOCK(tcpPortLock); if (listener >= 0) { (void) close(listener); listener = -1; #if defined(__MINGW__) WSACloseEvent(eventListener); eventListener = WSA_INVALID_EVENT; #endif } if (handles != 0) { handleArray = NSAllMapTableValues(handles); i = [handleArray count]; while (i-- > 0) { GSTcpHandle *handle = [handleArray objectAtIndex: i]; [handle invalidate]; } } #if defined(__MINGW__) if (events != 0) { NSFreeMapTable(events); events = 0; } #endif [[NSSocketPortNameServer sharedInstance] removePort: self]; [super invalidate]; } M_UNLOCK(myLock); RELEASE(self); } } - (BOOL) isEqual: (id)anObject { if (anObject == self) { return YES; } if ([anObject class] == [self class]) { NSSocketPort *o = (NSSocketPort*)anObject; if (o->portNum == portNum && [o->host isEqual: host]) { return YES; } } return NO; } - (uint16_t) portNumber { return portNum; } - (void) receivedEvent: (void*)data type: (RunLoopEventType)type extra: (void*)extra forMode: (NSString*)mode { #if defined(__MINGW__) WSAEVENT event = (WSAEVENT)extra; SOCKET desc; #else SOCKET desc = (SOCKET)(uintptr_t)extra; #endif GSTcpHandle *handle; NSDebugMLLog(@"NSPort", @"received %s event %p on 0x%x", type == ET_RPORT ? "read" : "write", extra, self); #if defined(__MINGW__) if (event == eventListener) #else if (desc == listener) #endif { struct sockaddr_in sockAddr; socklen_t size = sizeof(sockAddr); desc = accept(listener, (struct sockaddr*)&sockAddr, &size); if (desc == INVALID_SOCKET) { NSDebugMLLog(@"NSPort", @"accept failed - handled in other thread?"); } else { int status = 1; setsockopt(desc, SOL_SOCKET, SO_KEEPALIVE, (char*)&status, sizeof(status)); #if defined(__MINGW__) // reset associated event with new socket WSAEventSelect(desc, eventListener, 0); #endif /* * Create a handle for the socket and set it up so we are its * receiving port, and it's waiting to get the port name from * the other end. */ handle = [GSTcpHandle handleWithDescriptor: desc]; memcpy(&handle->sockAddr, &sockAddr, sizeof(sockAddr)); handle->defaultAddress = RETAIN([NSString stringWithUTF8String: inet_ntoa(sockAddr.sin_addr)]); [handle setState: GS_H_ACCEPT]; [self addHandle: handle forSend: NO]; } } else { M_LOCK(myLock); #if defined(__MINGW__) desc = (SOCKET)NSMapGet(events, (void*)(uintptr_t)event); #endif handle = (GSTcpHandle*)NSMapGet(handles, (void*)(uintptr_t)desc); IF_NO_GC(AUTORELEASE(RETAIN(handle))); M_UNLOCK(myLock); if (handle == nil) { const char *t; #if defined(__MINGW__) if (type == ET_HANDLE) t = "winhandle"; #else if (type == ET_RDESC) t = "rdesc"; else if (type == ET_WDESC) t = "wdesc"; else if (type == ET_EDESC) t = "edesc"; #endif else if (type == ET_RPORT) t = "rport"; else t = "unknown"; NSLog(@"No handle for event %s on descriptor %d", t, desc); [[runLoopClass currentRunLoop] removeEvent: extra type: type forMode: mode all: YES]; } else { [handle receivedEvent: data type: type extra: extra forMode: mode]; } } } - (void) release { M_LOCK(tcpPortLock); if (NSDecrementExtraRefCountWasZero(self)) { NSMapTable *thePorts; thePorts = NSMapGet(tcpPortMap, (void*)(uintptr_t)portNum); if (thePorts != 0) { NSMapRemove(thePorts, host); } M_UNLOCK(tcpPortLock); [self dealloc]; } else { M_UNLOCK(tcpPortLock); } } /* * This is called when a tcp/ip socket connection is broken. We remove the * connection handle from this port and, if this was the last handle to a * remote port, we invalidate the port. */ - (void) removeHandle: (GSTcpHandle*)handle { IF_NO_GC(RETAIN(self);) M_LOCK(myLock); if ([handle sendPort] == self) { if (handle->caller != YES) { /* * This is a handle for a send port, and the handle was not formed * by calling the remote process, so this port object must have * been created to deal with an incoming connection and will have * been retained - we must therefore release this port since the * handle no longer uses it. */ IF_NO_GC(RELEASE(self);) } handle->sendPort = nil; } if ([handle recvPort] == self) { handle->recvPort = nil; } NSMapRemove(handles, (void*)(uintptr_t)[handle descriptor]); #if defined(__MINGW__) NSMapRemove(events, (void*)(uintptr_t)[handle eventHandle]); #endif if (((int) listener) < 0 && NSCountMapTable(handles) == 0) { [self invalidate]; } M_UNLOCK(myLock); RELEASE(self); } /* * This returns the amount of space that a port coder should reserve at the * start of its encoded data so that the NSSocketPort can insert header info * into the data. * The idea is that a message consisting of a single data item with space at * the start can be written directly without having to copy data to another * buffer etc. */ - (NSUInteger) reservedSpaceLength { return sizeof(GSPortItemHeader) + sizeof(GSPortMsgHeader); } - (BOOL) sendBeforeDate: (NSDate*)when msgid: (NSInteger)msgId components: (NSMutableArray*)components from: (NSPort*)receivingPort reserved: (NSUInteger)length { BOOL sent = NO; GSTcpHandle *h; unsigned rl; if ([self isValid] == NO) { return NO; } if ([components count] == 0) { NSLog(@"empty components sent"); return NO; } /* * If the reserved length in the first data object is wrong - we have to * fail, unless it's zero, in which case we can insert a data object for * the header. */ rl = [self reservedSpaceLength]; if (length != 0 && length != rl) { NSLog(@"bad reserved length - %u", length); return NO; } if ([receivingPort isKindOfClass: tcpPortClass] == NO) { NSLog(@"woah there - receiving port is not the correct type"); return NO; } h = [self handleForPort: (NSSocketPort*)receivingPort beforeDate: when]; if (h != nil) { NSMutableData *header; unsigned hLength; unsigned l; GSPortItemHeader *pih; GSPortMsgHeader *pmh; unsigned c = [components count]; unsigned i; BOOL pack = YES; /* * Ok - ensure we have space to insert header info. */ if (length == 0 && rl != 0) { header = [[mutableDataClass alloc] initWithCapacity: NETBLOCK]; [header setLength: rl]; [components insertObject: header atIndex: 0]; RELEASE(header); } header = [components objectAtIndex: 0]; /* * The Item header contains the item type and the length of the * data in the item (excluding the item header itself). */ hLength = [header length]; l = hLength - sizeof(GSPortItemHeader); pih = (GSPortItemHeader*)[header mutableBytes]; pih->type = GSSwapHostI32ToBig(GSP_HEAD); pih->length = GSSwapHostI32ToBig(l); /* * The message header contains the message Id and the original count * of components in the message (excluding any extra component added * simply to hold the header). */ pmh = (GSPortMsgHeader*)&pih[1]; pmh->mId = GSSwapHostI32ToBig(msgId); pmh->nItems = GSSwapHostI32ToBig(c); /* * Now insert item header information as required. * Pack as many items into the initial data object as possible, up to * a maximum of NETBLOCK bytes. This is to try to get a single, * efficient write operation if possible. */ c = [components count]; for (i = 1; i < c; i++) { id o = [components objectAtIndex: i]; if ([o isKindOfClass: [NSData class]]) { GSPortItemHeader *pih; unsigned h = sizeof(GSPortItemHeader); unsigned l = [o length]; void *b; if (pack == YES && hLength + l + h <= NETBLOCK) { [header setLength: hLength + l + h]; b = [header mutableBytes]; b += hLength; #if NEED_WORD_ALIGNMENT /* * When packing data, an item may not be aligned on a * word boundary, so we work with an aligned buffer * and use memcmpy() */ if ((hLength % __alignof__(uint32_t)) != 0) { GSPortItemHeader itemHeader; pih = (GSPortItemHeader*)&itemHeader; pih->type = GSSwapHostI32ToBig(GSP_DATA); pih->length = GSSwapHostI32ToBig(l); memcpy(b, (void*)pih, h); } else { pih = (GSPortItemHeader*)b; pih->type = GSSwapHostI32ToBig(GSP_DATA); pih->length = GSSwapHostI32ToBig(l); } #else pih = (GSPortItemHeader*)b; pih->type = GSSwapHostI32ToBig(GSP_DATA); pih->length = GSSwapHostI32ToBig(l); #endif memcpy(b+h, [o bytes], l); [components removeObjectAtIndex: i--]; c--; hLength += l + h; } else { NSMutableData *d; pack = NO; d = [[NSMutableData alloc] initWithLength: l + h]; b = [d mutableBytes]; pih = (GSPortItemHeader*)b; memcpy(b+h, [o bytes], l); pih->type = GSSwapHostI32ToBig(GSP_DATA); pih->length = GSSwapHostI32ToBig(l); [components replaceObjectAtIndex: i withObject: d]; RELEASE(d); } } else if ([o isKindOfClass: tcpPortClass]) { NSData *d = newDataWithEncodedPort(o); unsigned dLength = [d length]; if (pack == YES && hLength + dLength <= NETBLOCK) { void *b; [header setLength: hLength + dLength]; b = [header mutableBytes]; b += hLength; hLength += dLength; memcpy(b, [d bytes], dLength); [components removeObjectAtIndex: i--]; c--; } else { pack = NO; [components replaceObjectAtIndex: i withObject: d]; } RELEASE(d); } } /* * Now send the message. */ sent = [h sendMessage: components beforeDate: when]; } return sent; } @end