home *** CD-ROM | disk | FTP | other *** search
/ OpenStep (Enterprise) / OpenStepENTCD.toast / OEDEV / DEV.Z / TCPPort.m < prev    next >
Encoding:
Text File  |  1996-04-17  |  24.1 KB  |  800 lines

  1. /* TCPPort.m created by blaine on Wed 03-Apr-1996
  2.    Copyright 1996, NeXT Computer, Inc.
  3.    Blaine Garst
  4.    Marc Majka
  5.  */
  6.  
  7.  
  8. #import "TCPPort.h"
  9.  
  10. #if defined(WIN32)
  11. #import <windows.h>
  12. #import <winsock.h>
  13. #import <winnt-pdo.h>
  14. #else
  15. #import <sys/types.h>
  16. #import <sys/socket.h>
  17. #import <netinet/in.h>
  18. #import <netdb.h>
  19.  
  20. #define SOCKET int
  21. #define INVALID_SOCKET -1
  22. #define closesocket(x) close(x)
  23.  
  24. #endif
  25.  
  26. #if !defined(__svr4__)
  27. #import <libc.h>
  28. #endif
  29.  
  30. #define ProtocolNameTCP "tcp"
  31. #define MaxTCPListenQueue 5
  32.  
  33. static int enableLogging = 0;
  34.  
  35.  
  36. /*************** Hashtable stuff *********************/
  37. /*
  38.  * Keep a table of TCPPorts so that if you're sent a port that
  39.  * you already know about, you don't connect, etc. again.
  40.  */
  41.  
  42. static NSHashTable *TCPPorts = NULL;
  43.  
  44. /* create a fake TCPPort to see if its in the table already */
  45. static TCPPort *lookupSocketPort(unsigned short tcpPort, unsigned long addr, TCPPort *listener) {
  46.     struct { @defs(TCPPort) } shadow;
  47.  
  48.     shadow.isa = [TCPPort class];
  49.     shadow.internetAddr = addr;
  50.     shadow.tcpPort = tcpPort;
  51.     shadow.listener = listener;
  52.     // [tableLock lock];
  53.     return NSHashGet(TCPPorts, &shadow);
  54.     // [tableLock unlock];
  55. }
  56.  
  57. /*********** Utilities ********************************/
  58. /*
  59.  * A little function to canonicalize byte order dependent data.
  60.  */
  61.  
  62. static unsigned int sanitize(unsigned int x) {
  63.     return NSSwapBigIntToHost(x);  // no-op on BigEndians
  64. }        
  65.  
  66. // Two socket utilities: tcpp and addrp are supposed to be inout, but
  67. // experience with bind doesn't seem to indicate that they actually change.
  68. // To wit, INADDR_ANY (0) stays that way...
  69. static BOOL tryBind(int fd, unsigned short *tcpp, unsigned long *addrp) {
  70.     struct sockaddr_in addr;
  71.     int len, retval;
  72.  
  73.     // Set up my address
  74.     len = sizeof(struct sockaddr_in);
  75.     memset((char *)&addr, 0, len);
  76.     addr.sin_family = AF_INET;
  77.     addr.sin_addr.s_addr = *addrp; // INADDR_ANY;
  78.     addr.sin_port = *tcpp; // p;
  79.  
  80.     // Give the socket my address and port 
  81.     retval = bind(fd, (struct sockaddr *)&addr, len);
  82.     *addrp = addr.sin_addr.s_addr;
  83.     *tcpp = addr.sin_port;
  84.     if (retval < 0) NSLog(@"bind failed");
  85.     return (retval >= 0);
  86. }
  87.  
  88. // again, the out case doesn't seem to do anything XXX
  89. static BOOL tryConnect(int fd, unsigned short *tcpp, unsigned long *addrp) {
  90.     struct sockaddr_in addr;
  91.     int len, retval;
  92.     extern int errno;
  93.  
  94.     // Set up address
  95.     len = sizeof(struct sockaddr_in);
  96.     memset((char *)&addr, 0, len);
  97.     addr.sin_family = AF_INET;
  98.     addr.sin_addr.s_addr = *addrp;
  99.     addr.sin_port = *tcpp;
  100.  
  101.     retval = connect(fd, (struct sockaddr *)&addr, len);
  102.     *addrp = addr.sin_addr.s_addr;
  103.     *tcpp = addr.sin_port;
  104.     if (retval != 0) NSLog(@"connect failed, errno %d", errno);
  105.     return (retval == 0);
  106. }
  107.  
  108. static unsigned short getNumber(int s1) {
  109.     struct sockaddr_in mine;
  110.     int len, ret;
  111.  
  112.     len = sizeof(struct sockaddr_in);
  113.     ret = getsockname(s1, (struct sockaddr *)&mine, &len);
  114.     if (ret < 0) printf("socket %d has no name!\n", s1);
  115.     return mine.sin_port;
  116. }
  117.  
  118. static SOCKET makeSocket() {
  119.     static struct protoent *proto = NULL;
  120.     SOCKET result;
  121.  
  122. #if 1
  123.     if (!proto) {
  124.     // Get the TCP protocol number
  125.     proto = getprotobyname(ProtocolNameTCP);
  126.     if (proto == NULL) {
  127.           NSLog(@"failed to getproto");
  128.         return INVALID_SOCKET;
  129.     }
  130.     }
  131. #endif
  132.  
  133.     // Create a new TCP socket
  134.     result = socket(PF_INET, SOCK_STREAM, 0 /*proto->p_proto*/);
  135.     if (enableLogging) NSLog(@"created socket %u", result);
  136.     if (result == INVALID_SOCKET)
  137.     if (enableLogging) NSLog(@"socket call failed");
  138.     return result;
  139. }
  140.  
  141. /*******************************************************/
  142. /*********** the real concrete class *******************/
  143. /*******************************************************/
  144.  
  145. @implementation TCPPort
  146.  
  147.  
  148. /*
  149.  * Before anything else, set up the hashtable.  The hashtable
  150.  * doesn't retain its objects because they would never get
  151.  * freed otherwise!
  152.  * Also, watch out for multiple invocations of initialize.  This
  153.  * is a problem for base classes but be careful anyway.
  154.  */
  155. + (void)initialize {
  156. #if defined(WIN32)
  157.     DWORD vR = MAKEWORD(1, 1);
  158.     WSADATA wsaData;
  159. #endif
  160.  
  161.     if (TCPPorts) return;
  162.     TCPPorts = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0);
  163.     // if not multithreaded already, register for multithreaded notification
  164.     // otherwise just dipatch to the notification XXX
  165. #if WATCH_ALL
  166.     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readAllNotification:) name:NSFileHandleReadCompletionNotification object:nil];
  167. #endif
  168.  
  169. #if defined(WIN32)
  170.     (void)WSAStartup(vR, &wsaData);
  171. #endif
  172. }
  173.  
  174. #if WATCH_ALL
  175. + (void)readAllNotification:(NSNotification *)note {
  176.     NSLog(@"-- read notification for %d, data length %u", [[note object] fileDescriptor], [[[note userInfo] objectForKey:NSFileHandleNotificationDataItem] length]);
  177. }
  178. #endif
  179.  
  180. + (void)_toggleLogging {
  181.     enableLogging = enableLogging ? 0 : 1;
  182. }
  183.  
  184. /* the machPort cover is grody and should do something radical */
  185. - (TCPPort *)initWithMachPort:(int)machPort {
  186.     // raise or be nicer and return nil
  187.     [self dealloc];
  188.     return nil;    
  189. }
  190.  
  191. /* reimplement hash and isEqual so that we can distinguish
  192.    functional equivalents even if the memory (or socket)
  193.    is different.  Elsewhere we cons up a fake object to see if we can
  194.    find one that already exists so that we can avoid a duplicate.
  195.   */
  196.   
  197. - (unsigned) hash {
  198.     return internetAddr^tcpPort;
  199. }
  200.  
  201. - (BOOL)isEqual:(TCPPort *)other {
  202.     if (!other || other->isa != isa) return NO;
  203.     return other
  204.     && other->isa == isa
  205.     && other->internetAddr == internetAddr
  206.     && other->tcpPort == tcpPort
  207.     && other->listener == listener;
  208.     /* note that the socket itself is defined to be not important!! */
  209. }
  210.  
  211.  
  212.  
  213.  
  214. /*********** initialization stuff **************/
  215.  
  216. - _acceptorWithFileDescriptor:(int)fd {
  217. #if defined(WIN32)
  218.     NSFileHandle *result = [[NSFileHandle alloc] initWithNativeHandle:fd];
  219. #else
  220.     NSFileHandle *result = [[NSFileHandle alloc] initWithFileDescriptor:fd];
  221. #endif
  222.     // XXX use constant when available
  223.     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(acceptNotification:) name:@"NSFileHandleConnectionAcceptedNotification" object:result];
  224.     return result;
  225. }
  226.  
  227. - (void)_readerWithFileHandle:result {
  228.     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readNotification:) name:NSFileHandleReadCompletionNotification object:result];
  229.     dataStream = [[DataStream alloc] init];
  230. }
  231.  
  232. - _readerWithFileDescriptor:(int)fd {
  233.     NSFileHandle *result = [[NSFileHandle alloc] initWithFileDescriptor:fd];
  234.     [self _readerWithFileHandle:result];
  235.     return result;
  236. }
  237.  
  238. // build up a tcpPort that will listen()
  239. // tcpPort will indeed be the port we listen on
  240. - (TCPPort *)initWithNumber:(unsigned short)p {
  241.     SOCKET fd = makeSocket();
  242.     int retval;
  243.  
  244.     if (fd == INVALID_SOCKET) {
  245.     [super dealloc];
  246.     return nil;
  247.     }
  248.     internetAddr = INADDR_ANY;
  249.     tcpPort = p;
  250.     if (!tryBind(fd, &tcpPort, &internetAddr)) {
  251.     closesocket(fd);
  252.     [super dealloc];
  253.     return nil;
  254.     }
  255.     if (!p) {
  256.         // XXX untested
  257.         tcpPort = getNumber(fd);
  258.     }
  259.     if (internetAddr == INADDR_ANY) {
  260.         NSHost *me = [NSHost currentHost];
  261.     if (me == nil) {
  262.         closesocket(fd);
  263.         return nil;
  264.     }
  265.     internetAddr = inet_addr([[me address] cString]);
  266.     }
  267.  
  268.     // Put the socket in passive mode (accept connections)
  269.     retval = listen(fd, MaxTCPListenQueue);
  270.     if (retval == INVALID_SOCKET) {
  271.     closesocket(fd);
  272.     [super dealloc];
  273.     return nil;
  274.     }
  275.  
  276.     modes = [NSCountedSet new];
  277.     readWriter = [self _acceptorWithFileDescriptor:fd];
  278.     return self;
  279. }
  280.  
  281.  
  282. - (BOOL)_connect {
  283.     int fd = makeSocket();
  284.     unsigned short myPort = tcpPort;
  285.     unsigned long connectAddr = internetAddr;
  286.     unsigned long tmp;
  287.     
  288.     if (fd == INVALID_SOCKET) {
  289.         return NO;
  290.     }
  291.     if (!listener) return NO;
  292.     if (!tryConnect(fd, &myPort, &connectAddr)) {
  293.         if (enableLogging) NSLog(@"***failed to connect to port %d addr %u", tcpPort, internetAddr);
  294.     closesocket(fd);
  295.     return NO;
  296.     }
  297.  
  298.     if (enableLogging) NSLog(@"connect()ed to port %d addr %u for fd %d", tcpPort, internetAddr, fd);
  299.     tmp = sanitize(listener->tcpPort);
  300.     if (write(fd, &tmp, 4) != 4) {
  301.     closesocket(fd);
  302.     return NO;
  303.     }
  304.     // address is already sanitized
  305.     if (write(fd, &listener->internetAddr, 4) != 4) {
  306.     closesocket(fd);
  307.     return NO;
  308.     }
  309.     readWriter = [self _readerWithFileDescriptor:fd];
  310.     return YES;
  311. }
  312.  
  313.  
  314. //
  315. // creates a connected tcp port suitable for DO (if necessary)
  316. // if one already exists, use it
  317. //
  318. - (TCPPort *)connectToNumber:(unsigned short)p address:(unsigned long)addr {
  319.     TCPPort *sender = lookupSocketPort(p, addr, self);
  320.  
  321.     if (sender) {
  322.         [sender retain];
  323.     }
  324.     if (!sender) {
  325.         sender = [isa alloc];
  326.     sender->internetAddr = addr;
  327.     sender->tcpPort = p;
  328.         sender->listener = [self retain];
  329.     NSHashInsertKnownAbsent(TCPPorts, sender);
  330.     sender->modes = [NSCountedSet new];
  331.     }
  332.  
  333.     if (!sender->readWriter && ![sender _connect]) {
  334.     [sender release];
  335.     sender = nil;
  336.     return nil;
  337.     }
  338.     return [sender autorelease];
  339. }
  340.  
  341. - (TCPPort *)connectToNumber:(unsigned short)p host:(NSString *)hostname {
  342.     NSHost *her;
  343.     unsigned long addr;
  344.  
  345.  
  346.     // Get the server's address
  347.     // XXX this stuff should be a category on NSHost...
  348.     her = [NSHost hostWithName:hostname];
  349.     if (her == nil) {
  350.         return nil;
  351.     }
  352.     addr = inet_addr([[her address] cString]);
  353.  
  354.     return [self connectToNumber:p address:addr];
  355. }
  356.  
  357.  
  358. //
  359. // Create a tcpPort from an existing listen()ing socket
  360. //
  361.  
  362. - (TCPPort *)tcpPortWithAcceptedSocket:handle {
  363.     TCPPort *source;
  364.     unsigned long logicalPort;
  365.     unsigned long destAddr;
  366.     int s = [handle fileDescriptor];
  367.  
  368.     if (enableLogging) NSLog(@"accept()ed socket %d, reading...", s);
  369.     if (read(s, &logicalPort, 4) != 4) {
  370.     if (enableLogging) NSLog(@"didn't read send logicalPort");
  371.     return nil;
  372.     }
  373.     logicalPort = sanitize(logicalPort);
  374.     if (read(s, &destAddr, 4) != 4) {
  375.     if (enableLogging) NSLog(@"didn't read sender's addr");
  376.     return nil;
  377.     }
  378.     source = lookupSocketPort(logicalPort, destAddr, self);
  379.     if (source) {
  380.     if (enableLogging) NSLog(@"found accepted port %d addr %u", logicalPort, destAddr);
  381.         [source retain];
  382.     if (source->reader) {
  383.         if (enableLogging) NSLog(@"***got 2nd reader!");
  384.         [source->reader release];
  385.     }
  386.     source->reader = [handle retain];
  387.     [source _readerWithFileHandle:handle];
  388.     // sync up with any already established connections
  389.     [source->reader readInBackgroundAndNotifyForModes:[modes allObjects]];
  390.     }
  391.     else {
  392.         source = [isa alloc];
  393.     if (enableLogging) NSLog(@"creating accepted port %d addr %u", logicalPort, destAddr);
  394.     source->listener = [self retain];
  395.     source->tcpPort = logicalPort;
  396.     source->internetAddr = destAddr;
  397.     NSHashInsertKnownAbsent(TCPPorts, source);
  398.     source->readWriter = [handle retain];
  399.         source->modes = [NSCountedSet new];
  400.     [source _readerWithFileHandle:handle];
  401.     }
  402.     return [source autorelease];
  403. }
  404.  
  405. // 
  406. // Create a TCP server on an arbitrary socket
  407. //
  408.  
  409. - init {
  410.     return [[isa alloc] initWithNumber:0];
  411. }
  412.    
  413. + (NSPort *)port {
  414.     return [[[self alloc] initWithNumber:0] autorelease];
  415. }
  416.  
  417. /*****************************************************/
  418. /************ Coding behavior ************************/
  419. /*****************************************************/
  420.  
  421. /* The good news is that we don't have to do anything!
  422.  * This works because ports are treated very specially by the
  423.  * portcoder - as long as they internally use the
  424.  * {encode/decode}PortObject: methods.  These methods simply
  425.  * stuff the port supplied into the portCoders array of
  426.  * components.  We'll see these objects later when the portCoder
  427.  * asks us to send a message along, or when we feed components
  428.  * into a portCoder when we get a message off the wire.  All I'm
  429.  * trying to say is that portCoder doesn't care how ports are
  430.  * constructed internally.
  431.  */
  432.  
  433.  
  434. - (void)encodeWithCoder:(NSCoder *)coder {
  435. if (enableLogging) NSLog(@"encoding port %d addr %d (out %d, in %d)", tcpPort, internetAddr, [readWriter fileDescriptor], [reader fileDescriptor]);
  436.     [super encodeWithCoder:coder];
  437. }
  438.  
  439. /*****************************************************/
  440. /********* Deallocation behavior *********************/
  441. /*****************************************************/
  442.  
  443. /* invalidate the port and inform everyone of such a circumstance */
  444. - (void)invalidate {
  445.     id reader2 = reader;
  446.     id writer2 = readWriter;
  447.     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  448.  
  449.     if (invalid) return;
  450.     invalid = 1;
  451.     reader = nil;
  452.     readWriter = nil;
  453.     [connection release]; connection = nil;
  454.  
  455.     NSHashRemove(TCPPorts, self);
  456.     [listener release]; listener = nil;
  457.     [center removeObserver:self];
  458.     [center postNotificationName:NSPortDidBecomeInvalidNotification object:self];
  459.     // we may be free already, so operate on stack variables !!!
  460.     if (reader2) [center removeObserver:reader2];
  461.     if (writer2) [center removeObserver:writer2];
  462.     [reader2 release];
  463.     [writer2 release];
  464. }
  465.  
  466. - (void)dealloc {
  467.     [modes release];
  468.  
  469.     /** partial stuff **/
  470.     [items release];
  471.     [dataItem release];
  472.     [dataStream release];
  473.  
  474.     [super dealloc];
  475. }
  476.  
  477. - (BOOL)isValid {
  478.     return !invalid;
  479. }
  480.  
  481. /********* retain/release/retainCount must all be overridden */
  482. - retain {
  483.    ++refCount;
  484.    return self;
  485. }
  486.  
  487. /* Dereference is tricky.  Since many people can hold references to
  488.    ports we need a way to inform them of our potential invalidity.
  489.    We do this with a notification.  Unfortunately, this bumps our
  490.    reference count.  So if our reference count goes to zip, we
  491.    form and send a notification, bumping it, it then again drops to
  492.    zero and attempts to call dealloc twice.  Not good.
  493.    Keep an isDying bit to catch the final ref==0 case.
  494.  */
  495. - (void)release {
  496.     if (refCount == 0) {
  497.         // invalidate calls postNotification which retain/releases
  498.         // but its correspondent might retain & autorrelease!
  499.         if (!isDying) {
  500.             isDying = 1;
  501.             [self invalidate];
  502.             if (refCount == 0) {  // don't do this now if retain-autoreleased
  503.                 [self dealloc];
  504.             }
  505.         }
  506.         else {    // the autorelease case comes here
  507.             [self dealloc];
  508.         }
  509.     }
  510.     else {
  511.         --refCount;
  512.     }
  513. }
  514.  
  515. - (unsigned)retainCount {
  516.     return refCount + 1;
  517. }
  518.  
  519. /****************************************************************/
  520. /******************* general instance behavior ******************/
  521. /****************************************************************/
  522.  
  523. /********* NSPort overrides ************************************/
  524. /********* needed by all subclassers ***************************/
  525.  
  526. /* Give something useful. FD and tcpPort are equal contenders */
  527. - (int)machPort {
  528.    return tcpPort;
  529. }
  530.  
  531. - (void)setDelegate:(id)anId {
  532.    delegate = anId;
  533. }
  534.  
  535. - (id)delegate {
  536.    return delegate;
  537. }
  538.  
  539. /*** runLoop interactions ****/
  540.  
  541.  
  542. - (void)addConnection:(NSConnection *)conn toRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode {
  543.     TCPPort *sendPort = [conn sendPort];
  544.     
  545.     [modes addObject:mode];
  546.     [sendPort->modes addObject:mode];
  547.  
  548.     [readWriter acceptConnectionInBackgroundAndNotifyForModes:[modes allObjects]];
  549.     if (enableLogging) NSLog(@"accept()ing on %@ for modes %@", self, modes);
  550.     [sendPort->readWriter readInBackgroundAndNotifyForModes:[sendPort->modes allObjects]];
  551.     if (enableLogging) NSLog(@"read()ing on %@ for modes %@", sendPort, sendPort->modes);
  552.     [sendPort->reader readInBackgroundAndNotifyForModes:[sendPort->modes allObjects]];
  553. }
  554.  
  555. - (void)removeConnection:(NSConnection *)conn fromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode {
  556.     TCPPort *sendPort = [conn sendPort];
  557.     
  558.     [modes removeObject:mode];
  559.     [sendPort->modes removeObject:mode];
  560.  
  561.     [readWriter acceptConnectionInBackgroundAndNotifyForModes:[modes allObjects]];
  562.     if (enableLogging) NSLog(@"accept()ing on %@ for modes %@", self, modes);
  563.     [sendPort->readWriter readInBackgroundAndNotifyForModes:[sendPort->modes allObjects]];
  564.     if (enableLogging) NSLog(@"read()ing on %@ for modes %@", sendPort, sendPort->modes);
  565.     [sendPort->reader readInBackgroundAndNotifyForModes:[sendPort->modes allObjects]];
  566. }
  567.  
  568.  
  569. #define MAGIC_NUMBER 19560610
  570. #define DATA_TYPE 1
  571. #define PORT_TYPE 2
  572.  
  573. // this method causes portCoder to reserve space in the first data
  574. // component for (surprise) necessary headers.  This avoids a message
  575. // construction copy in many many cases.
  576.  
  577. - (unsigned)reservedSpaceLength {
  578.     return 16;    // magic, component count, DATA_TYPE, data length
  579. }
  580.  
  581. // portCoder tells us to send a message over the wire.  We're the
  582. // sendPort so go ahead and use our socket.
  583.  
  584. - (BOOL) sendBeforeDate:(NSDate *)limitDate components:(NSMutableArray *)components from:(NSPort *)receivePort reserved:(unsigned)rsvd {
  585.     unsigned counter;
  586.     NSData *firstComponent = [components objectAtIndex:0];
  587.     int *header = (int *)[firstComponent bytes];
  588.     int length = [firstComponent length];
  589.     int written;
  590.  
  591.     if (rsvd != 16) {
  592.         if (enableLogging) NSLog(@"whoops, no header space reserved");
  593.     // XXX be friendlier for more general use
  594.         return NO;
  595.     }
  596.     // prepare magic number
  597.     header[0] = sanitize(MAGIC_NUMBER);
  598.     // prepare number of components, etc.
  599.     header[1] = sanitize([components count]);
  600.     header[2] = sanitize(DATA_TYPE);
  601.     header[3] = sanitize(length-rsvd);
  602.     written = write([readWriter fileDescriptor], [firstComponent bytes], length);
  603.     if (written != length) {
  604.         if (enableLogging) NSLog(@"oops, didn't send the entire first hunk");
  605.         return NO;
  606.     }
  607.     // better schemes might be to collect several of these, use
  608.     // a gathered write, etc.
  609.     for (counter = 1; counter < [components count]; ++counter) {
  610.         id item = [components objectAtIndex:counter];
  611.  
  612.         if ([item isMemberOfClass:[NSData class]]) {
  613.             int length = [item length];
  614.             int stuff[2] = { sanitize(DATA_TYPE), sanitize(length) };
  615.  
  616.             written = write([readWriter fileDescriptor], &stuff, sizeof(stuff));
  617.             if (written != sizeof(stuff)) {
  618.                 if (enableLogging) NSLog(@"oops, didn't send the type");
  619.                 return NO;
  620.             }
  621.             written = write([readWriter fileDescriptor], [item bytes], length);
  622.             if (written != length) {
  623.                 if (enableLogging) NSLog(@"oops, didn't send the data");
  624.                 return NO;
  625.             }
  626.         }
  627.         else if ([item isMemberOfClass:[TCPPort class]]) {
  628.             TCPPort *sp = item;
  629.             int stuff[3] = { sanitize(PORT_TYPE), sanitize(sp->tcpPort), sp->internetAddr };
  630.         if (enableLogging) NSLog(@"writing sanitized port %d addr %d", stuff[1], stuff[2]);           
  631.             written = write([readWriter fileDescriptor], &stuff, sizeof(stuff));
  632.             if (written != sizeof(stuff)) {
  633.                 if (enableLogging) NSLog(@"oops, didn't send the port stuff");
  634.                 return NO;
  635.             }
  636.  
  637.         }
  638.         else {
  639.             if (enableLogging) NSLog(@"can't send component of type %@", [item class]);
  640.             return NO;
  641.         }
  642.     }
  643.     if (enableLogging) NSLog(@"successfully wrote message on %d", [readWriter fileDescriptor]);
  644.     return YES;
  645. }
  646.  
  647. enum PartialState {
  648.     READING_HEADER,
  649.     READING_TYPE,
  650.     READING_DATA_LENGTH,
  651.     READING_DATA,
  652.     READING_PORT,
  653.     DISPATCHING,
  654. };
  655.  
  656. - (void)readNotification:(NSNotification *)note {
  657.     NSData *data = [[note userInfo] objectForKey:NSFileHandleNotificationDataItem];
  658.     if ([data length]) {
  659.     if (enableLogging) NSLog(@"read notification to %@; restarting with %@", self, modes);
  660.         [[note object] readInBackgroundAndNotifyForModes:[modes allObjects]];
  661.     }
  662.     else {
  663.     if (enableLogging) NSLog(@"failed read notification %@", data);
  664.         [self invalidate];
  665.     return;
  666.     }
  667.     [dataStream appendData:[[note userInfo] objectForKey:NSFileHandleNotificationDataItem]];
  668.     for(;;) switch (partialState) {
  669.         case READING_HEADER: {
  670.             int stuff[2];
  671.             if (![dataStream getBytes:&stuff[0] length:sizeof(stuff)]) return;
  672.             if (sanitize(stuff[0]) != MAGIC_NUMBER) {
  673.                 if (enableLogging) NSLog(@"**** didn't get magic number");
  674.                 [self invalidate];
  675.                 return;
  676.             }
  677.             nItems = sanitize(stuff[1]);
  678.             itemCounter = 0;
  679.             if (!items) items = [[NSMutableArray alloc] init];
  680.             partialState = READING_TYPE;
  681.         }
  682.         /* fall through */
  683.     case READING_TYPE: {
  684.             if (![dataStream getBytes:&itemType length:sizeof(itemType)]) return;
  685.             itemType = sanitize(itemType);
  686.             if (itemType == DATA_TYPE)
  687.                 partialState = READING_DATA_LENGTH;
  688.             else if (itemType == PORT_TYPE)
  689.                 partialState = READING_PORT;
  690.             else {
  691.                 if (enableLogging) NSLog(@"**bad type %u", itemType);
  692.                 [self invalidate];
  693.                 return;
  694.             }
  695.             break;
  696.         }
  697.  
  698.     case READING_DATA_LENGTH: {
  699.             if (![dataStream getBytes:&dataLength length:sizeof(dataLength)]) return;
  700.             dataLength = sanitize(dataLength);
  701.             if (dataLength > 64*1024*1024) {
  702.                 if (enableLogging) NSLog(@"**huge data item (%u bytes) error");
  703.                 [self invalidate];
  704.                 return;
  705.             }
  706.             dataItem = [[NSMutableData alloc] init];
  707.             partialState = READING_DATA;
  708.             break;
  709.         }
  710.  
  711.     case READING_DATA: {
  712.             unsigned int available = [dataStream length];
  713.             if (!available) return;
  714.             if (available > dataLength)
  715.                 available = dataLength;
  716.             [dataStream fillData:dataItem length:available];
  717.             dataLength -= available;
  718.             if (dataLength == 0) {
  719.                 [items addObject:dataItem];
  720.                 [dataItem release];
  721.         dataItem = nil;
  722.                 ++itemCounter;
  723.                 if (itemCounter == nItems)
  724.                     partialState = DISPATCHING;
  725.                 else
  726.                     partialState = READING_TYPE;
  727.             }
  728.             break;
  729.         }
  730.  
  731.     case READING_PORT: {
  732.             int stuff[2];
  733.             TCPPort *sp;
  734.  
  735.             if (![dataStream getBytes:stuff length:sizeof(stuff)]) return;
  736.             if (enableLogging) NSLog(@"got sanitized port %d addr %u", sanitize(stuff[0]), stuff[1]);
  737.             sp = [listener connectToNumber:sanitize(stuff[0]) address:stuff[1]];
  738.             if (!sp) {
  739.                 if (enableLogging) NSLog(@"*** didn't get port %d addr %u", sanitize(stuff[0]), stuff[1]);
  740.                 [self invalidate];
  741.                 return;
  742.             }
  743.             [items addObject:sp];
  744.             ++itemCounter;
  745.             if (itemCounter == nItems)
  746.                 partialState = DISPATCHING;
  747.             else
  748.                 partialState = READING_TYPE;
  749.             break;
  750.         }
  751.  
  752.     case DISPATCHING: {
  753.             NSPortCoder *pc = [NSPortCoder portCoderWithReceivePort:listener sendPort:self components:items];
  754.  
  755.         if (enableLogging) NSLog(@"dispatching %@!", pc);
  756.             [pc dispatch];
  757.             [items release];
  758.             items = nil;
  759.             partialState = READING_HEADER;
  760.             if (connection) {
  761.                 [connection release];
  762.                 connection = nil;
  763.             }
  764.             break;
  765.         }
  766.     
  767.     default:
  768.         NSLog(@"bad case %u", partialState);
  769.         return;
  770.     }
  771. }
  772.  
  773. - (void)acceptNotification:(NSNotification *)note {
  774.     NSFileHandle *socket = [[note userInfo] objectForKey:@"NSFileHandleNotificationFileHandleItem"];
  775.     TCPPort *sp;
  776.  
  777.     if (!socket) {
  778.         if (enableLogging) NSLog(@"** no socket in notification info %@", [note userInfo]);
  779.         [self invalidate];
  780.         return;
  781.     }
  782.     else {
  783.         [readWriter acceptConnectionInBackgroundAndNotifyForModes:[modes allObjects]];
  784.     if (enableLogging) NSLog(@"got socket %d from background, accepting for modes %@", [socket fileDescriptor], modes);
  785.     }
  786.     sp = [self tcpPortWithAcceptedSocket:socket];
  787.     if (!sp) {
  788.         if (enableLogging) NSLog(@"didn't set up socket port");
  789.         return;
  790.     }
  791.     connection = [[NSConnection alloc] initWithReceivePort:self sendPort:sp];
  792. }
  793.  
  794. - (NSString *)description {
  795.     return [NSString stringWithFormat:@"<TCPPort %u, addr %u, reader %d, readWriter %d>", tcpPort, internetAddr, [reader fileDescriptor], [readWriter fileDescriptor]];
  796. }
  797. @end
  798.  
  799.  
  800.