home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / mac / tclMacSock.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  68.4 KB  |  2,619 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tclMacSock.c
  3.  *
  4.  *    Channel drivers for Macintosh sockets.
  5.  *
  6.  * Copyright (c) 1996-1997 Sun Microsystems, Inc.
  7.  *
  8.  * See the file "license.terms" for information on usage and redistribution
  9.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  10.  *
  11.  * SCCS: @(#) tclMacSock.c 1.57 97/07/30 18:49:35
  12.  */
  13.  
  14. #include "tclInt.h"
  15. #include "tclPort.h"
  16. #include "tclMacInt.h"
  17. #include <AddressXlation.h>
  18. #include <Aliases.h>
  19. #undef Status
  20. #include <Devices.h>
  21. #include <Errors.h>
  22. #include <Events.h>
  23. #include <Files.h>
  24. #include <Gestalt.h>
  25. #include <MacTCP.h>
  26. #include <Processes.h>
  27. #include <Strings.h>
  28.  
  29. /*
  30.  * The following variable is used to tell whether this module has been
  31.  * initialized.
  32.  */
  33.  
  34. static int initialized = 0;
  35.  
  36. /*
  37.  * If debugging is on we may drop into the debugger to handle certain cases
  38.  * that are not supposed to happen.  Otherwise, we change ignore the error
  39.  * and most code should handle such errors ok.
  40.  */
  41.  
  42. #ifndef TCL_DEBUG
  43.     #define Debugger()
  44. #endif
  45.  
  46. /*
  47.  * The preferred buffer size for Macintosh channels.
  48.  */
  49.  
  50. #define CHANNEL_BUF_SIZE    8192
  51.  
  52. /*
  53.  * Port information structure.  Used to match service names
  54.  * to a Tcp/Ip port number.
  55.  */
  56.  
  57. typedef struct {
  58.     char *name;            /* Name of service. */
  59.     int port;            /* Port number. */
  60. } PortInfo;
  61.  
  62. /*
  63.  * This structure describes per-instance state of a tcp based channel.
  64.  */
  65.  
  66. typedef struct TcpState {
  67.     TCPiopb pb;               /* Parameter block used by this stream. 
  68.                     * This must be in the first position. */
  69.     ProcessSerialNumber    psn;       /* PSN used to wake up process. */
  70.     StreamPtr tcpStream;       /* Macintosh tcp stream pointer. */
  71.     int port;               /* The port we are connected to. */
  72.     int flags;               /* Bit field comprised of the flags
  73.                     * described below.  */
  74.     int checkMask;           /* OR'ed combination of TCL_READABLE and
  75.                     * TCL_WRITABLE as set by an asynchronous
  76.                     * event handler. */
  77.     int watchMask;           /* OR'ed combination of TCL_READABLE and
  78.                     * TCL_WRITABLE as set by Tcl_WatchFile. */
  79.     Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */
  80.     ClientData acceptProcData;       /* The data for the accept proc. */
  81.     rdsEntry rdsarray[5+1];       /* Array used when cleaning out recieve 
  82.                     * buffers on a closing socket. */
  83.     Tcl_Channel channel;       /* Channel associated with this socket. */
  84.     struct TcpState *nextPtr;       /* The next socket on the global socket
  85.                     * list. */
  86. } TcpState;
  87.  
  88. /*
  89.  * This structure is used by domain name resolver callback.
  90.  */
  91.  
  92. typedef struct DNRState {
  93.     struct hostInfo hostInfo;    /* Data structure used by DNR functions. */
  94.     int done;            /* Flag to determine when we are done. */
  95.     ProcessSerialNumber psn;    /* Process to wake up when we are done. */
  96. } DNRState;
  97.  
  98. /*
  99.  * The following macros may be used to set the flags field of
  100.  * a TcpState structure.
  101.  */
  102.  
  103. #define TCP_ASYNC_SOCKET    (1<<0)  /* The socket is in async mode. */
  104. #define TCP_ASYNC_CONNECT    (1<<1)  /* The socket is trying to connect. */
  105. #define TCP_CONNECTED        (1<<2)  /* The socket is connected. */
  106. #define TCP_PENDING        (1<<3)    /* A SocketEvent is on the queue. */
  107. #define TCP_LISTENING         (1<<4)  /* This socket is listening for
  108.                      * a connection. */
  109. #define TCP_LISTEN_CONNECT     (1<<5)  /* Someone has connect to the
  110.                      * listening port. */
  111. #define TCP_REMOTE_CLOSED     (1<<6)  /* The remote side has closed
  112.                      * the connection. */
  113. #define TCP_RELEASE         (1<<7)  /* The socket may now be released. */
  114. #define TCP_WRITING        (1<<8)  /* A background write is in progress. */
  115. #define TCP_SERVER_ZOMBIE    (1<<9)  /* The server can no longer accept connects. */
  116.  
  117. /*
  118.  * The following structure is what is added to the Tcl event queue when
  119.  * a socket event occurs.
  120.  */
  121.  
  122. typedef struct SocketEvent {
  123.     Tcl_Event header;        /* Information that is standard for
  124.                  * all events. */
  125.     TcpState *statePtr;        /* Socket descriptor that is ready. */
  126.     StreamPtr tcpStream;    /* Low level Macintosh stream. */
  127. } SocketEvent;
  128.  
  129. /*
  130.  * Static routines for this file:
  131.  */
  132.  
  133. static pascal void    CleanUpExitProc _ANSI_ARGS_((void));
  134. static void        ClearZombieSockets _ANSI_ARGS_((void));
  135. static void        CloseCompletionRoutine _ANSI_ARGS_((TCPiopb *pb));
  136. static TcpState *    CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
  137.                 int port, char *host, char *myAddr,  int myPort,
  138.                 int server, int async));
  139. static pascal void    DNRCompletionRoutine _ANSI_ARGS_((
  140.                 struct hostInfo *hostinfoPtr,
  141.                 DNRState *dnrStatePtr));
  142. static void        FreeSocketInfo _ANSI_ARGS_((TcpState *statePtr));
  143. static long        GetBufferSize _ANSI_ARGS_((void));
  144. static OSErr        GetHostFromString _ANSI_ARGS_((char *name,
  145.                 ip_addr *address));
  146. static OSErr        GetLocalAddress _ANSI_ARGS_((unsigned long *addr));
  147. static void        IOCompletionRoutine _ANSI_ARGS_((TCPiopb *pb));
  148. static void        InitMacTCPParamBlock _ANSI_ARGS_((TCPiopb *pBlock,
  149.                 int csCode));
  150. static void        InitSockets _ANSI_ARGS_((void));
  151. static TcpState *    NewSocketInfo _ANSI_ARGS_((StreamPtr stream));
  152. static OSErr        ResolveAddress _ANSI_ARGS_((ip_addr tcpAddress,
  153.                 Tcl_DString *dsPtr));
  154. static void        SocketCheckProc _ANSI_ARGS_((ClientData clientData,
  155.                 int flags));
  156. static int        SocketEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  157.                 int flags));
  158. static void        SocketExitHandler _ANSI_ARGS_((ClientData clientData));
  159. static void        SocketFreeProc _ANSI_ARGS_((ClientData clientData));
  160. static int        SocketReady _ANSI_ARGS_((TcpState *statePtr));
  161. static void        SocketSetupProc _ANSI_ARGS_((ClientData clientData,
  162.                 int flags));
  163. static void        TcpAccept _ANSI_ARGS_((TcpState *statePtr));
  164. static int        TcpBlockMode _ANSI_ARGS_((ClientData instanceData, int mode));
  165. static int        TcpClose _ANSI_ARGS_((ClientData instanceData,
  166.                 Tcl_Interp *interp));
  167. static int        TcpGetHandle _ANSI_ARGS_((ClientData instanceData,
  168.                     int direction, ClientData *handlePtr));
  169. static int        TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
  170.                             Tcl_Interp *interp, char *optionName,
  171.                 Tcl_DString *dsPtr));
  172. static int        TcpInput _ANSI_ARGS_((ClientData instanceData,
  173.                 char *buf, int toRead, int *errorCodePtr));
  174. static int        TcpOutput _ANSI_ARGS_((ClientData instanceData,
  175.                 char *buf, int toWrite, int *errorCodePtr));
  176. static void        TcpWatch _ANSI_ARGS_((ClientData instanceData,
  177.                     int mask));
  178. static int        WaitForSocketEvent _ANSI_ARGS_((TcpState *infoPtr,
  179.                     int mask, int *errorCodePtr));
  180.  
  181. /*
  182.  * This structure describes the channel type structure for TCP socket
  183.  * based IO:
  184.  */
  185.  
  186. static Tcl_ChannelType tcpChannelType = {
  187.     "tcp",            /* Type name. */
  188.     TcpBlockMode,        /* Set blocking or
  189.                                  * non-blocking mode.*/
  190.     TcpClose,            /* Close proc. */
  191.     TcpInput,            /* Input proc. */
  192.     TcpOutput,            /* Output proc. */
  193.     NULL,            /* Seek proc. */
  194.     NULL,            /* Set option proc. */
  195.     TcpGetOptionProc,        /* Get option proc. */
  196.     TcpWatch,            /* Initialize notifier. */
  197.     TcpGetHandle        /* Get handles out of channel. */
  198. };
  199.  
  200. /*
  201.  * Universal Procedure Pointers (UPP) for various callback
  202.  * routines used by MacTcp code.
  203.  */
  204.  
  205. ResultUPP resultUPP = NULL;
  206. TCPIOCompletionUPP completeUPP = NULL;
  207. TCPIOCompletionUPP closeUPP = NULL;
  208.  
  209. /*
  210.  * Built-in commands, and the procedures associated with them:
  211.  */
  212.  
  213. static PortInfo portServices[] = {
  214.     {"echo",        7},
  215.     {"discard",        9},
  216.     {"systat",        11},
  217.     {"daytime",        13},
  218.     {"netstat",        15},
  219.     {"chargen",        19},
  220.     {"ftp-data",    20},
  221.     {"ftp",        21},
  222.     {"telnet",        23},
  223.     {"telneto",        24},
  224.     {"smtp",        25},
  225.     {"time",        37},
  226.     {"whois",        43},
  227.     {"domain",        53},
  228.     {"gopher",        70},
  229.     {"finger",        79},
  230.     {"hostnames",    101},
  231.     {"sunrpc",        111},
  232.     {"nntp",        119},
  233.     {"exec",        512},
  234.     {"login",        513},
  235.     {"shell",        514},
  236.     {"printer",        515},
  237.     {"courier",        530},
  238.     {"uucp",        540},
  239.     {NULL,        0},
  240. };
  241.  
  242. /*
  243.  * Every open socket has an entry on the following list.
  244.  */
  245.  
  246. static TcpState *socketList = NULL;
  247.  
  248. /*
  249.  * Globals for holding information about OS support for sockets.
  250.  */
  251.  
  252. static int socketsTestInited = false;
  253. static int hasSockets = false;
  254. static short driverRefNum = 0;
  255. static int socketNumber = 0;
  256. static int socketBufferSize = CHANNEL_BUF_SIZE;
  257. static ProcessSerialNumber applicationPSN;
  258.  
  259. /*
  260.  *----------------------------------------------------------------------
  261.  *
  262.  * InitSockets --
  263.  *
  264.  *    Load the MacTCP driver and open the name resolver.  We also
  265.  *    create several UPP's used by our code.  Lastly, we install
  266.  *    a patch to ExitToShell to clean up socket connections if
  267.  *    we are about to exit.
  268.  *
  269.  * Results:
  270.  *    1 if successful, 0 on failure.
  271.  *
  272.  * Side effects:
  273.  *    Creates a new event source, loads the MacTCP driver,
  274.  *    registers an exit to shell callback.
  275.  *
  276.  *----------------------------------------------------------------------
  277.  */
  278.  
  279. #define gestaltMacTCPVersion 'mtcp'
  280. static void
  281. InitSockets()
  282. {
  283.     ParamBlockRec pb; 
  284.     OSErr err;
  285.     long response;
  286.  
  287.     initialized = 1;
  288.     Tcl_CreateExitHandler(SocketExitHandler, (ClientData) NULL);
  289.     
  290.     if (Gestalt(gestaltMacTCPVersion, &response) == noErr) {
  291.     hasSockets = true;
  292.     } else {
  293.     hasSockets = false;
  294.     }
  295.  
  296.     if (!hasSockets) {
  297.     return;
  298.     }
  299.  
  300.     /*
  301.      * Load MacTcp driver and name server resolver.
  302.      */
  303.     
  304.         
  305.     pb.ioParam.ioCompletion = 0L; 
  306.     pb.ioParam.ioNamePtr = "\p.IPP"; 
  307.     pb.ioParam.ioPermssn = fsCurPerm; 
  308.     err = PBOpenSync(&pb); 
  309.     if (err != noErr) {
  310.     hasSockets = 0;
  311.     return;
  312.     }
  313.     driverRefNum = pb.ioParam.ioRefNum; 
  314.     
  315.     socketBufferSize = GetBufferSize();
  316.     err = OpenResolver(NULL);
  317.     if (err != noErr) {
  318.     hasSockets = 0;
  319.     return;
  320.     }
  321.  
  322.     GetCurrentProcess(&applicationPSN);
  323.     /*
  324.      * Create UPP's for various callback routines.
  325.      */
  326.  
  327.     resultUPP = NewResultProc(DNRCompletionRoutine);
  328.     completeUPP = NewTCPIOCompletionProc(IOCompletionRoutine);
  329.     closeUPP = NewTCPIOCompletionProc(CloseCompletionRoutine);
  330.  
  331.     /*
  332.      * Install an ExitToShell patch.  We use this patch instead
  333.      * of the Tcl exit mechanism because we need to ensure that
  334.      * these routines are cleaned up even if we crash or are forced
  335.      * to quit.  There are some circumstances when the Tcl exit
  336.      * handlers may not fire.
  337.      */
  338.  
  339.     TclMacInstallExitToShellPatch(CleanUpExitProc);
  340.     
  341.     Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
  342.  
  343.     initialized = 1;
  344. }
  345.  
  346. /*
  347.  *----------------------------------------------------------------------
  348.  *
  349.  * SocketExitHandler --
  350.  *
  351.  *    Callback invoked during exit clean up to deinitialize the
  352.  *    socket module.
  353.  *
  354.  * Results:
  355.  *    None.
  356.  *
  357.  * Side effects:
  358.  *    None.
  359.  *
  360.  *----------------------------------------------------------------------
  361.  */
  362.  
  363. static void
  364. SocketExitHandler(
  365.     ClientData clientData)              /* Not used. */
  366. {
  367.     if (hasSockets) {
  368.     Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL);
  369.     /* CleanUpExitProc();
  370.     TclMacDeleteExitToShellPatch(CleanUpExitProc); */
  371.     }
  372.     initialized = 0;
  373. }
  374.  
  375. /*
  376.  *----------------------------------------------------------------------
  377.  *
  378.  * TclHasSockets --
  379.  *
  380.  *    This function determines whether sockets are available on the
  381.  *    current system and returns an error in interp if they are not.
  382.  *    Note that interp may be NULL.
  383.  *
  384.  * Results:
  385.  *    Returns TCL_OK if the system supports sockets, or TCL_ERROR with
  386.  *    an error in interp.
  387.  *
  388.  * Side effects:
  389.  *    None.
  390.  *
  391.  *----------------------------------------------------------------------
  392.  */
  393.  
  394. int
  395. TclHasSockets(
  396.     Tcl_Interp *interp)        /* Interp for error messages. */
  397. {
  398.     if (!initialized) {
  399.     InitSockets();
  400.     }
  401.  
  402.     if (hasSockets) {
  403.     return TCL_OK;
  404.     }
  405.     if (interp != NULL) {
  406.     Tcl_AppendResult(interp, "sockets are not available on this system",
  407.         NULL);
  408.     }
  409.     return TCL_ERROR;
  410. }
  411.  
  412. /*
  413.  *----------------------------------------------------------------------
  414.  *
  415.  * SocketSetupProc --
  416.  *
  417.  *    This procedure is invoked before Tcl_DoOneEvent blocks waiting
  418.  *    for an event.
  419.  *
  420.  * Results:
  421.  *    None.
  422.  *
  423.  * Side effects:
  424.  *    Adjusts the block time if needed.
  425.  *
  426.  *----------------------------------------------------------------------
  427.  */
  428.  
  429. static void
  430. SocketSetupProc(
  431.     ClientData data,        /* Not used. */
  432.     int flags)            /* Event flags as passed to Tcl_DoOneEvent. */
  433. {
  434.     TcpState *statePtr;
  435.     Tcl_Time blockTime = { 0, 0 };
  436.  
  437.     if (!(flags & TCL_FILE_EVENTS)) {
  438.     return;
  439.     }
  440.     
  441.     /*
  442.      * Check to see if there is a ready socket.  If so, poll.
  443.      */
  444.  
  445.     for (statePtr = socketList; statePtr != NULL;
  446.         statePtr = statePtr->nextPtr) {
  447.     if (statePtr->flags & TCP_RELEASE) {
  448.         continue;
  449.     }
  450.     if (SocketReady(statePtr)) {
  451.         Tcl_SetMaxBlockTime(&blockTime);
  452.         break;
  453.     }
  454.     }
  455. }
  456.  
  457. /*
  458.  *----------------------------------------------------------------------
  459.  *
  460.  * SocketCheckProc --
  461.  *
  462.  *    This procedure is called by Tcl_DoOneEvent to check the socket
  463.  *    event source for events. 
  464.  *
  465.  * Results:
  466.  *    None.
  467.  *
  468.  * Side effects:
  469.  *    May queue an event.
  470.  *
  471.  *----------------------------------------------------------------------
  472.  */
  473.  
  474. static void
  475. SocketCheckProc(
  476.     ClientData data,        /* Not used. */
  477.     int flags)            /* Event flags as passed to Tcl_DoOneEvent. */
  478. {
  479.     TcpState *statePtr;
  480.     SocketEvent *evPtr;
  481.     TcpState dummyState;
  482.  
  483.     if (!(flags & TCL_FILE_EVENTS)) {
  484.     return;
  485.     }
  486.     
  487.     /*
  488.      * Queue events for any ready sockets that don't already have events
  489.      * queued (caused by persistent states that won't generate WinSock
  490.      * events).
  491.      */
  492.  
  493.     for (statePtr = socketList; statePtr != NULL;
  494.         statePtr = statePtr->nextPtr) {
  495.     /*
  496.      * Check to see if this socket is dead and needs to be cleaned
  497.      * up.  We use a dummy statePtr whose only valid field is the
  498.      * nextPtr to allow the loop to continue even if the element
  499.      * is deleted.
  500.      */
  501.  
  502.     if (statePtr->flags & TCP_RELEASE) {
  503.         if (!(statePtr->flags & TCP_PENDING)) {
  504.         dummyState.nextPtr = statePtr->nextPtr;
  505.         SocketFreeProc(statePtr);
  506.         statePtr = &dummyState;
  507.         }
  508.         continue;
  509.     }
  510.  
  511.     if (!(statePtr->flags & TCP_PENDING) && SocketReady(statePtr)) {
  512.         statePtr->flags |= TCP_PENDING;
  513.         evPtr = (SocketEvent *) ckalloc(sizeof(SocketEvent));
  514.         evPtr->header.proc = SocketEventProc;
  515.         evPtr->statePtr = statePtr;
  516.         evPtr->tcpStream = statePtr->tcpStream;
  517.         Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
  518.     }
  519.     }
  520. }
  521.  
  522. /*
  523.  *----------------------------------------------------------------------
  524.  *
  525.  * SocketReady --
  526.  *
  527.  *    This function checks the current state of a socket to see
  528.  *    if any interesting conditions are present.
  529.  *
  530.  * Results:
  531.  *    Returns 1 if an event that someone is watching is present, else
  532.  *    returns 0.
  533.  *
  534.  * Side effects:
  535.  *    Updates the checkMask for the socket to reflect any newly
  536.  *    detected events.
  537.  *
  538.  *----------------------------------------------------------------------
  539.  */
  540.  
  541. static int
  542. SocketReady(
  543.     TcpState *statePtr)
  544. {
  545.     TCPiopb statusPB;
  546.     int foundSomething = 0;
  547.     int didStatus = 0;
  548.     int amount;
  549.     OSErr err;
  550.  
  551.     if (statePtr->flags & TCP_LISTEN_CONNECT) {
  552.     foundSomething = 1;
  553.     statePtr->checkMask |= TCL_READABLE;
  554.     }
  555.     if (statePtr->watchMask & TCL_READABLE) {
  556.     if (statePtr->checkMask & TCL_READABLE) {
  557.         foundSomething = 1;
  558.     } else if (statePtr->flags & TCP_CONNECTED) {
  559.         statusPB.ioCRefNum = driverRefNum;
  560.         statusPB.tcpStream = statePtr->tcpStream;
  561.         statusPB.csCode = TCPStatus;
  562.         err = PBControlSync((ParmBlkPtr) &statusPB);
  563.         didStatus = 1;
  564.  
  565.         /*
  566.          * We make the fchannel readable if 1) we get an error,
  567.          * 2) there is more data available, or 3) we detect
  568.          * that a close from the remote connection has arrived.
  569.          */
  570.  
  571.         if ((err != noErr) ||
  572.             (statusPB.csParam.status.amtUnreadData > 0) ||
  573.             (statusPB.csParam.status.connectionState == 14)) {
  574.         statePtr->checkMask |= TCL_READABLE;
  575.         foundSomething = 1;
  576.         }
  577.     }
  578.     }
  579.     if (statePtr->watchMask & TCL_WRITABLE) {
  580.     if (statePtr->checkMask & TCL_WRITABLE) {
  581.         foundSomething = 1;
  582.     } else if (statePtr->flags & TCP_CONNECTED) {
  583.         if (!didStatus) {
  584.         statusPB.ioCRefNum = driverRefNum;
  585.         statusPB.tcpStream = statePtr->tcpStream;
  586.         statusPB.csCode = TCPStatus;
  587.         err = PBControlSync((ParmBlkPtr) &statusPB);
  588.         }
  589.  
  590.         /*
  591.          * If there is an error or there if there is room to
  592.          * send more data we make the channel writeable.
  593.          */
  594.  
  595.         amount = statusPB.csParam.status.sendWindow - 
  596.         statusPB.csParam.status.amtUnackedData;
  597.         if ((err != noErr) || (amount > 0)) {
  598.         statePtr->checkMask |= TCL_WRITABLE;
  599.         foundSomething = 1;
  600.         }
  601.     }
  602.     }
  603.     return foundSomething;
  604. }
  605.  
  606. /*
  607.  *----------------------------------------------------------------------
  608.  *
  609.  * InitMacTCPParamBlock--
  610.  *
  611.  *    Initialize a MacTCP parameter block.
  612.  *
  613.  * Results:
  614.  *    None.
  615.  *
  616.  * Side effects:
  617.  *    Initializes the parameter block.
  618.  *
  619.  *----------------------------------------------------------------------
  620.  */
  621.  
  622. static void
  623. InitMacTCPParamBlock(
  624.     TCPiopb *pBlock,        /* Tcp parmeter block. */
  625.     int csCode)            /* Tcp operation code. */
  626. {
  627.     memset(pBlock, 0, sizeof(TCPiopb));
  628.     pBlock->ioResult = 1;
  629.     pBlock->ioCRefNum = driverRefNum;
  630.     pBlock->csCode = (short) csCode;
  631. }
  632.  
  633. /*
  634.  *----------------------------------------------------------------------
  635.  *
  636.  * TcpBlockMode --
  637.  *
  638.  *    Set blocking or non-blocking mode on channel.
  639.  *
  640.  * Results:
  641.  *    0 if successful, errno when failed.
  642.  *
  643.  * Side effects:
  644.  *    Sets the device into blocking or non-blocking mode.
  645.  *
  646.  *----------------------------------------------------------------------
  647.  */
  648.  
  649. static int
  650. TcpBlockMode(
  651.     ClientData instanceData,         /* Channel state. */
  652.     int mode)                /* The mode to set. */
  653. {
  654.     TcpState *statePtr = (TcpState *) instanceData;
  655.     
  656.     if (mode == TCL_MODE_BLOCKING) {
  657.     statePtr->flags &= ~TCP_ASYNC_SOCKET;
  658.     } else {
  659.     statePtr->flags |= TCP_ASYNC_SOCKET;
  660.     }
  661.     return 0;
  662. }
  663.  
  664. /*
  665.  *----------------------------------------------------------------------
  666.  *
  667.  * TcpClose --
  668.  *
  669.  *    Close the socket.
  670.  *
  671.  * Results:
  672.  *    0 if successful, the value of errno if failed.
  673.  *
  674.  * Side effects:
  675.  *    Closes the socket.
  676.  *
  677.  *----------------------------------------------------------------------
  678.  */
  679.  
  680. static int
  681. TcpClose(
  682.     ClientData instanceData,        /* The socket to close. */
  683.     Tcl_Interp *interp)            /* Interp for error messages. */
  684. {
  685.     TcpState *statePtr = (TcpState *) instanceData;
  686.     StreamPtr tcpStream;
  687.     TCPiopb closePB;
  688.     OSErr err;
  689.  
  690.     tcpStream = statePtr->tcpStream;
  691.     statePtr->flags &= ~TCP_CONNECTED;
  692.     
  693.     /*
  694.      * If this is a server socket we can't use the statePtr
  695.      * param block because it is in use.  However, we can 
  696.      * close syncronously.
  697.      */
  698.  
  699.     if ((statePtr->flags & TCP_LISTENING) ||
  700.         (statePtr->flags & TCP_LISTEN_CONNECT)) {
  701.     InitMacTCPParamBlock(&closePB, TCPClose);
  702.         closePB.tcpStream = tcpStream;
  703.         closePB.ioCompletion = NULL; 
  704.         err = PBControlSync((ParmBlkPtr) &closePB);
  705.         if (err != noErr) {
  706.             Debugger();
  707.             panic("error closing server socket");
  708.         }
  709.     statePtr->flags |= TCP_RELEASE;
  710.  
  711.     /*
  712.      * Server sockets are closed sync.  Therefor, we know it is OK to
  713.      * release the socket now.
  714.      */
  715.  
  716.     InitMacTCPParamBlock(&statePtr->pb, TCPRelease);
  717.     statePtr->pb.tcpStream = statePtr->tcpStream;
  718.     err = PBControlSync((ParmBlkPtr) &statePtr->pb);
  719.     if (err != noErr) {
  720.             panic("error releasing server socket");
  721.     }
  722.  
  723.     /*
  724.      * Free the buffer space used by the socket and the 
  725.      * actual socket state data structure.
  726.      */
  727.  
  728.     ckfree((char *) statePtr->pb.csParam.create.rcvBuff);
  729.     FreeSocketInfo(statePtr);
  730.     return 0;
  731.     }
  732.  
  733.     /*
  734.      * If this socket is in the midddle on async connect we can just
  735.      * abort the connect and release the stream right now.
  736.      */
  737.  
  738.     if (statePtr->flags & TCP_ASYNC_CONNECT) {
  739.     InitMacTCPParamBlock(&closePB, TCPClose);
  740.         closePB.tcpStream = tcpStream;
  741.         closePB.ioCompletion = NULL; 
  742.         err = PBControlSync((ParmBlkPtr) &closePB);
  743.         if (err != noErr) {
  744.             panic("error closing async connect socket");
  745.         }
  746.     statePtr->flags |= TCP_RELEASE;
  747.  
  748.     InitMacTCPParamBlock(&statePtr->pb, TCPRelease);
  749.     statePtr->pb.tcpStream = statePtr->tcpStream;
  750.     err = PBControlSync((ParmBlkPtr) &statePtr->pb);
  751.     if (err != noErr) {
  752.             panic("error releasing async connect socket");
  753.     }
  754.  
  755.     /*
  756.      * Free the buffer space used by the socket and the 
  757.      * actual socket state data structure.
  758.      */
  759.  
  760.     ckfree((char *) statePtr->pb.csParam.create.rcvBuff);
  761.     FreeSocketInfo(statePtr);
  762.     return 0;
  763.     }
  764.  
  765.     /*
  766.      * Client sockets:
  767.      * If a background write is in progress, don't close
  768.      * the socket yet.  The completion routine for the 
  769.      * write will take care of it.
  770.      */
  771.     
  772.     if (!(statePtr->flags & TCP_WRITING)) {
  773.     InitMacTCPParamBlock(&statePtr->pb, TCPClose);
  774.         statePtr->pb.tcpStream = tcpStream;
  775.         statePtr->pb.ioCompletion = closeUPP; 
  776.         statePtr->pb.csParam.close.userDataPtr = (Ptr) statePtr;
  777.         err = PBControlAsync((ParmBlkPtr) &statePtr->pb);
  778.         if (err != noErr) {
  779.         Debugger();
  780.         statePtr->flags |= TCP_RELEASE;
  781.             /* return 0; */
  782.         }
  783.     }
  784.  
  785.     SocketFreeProc(instanceData);
  786.     return 0;
  787. }
  788.  
  789. /*
  790.  *----------------------------------------------------------------------
  791.  *
  792.  * CloseCompletionRoutine --
  793.  *
  794.  *    Handles the close protocol for a Tcp socket.  This will do
  795.  *    a series of calls to release all data currently buffered for
  796.  *    the socket.  This is important to do to as it allows the remote
  797.  *    connection to recieve and issue it's own close on the socket.
  798.  *    Note that this function is running at interupt time and can't
  799.  *    allocate memory or do much else except set state.
  800.  *
  801.  * Results:
  802.  *    None.
  803.  *
  804.  * Side effects:
  805.  *    The buffers for the socket are flushed.
  806.  *
  807.  *----------------------------------------------------------------------
  808.  */
  809.  
  810. static void
  811. CloseCompletionRoutine(
  812.     TCPiopb *pbPtr)        /* Tcp parameter block. */
  813. {
  814.     TcpState *statePtr;
  815.     OSErr err;
  816.     
  817.     if (pbPtr->csCode == TCPClose) {
  818.     statePtr = (TcpState *) (pbPtr->csParam.close.userDataPtr);
  819.     } else {
  820.     statePtr = (TcpState *) (pbPtr->csParam.receive.userDataPtr);
  821.     }
  822.  
  823.     /*
  824.      * It's very bad if the statePtr is nNULL - we should probably panic...
  825.      */
  826.  
  827.     if (statePtr == NULL) {
  828.     Debugger();
  829.     return;
  830.     }
  831.     
  832.     WakeUpProcess(&statePtr->psn);
  833.  
  834.     /*
  835.      * If there is an error we assume the remote side has already
  836.      * close.  We are done closing as soon as we decide that the
  837.      * remote connection has closed.
  838.      */
  839.     
  840.     if (pbPtr->ioResult != noErr) {
  841.     statePtr->flags |= TCP_RELEASE;
  842.     return;
  843.     }
  844.     if (statePtr->flags & TCP_REMOTE_CLOSED) {
  845.     statePtr->flags |= TCP_RELEASE;
  846.     return;
  847.     }
  848.     
  849.     /*
  850.      * If we just did a recieve we need to return the buffers.
  851.      * Otherwise, attempt to recieve more data until we recieve an
  852.      * error (usually because we have no more data).
  853.      */
  854.  
  855.     if (statePtr->pb.csCode == TCPNoCopyRcv) {
  856.     InitMacTCPParamBlock(&statePtr->pb, TCPRcvBfrReturn);
  857.         statePtr->pb.tcpStream = statePtr->tcpStream;
  858.     statePtr->pb.ioCompletion = closeUPP; 
  859.     statePtr->pb.csParam.receive.rdsPtr = (Ptr) statePtr->rdsarray;
  860.         statePtr->pb.csParam.receive.userDataPtr = (Ptr) statePtr;
  861.     err = PBControlAsync((ParmBlkPtr) &statePtr->pb);
  862.     } else {
  863.     InitMacTCPParamBlock(&statePtr->pb, TCPNoCopyRcv);
  864.         statePtr->pb.tcpStream = statePtr->tcpStream;
  865.     statePtr->pb.ioCompletion = closeUPP; 
  866.     statePtr->pb.csParam.receive.commandTimeoutValue = 1;
  867.     statePtr->pb.csParam.receive.rdsPtr = (Ptr) statePtr->rdsarray;
  868.     statePtr->pb.csParam.receive.rdsLength = 5;
  869.         statePtr->pb.csParam.receive.userDataPtr = (Ptr) statePtr;
  870.     err = PBControlAsync((ParmBlkPtr) &statePtr->pb);
  871.     }
  872.  
  873.     if (err != noErr) {
  874.     statePtr->flags |= TCP_RELEASE;
  875.     }
  876. }
  877. /*
  878.  *----------------------------------------------------------------------
  879.  *
  880.  * SocketFreeProc --
  881.  *
  882.  *      This callback is invoked in order to delete
  883.  *      the notifier data associated with a file handle.
  884.  *
  885.  * Results:
  886.  *      None.
  887.  *
  888.  * Side effects:
  889.  *      Removes the SocketInfo from the global socket list.
  890.  *
  891.  *----------------------------------------------------------------------
  892.  */
  893.  
  894. static void
  895. SocketFreeProc(
  896.     ClientData clientData)      /* Channel state. */
  897. {
  898.     TcpState *statePtr = (TcpState *) clientData;
  899.     OSErr err;
  900.     TCPiopb statusPB;
  901.  
  902.     /*
  903.      * Get the status of this connection.  We need to do a
  904.      * few tests to see if it's OK to release the stream now.
  905.      */
  906.  
  907.     if (!(statePtr->flags & TCP_RELEASE)) {
  908.     return;
  909.     }
  910.     statusPB.ioCRefNum = driverRefNum;
  911.     statusPB.tcpStream = statePtr->tcpStream;
  912.     statusPB.csCode = TCPStatus;
  913.     err = PBControlSync((ParmBlkPtr) &statusPB);
  914.     if ((statusPB.csParam.status.connectionState == 0) ||
  915.     (statusPB.csParam.status.connectionState == 2)) {
  916.     /*
  917.      * If the conection state is 0 then this was a client
  918.      * connection and it's closed.  If it is 2 then this a
  919.      * server client and we may release it.  If it isn't
  920.      * one of those values then we return and we'll try to
  921.      * clean up later.
  922.      */
  923.  
  924.     } else {
  925.     return;
  926.     }
  927.     
  928.     /*
  929.      * The Close request is made async.  We know it's
  930.      * OK to release the socket when the TCP_RELEASE flag
  931.      * gets set.
  932.      */
  933.  
  934.     InitMacTCPParamBlock(&statePtr->pb, TCPRelease);
  935.     statePtr->pb.tcpStream = statePtr->tcpStream;
  936.     err = PBControlSync((ParmBlkPtr) &statePtr->pb);
  937.     if (err != noErr) {
  938.         Debugger(); /* Ignoreing leaves stranded stream.  Is there an
  939.                alternative?  */
  940.     }
  941.  
  942.     /*
  943.      * Free the buffer space used by the socket and the 
  944.      * actual socket state data structure.
  945.      */
  946.  
  947.     ckfree((char *) statePtr->pb.csParam.create.rcvBuff);
  948.     FreeSocketInfo(statePtr);
  949. }
  950.  
  951. /*
  952.  *----------------------------------------------------------------------
  953.  *
  954.  * TcpInput --
  955.  *
  956.  *    Reads input from the IO channel into the buffer given. Returns
  957.  *    count of how many bytes were actually read, and an error 
  958.  *    indication.
  959.  *
  960.  * Results:
  961.  *    A count of how many bytes were read is returned.  A value of -1
  962.  *    implies an error occured.  A value of zero means we have reached
  963.  *    the end of data (EOF).
  964.  *
  965.  * Side effects:
  966.  *    Reads input from the actual channel.
  967.  *
  968.  *----------------------------------------------------------------------
  969.  */
  970.  
  971. int
  972. TcpInput(
  973.     ClientData instanceData,        /* Channel state. */
  974.     char *buf,                 /* Where to store data read. */
  975.     int bufSize,             /* How much space is available
  976.                                          * in the buffer? */
  977.     int *errorCodePtr)            /* Where to store error code. */
  978. {
  979.     TcpState *statePtr = (TcpState *) instanceData;
  980.     StreamPtr tcpStream;
  981.     OSErr err;
  982.     TCPiopb statusPB;
  983.     int toRead, dataAvail;
  984.  
  985.     *errorCodePtr = 0;
  986.     errno = 0;
  987.     tcpStream = statePtr->tcpStream;
  988.  
  989.     if (bufSize == 0) {
  990.         return 0;
  991.     }
  992.     toRead = bufSize;
  993.  
  994.     /*
  995.      * First check to see if EOF was already detected, to prevent
  996.      * calling the socket stack after the first time EOF is detected.
  997.      */
  998.  
  999.     if (statePtr->flags & TCP_REMOTE_CLOSED) {
  1000.     return 0;
  1001.     }
  1002.  
  1003.     /*
  1004.      * If an asynchronous connect is in progress, attempt to wait for it
  1005.      * to complete before reading.
  1006.      */
  1007.     
  1008.     if ((statePtr->flags & TCP_ASYNC_CONNECT)
  1009.         && ! WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) {
  1010.     return -1;
  1011.     }
  1012.  
  1013.     /*
  1014.      * No EOF, and it is connected, so try to read more from the socket.
  1015.      * If the socket is blocking, we keep trying until there is data
  1016.      * available or the socket is closed.
  1017.      */
  1018.  
  1019.     while (1) {
  1020.  
  1021.     statusPB.ioCRefNum = driverRefNum;
  1022.     statusPB.tcpStream = tcpStream;
  1023.     statusPB.csCode = TCPStatus;
  1024.     err = PBControlSync((ParmBlkPtr) &statusPB);
  1025.     if (err != noErr) {
  1026.         Debugger();
  1027.         statePtr->flags |= TCP_REMOTE_CLOSED;
  1028.         return 0;    /* EOF */
  1029.     }
  1030.     dataAvail = statusPB.csParam.status.amtUnreadData;
  1031.     if (dataAvail < bufSize) {
  1032.         toRead = dataAvail;
  1033.     } else {
  1034.         toRead = bufSize;
  1035.     }
  1036.     if (toRead != 0) {
  1037.         /*
  1038.          * Try to read the data.
  1039.          */
  1040.         
  1041.         InitMacTCPParamBlock(&statusPB, TCPRcv);
  1042.         statusPB.tcpStream = tcpStream;
  1043.         statusPB.csParam.receive.rcvBuff = buf;
  1044.         statusPB.csParam.receive.rcvBuffLen = toRead;
  1045.         err = PBControlSync((ParmBlkPtr) &statusPB);
  1046.  
  1047.         statePtr->checkMask &= ~TCL_READABLE;
  1048.         switch (err) {
  1049.         case noErr:
  1050.             /*
  1051.              * The channel remains readable only if this read succeds
  1052.              * and we had more data then the size of the buffer we were
  1053.              * trying to fill.  Use the info from the call to status to
  1054.              * determine this.
  1055.              */
  1056.  
  1057.             if (dataAvail > bufSize) {
  1058.             statePtr->checkMask |= TCL_READABLE;
  1059.             }
  1060.             return statusPB.csParam.receive.rcvBuffLen;
  1061.         case connectionClosing:
  1062.             *errorCodePtr = errno = ESHUTDOWN;
  1063.             statePtr->flags |= TCP_REMOTE_CLOSED;
  1064.             return 0;
  1065.         case connectionDoesntExist:
  1066.         case connectionTerminated:
  1067.             *errorCodePtr = errno = ENOTCONN;
  1068.             statePtr->flags |= TCP_REMOTE_CLOSED;
  1069.             return 0;
  1070.         case invalidStreamPtr:
  1071.         default:
  1072.             *errorCodePtr = EINVAL;
  1073.             return -1;
  1074.         }
  1075.     }
  1076.  
  1077.     /*
  1078.      * No data is available, so check the connection state to
  1079.      * see why this is the case.  
  1080.      */
  1081.  
  1082.     if (statusPB.csParam.status.connectionState == 14) {
  1083.         statePtr->flags |= TCP_REMOTE_CLOSED;
  1084.         return 0;
  1085.     }
  1086.     if (statusPB.csParam.status.connectionState != 8) {
  1087.         Debugger();
  1088.     }
  1089.     statePtr->checkMask &= ~TCL_READABLE;
  1090.     if (statePtr->flags & TCP_ASYNC_SOCKET) {
  1091.         *errorCodePtr = EWOULDBLOCK;
  1092.         return -1;
  1093.     }
  1094.  
  1095.     /*
  1096.      * In the blocking case, wait until the file becomes readable
  1097.      * or closed and try again.
  1098.      */
  1099.  
  1100.     if (!WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) {
  1101.         return -1;
  1102.     }
  1103.     }
  1104. }
  1105.  
  1106. /*
  1107.  *----------------------------------------------------------------------
  1108.  *
  1109.  * TcpGetHandle --
  1110.  *
  1111.  *    Called from Tcl_GetChannelFile to retrieve handles from inside
  1112.  *    a file based channel.
  1113.  *
  1114.  * Results:
  1115.  *    The appropriate handle or NULL if not present. 
  1116.  *
  1117.  * Side effects:
  1118.  *    None.
  1119.  *
  1120.  *----------------------------------------------------------------------
  1121.  */
  1122.  
  1123. static int
  1124. TcpGetHandle(
  1125.     ClientData instanceData,        /* The file state. */
  1126.     int direction,            /* Which handle to retrieve? */
  1127.     ClientData *handlePtr)
  1128. {
  1129.     TcpState *statePtr = (TcpState *) instanceData;
  1130.  
  1131.     *handlePtr = (ClientData) statePtr->tcpStream;
  1132.     return TCL_OK;
  1133. }
  1134.  
  1135. /*
  1136.  *----------------------------------------------------------------------
  1137.  *
  1138.  * TcpOutput--
  1139.  *
  1140.  *    Writes the given output on the IO channel. Returns count of how
  1141.  *    many characters were actually written, and an error indication.
  1142.  *
  1143.  * Results:
  1144.  *    A count of how many characters were written is returned and an
  1145.  *    error indication is returned in an output argument.
  1146.  *
  1147.  * Side effects:
  1148.  *    Writes output on the actual channel.
  1149.  *
  1150.  *----------------------------------------------------------------------
  1151.  */
  1152.  
  1153. static int
  1154. TcpOutput(
  1155.     ClientData instanceData,         /* Channel state. */
  1156.     char *buf,                 /* The data buffer. */
  1157.     int toWrite,             /* How many bytes to write? */
  1158.     int *errorCodePtr)            /* Where to store error code. */
  1159. {
  1160.     TcpState *statePtr = (TcpState *) instanceData;
  1161.     StreamPtr tcpStream;
  1162.     OSErr err;
  1163.     int amount;
  1164.     wdsEntry dataSegment[2];
  1165.     TCPiopb statusPB;
  1166.  
  1167.     *errorCodePtr = 0;
  1168.     tcpStream = statePtr->tcpStream;
  1169.  
  1170.     /*
  1171.      * If an asynchronous connect is in progress, attempt to wait for it
  1172.      * to complete before writing.
  1173.      */
  1174.     
  1175.     if ((statePtr->flags & TCP_ASYNC_CONNECT)
  1176.         && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) {
  1177.     return -1;
  1178.     }
  1179.  
  1180.     /*
  1181.      * Loop until we have written some data, or an error occurs.
  1182.      */
  1183.  
  1184.     while (1) {
  1185.     statusPB.ioCRefNum = driverRefNum;
  1186.     statusPB.tcpStream = tcpStream;
  1187.     statusPB.csCode = TCPStatus;
  1188.     err = PBControlSync((ParmBlkPtr) &statusPB);
  1189.     if ((err == connectionDoesntExist) || ((err == noErr) && 
  1190.         (statusPB.csParam.status.connectionState == 14))) {
  1191.         /*
  1192.          * The remote connection is gone away.  Report an error
  1193.          * and don't write anything.
  1194.          */
  1195.  
  1196.         *errorCodePtr = errno = EPIPE;
  1197.         return -1;
  1198.     } else if (err != noErr) {
  1199.         return -1;
  1200.     }
  1201.     amount = statusPB.csParam.status.sendWindow
  1202.         - statusPB.csParam.status.amtUnackedData;
  1203.  
  1204.     /*
  1205.      * Attempt to write the data to the socket if a background
  1206.      * write isn't in progress and there is room in the output buffers.
  1207.      */
  1208.  
  1209.     if (!(statePtr->flags & TCP_WRITING) && amount > 0) {
  1210.         if (toWrite < amount) {
  1211.         amount = toWrite;
  1212.         }
  1213.         dataSegment[0].length = amount;
  1214.         dataSegment[0].ptr = buf;
  1215.         dataSegment[1].length = 0;
  1216.         InitMacTCPParamBlock(&statePtr->pb, TCPSend);
  1217.         statePtr->pb.ioCompletion = completeUPP;
  1218.         statePtr->pb.tcpStream = tcpStream;
  1219.         statePtr->pb.csParam.send.wdsPtr = (Ptr) dataSegment;
  1220.         statePtr->pb.csParam.send.pushFlag = 1;
  1221.         statePtr->pb.csParam.send.userDataPtr = (Ptr) statePtr;
  1222.         statePtr->flags |= TCP_WRITING;
  1223.         err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
  1224.         switch (err) {
  1225.         case noErr:
  1226.             return amount;
  1227.         case connectionClosing:
  1228.             *errorCodePtr = errno = ESHUTDOWN;
  1229.             statePtr->flags |= TCP_REMOTE_CLOSED;
  1230.             return -1;
  1231.         case connectionDoesntExist:
  1232.         case connectionTerminated:
  1233.             *errorCodePtr = errno = ENOTCONN;
  1234.             statePtr->flags |= TCP_REMOTE_CLOSED;
  1235.             return -1;
  1236.         case invalidStreamPtr:
  1237.         default:
  1238.             return -1;
  1239.         }
  1240.  
  1241.     }
  1242.  
  1243.     /*
  1244.      * The socket wasn't writable.  In the non-blocking case, return
  1245.      * immediately, otherwise wait  until the file becomes writable
  1246.      * or closed and try again.
  1247.      */
  1248.  
  1249.     if (statePtr->flags & TCP_ASYNC_SOCKET) {
  1250.         statePtr->checkMask &= ~TCL_WRITABLE;
  1251.         *errorCodePtr = EWOULDBLOCK;
  1252.         return -1;
  1253.     } else if (!WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) {
  1254.         return -1;
  1255.     }
  1256.     }
  1257. }
  1258.  
  1259. /*
  1260.  *----------------------------------------------------------------------
  1261.  *
  1262.  * TcpGetOptionProc --
  1263.  *
  1264.  *    Computes an option value for a TCP socket based channel, or a
  1265.  *    list of all options and their values.
  1266.  *
  1267.  *    Note: This code is based on code contributed by John Haxby.
  1268.  *
  1269.  * Results:
  1270.  *    A standard Tcl result. The value of the specified option or a
  1271.  *    list of all options and    their values is returned in the
  1272.  *    supplied DString.
  1273.  *
  1274.  * Side effects:
  1275.  *    None.
  1276.  *
  1277.  *----------------------------------------------------------------------
  1278.  */
  1279.  
  1280. static int
  1281. TcpGetOptionProc(
  1282.     ClientData instanceData,         /* Socket state. */
  1283.     Tcl_Interp *interp,                 /* For error reporting - can be NULL.*/
  1284.     char *optionName,             /* Name of the option to
  1285.                                          * retrieve the value for, or
  1286.                                          * NULL to get all options and
  1287.                                          * their values. */
  1288.     Tcl_DString *dsPtr)            /* Where to store the computed
  1289.                                          * value; initialized by caller. */
  1290. {
  1291.     TcpState *statePtr = (TcpState *) instanceData;
  1292.     int doPeerName = false, doSockName = false, doAll = false;
  1293.     ip_addr tcpAddress;
  1294.     char buffer[128];
  1295.     OSErr err;
  1296.     Tcl_DString dString;
  1297.     TCPiopb statusPB;
  1298.     int errorCode;
  1299.  
  1300.     /*
  1301.      * If an asynchronous connect is in progress, attempt to wait for it
  1302.      * to complete before accessing the socket state.
  1303.      */
  1304.     
  1305.     if ((statePtr->flags & TCP_ASYNC_CONNECT)
  1306.         && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, &errorCode)) {
  1307.     if (interp) {
  1308.         /*
  1309.          * fix the error message.
  1310.          */
  1311.  
  1312.         Tcl_AppendResult(interp, "connect is in progress and can't wait",
  1313.                 NULL);
  1314.     }
  1315.     return TCL_ERROR;
  1316.     }
  1317.             
  1318.     /*
  1319.      * Determine which options we need to do.  Do all of them
  1320.      * if optionName is NULL.
  1321.      */
  1322.  
  1323.     if (optionName == (char *) NULL || optionName[0] == '\0') {
  1324.         doAll = true;
  1325.     } else {
  1326.     if (!strcmp(optionName, "-peername")) {
  1327.         doPeerName = true;
  1328.     } else if (!strcmp(optionName, "-sockname")) {
  1329.         doSockName = true;
  1330.     } else {
  1331.         return Tcl_BadChannelOption(interp, optionName, 
  1332.                 "peername sockname");
  1333.     }
  1334.     }
  1335.  
  1336.     /*
  1337.      * Get status on the stream.  Make sure to use a new pb struct because
  1338.      * the struct in the statePtr may be part of an asyncronous call.
  1339.      */
  1340.  
  1341.     statusPB.ioCRefNum = driverRefNum;
  1342.     statusPB.tcpStream = statePtr->tcpStream;
  1343.     statusPB.csCode = TCPStatus;
  1344.     err = PBControlSync((ParmBlkPtr) &statusPB);
  1345.     if ((err == connectionDoesntExist) ||
  1346.     ((err == noErr) && (statusPB.csParam.status.connectionState == 14))) {
  1347.     /*
  1348.      * The socket was probably closed on the other side of the connection.
  1349.      */
  1350.  
  1351.     if (interp) {
  1352.         Tcl_AppendResult(interp, "can't access socket info: ",
  1353.                  "connection reset by peer", NULL);
  1354.     }
  1355.     return TCL_ERROR;
  1356.     } else if (err != noErr) {
  1357.     if (interp) { 
  1358.         Tcl_AppendResult(interp, "unknown socket error", NULL);
  1359.     }
  1360.     Debugger();
  1361.     return TCL_ERROR;
  1362.     }
  1363.  
  1364.  
  1365.     /*
  1366.      * Get the sockname for the socket.
  1367.      */
  1368.  
  1369.     Tcl_DStringInit(&dString);
  1370.     if (doAll || doSockName) {
  1371.     if (doAll) {
  1372.         Tcl_DStringAppendElement(dsPtr, "-sockname");
  1373.         Tcl_DStringStartSublist(dsPtr);
  1374.     }
  1375.     tcpAddress = statusPB.csParam.status.localHost;
  1376.     sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24,
  1377.         tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff,
  1378.         tcpAddress & 0xff);
  1379.     Tcl_DStringAppendElement(dsPtr, buffer);
  1380.     if (ResolveAddress(tcpAddress, &dString) == noErr) {
  1381.         Tcl_DStringAppendElement(dsPtr, dString.string);
  1382.     } else {
  1383.         Tcl_DStringAppendElement(dsPtr, "<unknown>");
  1384.     }
  1385.     sprintf(buffer, "%d", statusPB.csParam.status.localPort);
  1386.     Tcl_DStringAppendElement(dsPtr, buffer);
  1387.     if (doAll) {
  1388.         Tcl_DStringEndSublist(dsPtr);
  1389.     }
  1390.     }
  1391.  
  1392.     /*
  1393.      * Get the peername for the socket.
  1394.      */
  1395.  
  1396.     if ((doAll || doPeerName) && (statePtr->flags & TCP_CONNECTED)) {
  1397.     if (doAll) {
  1398.         Tcl_DStringAppendElement(dsPtr, "-peername");
  1399.         Tcl_DStringStartSublist(dsPtr);
  1400.     }
  1401.     tcpAddress = statusPB.csParam.status.remoteHost;
  1402.     sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24,
  1403.         tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff,
  1404.         tcpAddress & 0xff);
  1405.     Tcl_DStringAppendElement(dsPtr, buffer);
  1406.     Tcl_DStringSetLength(&dString, 0);
  1407.     if (ResolveAddress(tcpAddress, &dString) == noErr) {
  1408.         Tcl_DStringAppendElement(dsPtr, dString.string);
  1409.     } else {
  1410.         Tcl_DStringAppendElement(dsPtr, "<unknown>");
  1411.     }
  1412.     sprintf(buffer, "%d", statusPB.csParam.status.remotePort);
  1413.     Tcl_DStringAppendElement(dsPtr, buffer);
  1414.     if (doAll) {
  1415.         Tcl_DStringEndSublist(dsPtr);
  1416.     }
  1417.     }
  1418.  
  1419.     Tcl_DStringFree(&dString);
  1420.     return TCL_OK;
  1421. }
  1422.  
  1423. /*
  1424.  *----------------------------------------------------------------------
  1425.  *
  1426.  * TcpWatch --
  1427.  *
  1428.  *    Initialize the notifier to watch this channel.
  1429.  *
  1430.  * Results:
  1431.  *    None.
  1432.  *
  1433.  * Side effects:
  1434.  *    Sets the watchMask for the channel.
  1435.  *
  1436.  *----------------------------------------------------------------------
  1437.  */
  1438.  
  1439. static void
  1440. TcpWatch(instanceData, mask)
  1441.     ClientData instanceData;        /* The file state. */
  1442.     int mask;                /* Events of interest; an OR-ed
  1443.                                          * combination of TCL_READABLE,
  1444.                                          * TCL_WRITABLE and TCL_EXCEPTION. */
  1445. {
  1446.     TcpState *statePtr = (TcpState *) instanceData;
  1447.  
  1448.     statePtr->watchMask = mask;
  1449. }
  1450.  
  1451. /*
  1452.  *----------------------------------------------------------------------
  1453.  *
  1454.  * NewSocketInfo --
  1455.  *
  1456.  *    This function allocates and initializes a new SocketInfo
  1457.  *    structure.
  1458.  *
  1459.  * Results:
  1460.  *    Returns a newly allocated SocketInfo.
  1461.  *
  1462.  * Side effects:
  1463.  *    Adds the socket to the global socket list, allocates memory.
  1464.  *
  1465.  *----------------------------------------------------------------------
  1466.  */
  1467.  
  1468. static TcpState *
  1469. NewSocketInfo(
  1470.     StreamPtr tcpStream)
  1471. {
  1472.     TcpState *statePtr;
  1473.  
  1474.     statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
  1475.     statePtr->tcpStream = tcpStream;
  1476.     statePtr->psn = applicationPSN;
  1477.     statePtr->flags = 0;
  1478.     statePtr->checkMask = 0;
  1479.     statePtr->watchMask = 0;
  1480.     statePtr->acceptProc = (Tcl_TcpAcceptProc *) NULL;
  1481.     statePtr->acceptProcData = (ClientData) NULL;
  1482.     statePtr->nextPtr = socketList;
  1483.     socketList = statePtr;
  1484.     return statePtr;
  1485. }
  1486.  
  1487. /*
  1488.  *----------------------------------------------------------------------
  1489.  *
  1490.  * FreeSocketInfo --
  1491.  *
  1492.  *    This function deallocates a SocketInfo structure that is no
  1493.  *    longer needed.
  1494.  *
  1495.  * Results:
  1496.  *    None.
  1497.  *
  1498.  * Side effects:
  1499.  *    Removes the socket from the global socket list, frees memory.
  1500.  *
  1501.  *----------------------------------------------------------------------
  1502.  */
  1503.  
  1504. static void
  1505. FreeSocketInfo(
  1506.     TcpState *statePtr)        /* The state pointer to free. */
  1507. {
  1508.     if (statePtr == socketList) {
  1509.     socketList = statePtr->nextPtr;
  1510.     } else {
  1511.     TcpState *p;
  1512.     for (p = socketList; p != NULL; p = p->nextPtr) {
  1513.         if (p->nextPtr == statePtr) {
  1514.         p->nextPtr = statePtr->nextPtr;
  1515.         break;
  1516.         }
  1517.     }
  1518.     }
  1519.     ckfree((char *) statePtr);
  1520. }
  1521.  
  1522. /*
  1523.  *----------------------------------------------------------------------
  1524.  *
  1525.  * Tcl_MakeTcpClientChannel --
  1526.  *
  1527.  *    Creates a Tcl_Channel from an existing client TCP socket.
  1528.  *
  1529.  * Results:
  1530.  *    The Tcl_Channel wrapped around the preexisting TCP socket.
  1531.  *
  1532.  * Side effects:
  1533.  *    None.
  1534.  *
  1535.  *----------------------------------------------------------------------
  1536.  */
  1537.  
  1538. Tcl_Channel
  1539. Tcl_MakeTcpClientChannel(
  1540.     ClientData sock)    /* The socket to wrap up into a channel. */
  1541. {
  1542.     TcpState *statePtr;
  1543.     char channelName[20];
  1544.  
  1545.     if (TclHasSockets(NULL) != TCL_OK) {
  1546.     return NULL;
  1547.     }
  1548.     
  1549.     statePtr = NewSocketInfo((StreamPtr) sock);
  1550.     /* TODO: do we need to set the port??? */
  1551.     
  1552.     sprintf(channelName, "sock%d", socketNumber++);
  1553.     
  1554.     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1555.             (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
  1556.     Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize);
  1557.     Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
  1558.     return statePtr->channel;
  1559. }
  1560.  
  1561. /*
  1562.  *----------------------------------------------------------------------
  1563.  *
  1564.  * CreateSocket --
  1565.  *
  1566.  *    This function opens a new socket and initializes the
  1567.  *    SocketInfo structure.
  1568.  *
  1569.  * Results:
  1570.  *    Returns a new SocketInfo, or NULL with an error in interp.
  1571.  *
  1572.  * Side effects:
  1573.  *    Adds a new socket to the socketList.
  1574.  *
  1575.  *----------------------------------------------------------------------
  1576.  */
  1577.  
  1578. static TcpState *
  1579. CreateSocket(
  1580.     Tcl_Interp *interp,        /* For error reporting; can be NULL. */
  1581.     int port,            /* Port number to open. */
  1582.     char *host,            /* Name of host on which to open port. */
  1583.     char *myaddr,        /* Optional client-side address */
  1584.     int myport,            /* Optional client-side port */
  1585.     int server,            /* 1 if socket should be a server socket,
  1586.                  * else 0 for a client socket. */
  1587.     int async)            /* 1 create async, 0 do sync. */
  1588. {
  1589.     ip_addr macAddr;
  1590.     OSErr err;
  1591.     TCPiopb pb;
  1592.     StreamPtr tcpStream;
  1593.     TcpState *statePtr;
  1594.     char * buffer;
  1595.     
  1596.     /*
  1597.      * Figure out the ip address from the host string.
  1598.      */
  1599.  
  1600.     if (host == NULL) {
  1601.     err = GetLocalAddress(&macAddr);
  1602.     } else {
  1603.     err = GetHostFromString(host, &macAddr);
  1604.     }
  1605.     if (err != noErr) {
  1606.     Tcl_SetErrno(EHOSTUNREACH);
  1607.     if (interp != (Tcl_Interp *) NULL) {
  1608.         Tcl_AppendResult(interp, "couldn't open socket: ",
  1609.                         Tcl_PosixError(interp), (char *) NULL);
  1610.     }
  1611.     return (TcpState *) NULL;
  1612.     }
  1613.     
  1614.     /*
  1615.      * Create a MacTCP stream and create the state used for socket
  1616.      * transactions from here on out.
  1617.      */
  1618.  
  1619.     ClearZombieSockets();
  1620.     buffer = ckalloc(socketBufferSize);
  1621.     InitMacTCPParamBlock(&pb, TCPCreate);
  1622.     pb.csParam.create.rcvBuff = buffer;
  1623.     pb.csParam.create.rcvBuffLen = socketBufferSize;
  1624.     err = PBControlSync((ParmBlkPtr) &pb);
  1625.     if (err != noErr) {
  1626.         Tcl_SetErrno(0); /* TODO: set to ENOSR - maybe?*/
  1627.         if (interp != (Tcl_Interp *) NULL) {
  1628.         Tcl_AppendResult(interp, "couldn't open socket: ",
  1629.         Tcl_PosixError(interp), (char *) NULL);
  1630.         }
  1631.     return (TcpState *) NULL;
  1632.     }
  1633.  
  1634.     tcpStream = pb.tcpStream;
  1635.     statePtr = NewSocketInfo(tcpStream);
  1636.     statePtr->port = port;
  1637.     
  1638.     if (server) {
  1639.         /* 
  1640.          * Set up server connection.
  1641.          */
  1642.  
  1643.     InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen);
  1644.     statePtr->pb.tcpStream = tcpStream;
  1645.     statePtr->pb.csParam.open.localPort = statePtr->port;
  1646.     statePtr->pb.ioCompletion = completeUPP; 
  1647.     statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr;
  1648.     statePtr->flags |= TCP_LISTENING;
  1649.     err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
  1650.  
  1651.     /*
  1652.      * If this is a server on port 0 then we need to wait until
  1653.      * the dynamic port allocation is made by the MacTcp driver.
  1654.      */
  1655.  
  1656.     if (statePtr->port == 0) {
  1657.         EventRecord dummy;
  1658.  
  1659.         while (statePtr->pb.csParam.open.localPort == 0) {
  1660.         WaitNextEvent(0, &dummy, 1, NULL);
  1661.         if (statePtr->pb.ioResult != 0) {
  1662.             break;
  1663.         }
  1664.         }
  1665.         statePtr->port = statePtr->pb.csParam.open.localPort;
  1666.     }
  1667.     Tcl_SetErrno(EINPROGRESS);
  1668.     } else {
  1669.     /*
  1670.      * Attempt to connect. The connect may fail at present with an
  1671.      * EINPROGRESS but at a later time it will complete. The caller
  1672.      * will set up a file handler on the socket if she is interested in
  1673.      * being informed when the connect completes.
  1674.      */
  1675.  
  1676.     InitMacTCPParamBlock(&statePtr->pb, TCPActiveOpen);
  1677.     statePtr->pb.tcpStream = tcpStream;
  1678.     statePtr->pb.csParam.open.remoteHost = macAddr;
  1679.     statePtr->pb.csParam.open.remotePort = port;
  1680.     statePtr->pb.csParam.open.localHost = 0;
  1681.     statePtr->pb.csParam.open.localPort = myport;
  1682.     statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr;
  1683.     statePtr->pb.ioCompletion = completeUPP;
  1684.     if (async) {
  1685.         statePtr->flags |= TCP_ASYNC_CONNECT;
  1686.         err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
  1687.         Tcl_SetErrno(EINPROGRESS);
  1688.     } else {
  1689.         err = PBControlSync((ParmBlkPtr) &(statePtr->pb));
  1690.     }
  1691.     }
  1692.     
  1693.     switch (err) {
  1694.     case noErr:
  1695.         if (!async) {
  1696.         statePtr->flags |= TCP_CONNECTED;
  1697.         }
  1698.         return statePtr;
  1699.     case duplicateSocket:
  1700.         Tcl_SetErrno(EADDRINUSE);
  1701.         break;
  1702.     case openFailed:
  1703.     case connectionTerminated:
  1704.         Tcl_SetErrno(ECONNREFUSED);
  1705.         break;
  1706.     case invalidStreamPtr:
  1707.     case connectionExists:
  1708.     default:
  1709.         /*
  1710.          * These cases should never occur.  However, we will fail
  1711.          * gracefully and hope Tcl can resume.  The alternative is to panic
  1712.          * which is probably a bit drastic.
  1713.          */
  1714.  
  1715.         Debugger();
  1716.         Tcl_SetErrno(err);
  1717.     }
  1718.  
  1719.     /*
  1720.      * We had error during the connection.  Release the stream
  1721.      * and file handle.  Also report to the interp.
  1722.      */
  1723.  
  1724.     pb.ioCRefNum = driverRefNum;
  1725.     pb.csCode = TCPRelease;
  1726.     pb.tcpStream = tcpStream;
  1727.     pb.ioCompletion = NULL; 
  1728.     err = PBControlSync((ParmBlkPtr) &pb);
  1729.  
  1730.     if (interp != (Tcl_Interp *) NULL) {
  1731.     Tcl_AppendResult(interp, "couldn't open socket: ",
  1732.         Tcl_PosixError(interp), (char *) NULL);
  1733.     }
  1734.  
  1735.     ckfree(buffer);
  1736.     FreeSocketInfo(statePtr);
  1737.     return (TcpState *) NULL;
  1738. }
  1739.  
  1740. /*
  1741.  *----------------------------------------------------------------------
  1742.  *
  1743.  * Tcl_OpenTcpClient --
  1744.  *
  1745.  *    Opens a TCP client socket and creates a channel around it.
  1746.  *
  1747.  * Results:
  1748.  *    The channel or NULL if failed. On failure, the routine also
  1749.  *    sets the output argument errorCodePtr to the error code.
  1750.  *
  1751.  * Side effects:
  1752.  *    Opens a client socket and creates a new channel.
  1753.  *
  1754.  *----------------------------------------------------------------------
  1755.  */
  1756.  
  1757. Tcl_Channel
  1758. Tcl_OpenTcpClient(
  1759.     Tcl_Interp *interp,         /* For error reporting; can be NULL. */
  1760.     int port,                 /* Port number to open. */
  1761.     char *host,             /* Host on which to open port. */
  1762.     char *myaddr,             /* Client-side address */
  1763.     int myport,             /* Client-side port */
  1764.     int async)                /* If nonzero, attempt to do an
  1765.                                          * asynchronous connect. Otherwise
  1766.                                          * we do a blocking connect. 
  1767.                                          * - currently ignored */
  1768. {
  1769.     TcpState *statePtr;
  1770.     char channelName[20];
  1771.  
  1772.     if (TclHasSockets(interp) != TCL_OK) {
  1773.     return NULL;
  1774.     }
  1775.     
  1776.     /*
  1777.      * Create a new client socket and wrap it in a channel.
  1778.      */
  1779.  
  1780.     statePtr = CreateSocket(interp, port, host, myaddr, myport, 0, async);
  1781.     if (statePtr == NULL) {
  1782.     return NULL;
  1783.     }
  1784.     
  1785.     sprintf(channelName, "sock%d", socketNumber++);
  1786.  
  1787.     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1788.             (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
  1789.     Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize);
  1790.     Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
  1791.     return statePtr->channel;
  1792. }
  1793.  
  1794. /*
  1795.  *----------------------------------------------------------------------
  1796.  *
  1797.  * Tcl_OpenTcpServer --
  1798.  *
  1799.  *    Opens a TCP server socket and creates a channel around it.
  1800.  *
  1801.  * Results:
  1802.  *    The channel or NULL if failed.
  1803.  *
  1804.  * Side effects:
  1805.  *    Opens a server socket and creates a new channel.
  1806.  *
  1807.  *----------------------------------------------------------------------
  1808.  */
  1809.  
  1810. Tcl_Channel
  1811. Tcl_OpenTcpServer(
  1812.     Tcl_Interp *interp,            /* For error reporting - may be
  1813.                                          * NULL. */
  1814.     int port,                /* Port number to open. */
  1815.     char *host,                /* Name of local host. */
  1816.     Tcl_TcpAcceptProc *acceptProc,    /* Callback for accepting connections
  1817.                                          * from new clients. */
  1818.     ClientData acceptProcData)        /* Data for the callback. */
  1819. {
  1820.     TcpState *statePtr;
  1821.     char channelName[20];
  1822.  
  1823.     if (TclHasSockets(interp) != TCL_OK) {
  1824.     return NULL;
  1825.     }
  1826.  
  1827.     /*
  1828.      * Create a new client socket and wrap it in a channel.
  1829.      */
  1830.  
  1831.     statePtr = CreateSocket(interp, port, host, NULL, 0, 1, 1);
  1832.     if (statePtr == NULL) {
  1833.     return NULL;
  1834.     }
  1835.  
  1836.     statePtr->acceptProc = acceptProc;
  1837.     statePtr->acceptProcData = acceptProcData;
  1838.  
  1839.     sprintf(channelName, "sock%d", socketNumber++);
  1840.  
  1841.     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  1842.             (ClientData) statePtr, 0);
  1843.     Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize);
  1844.     Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
  1845.     return statePtr->channel;
  1846. }
  1847.  
  1848. /*
  1849.  *----------------------------------------------------------------------
  1850.  *
  1851.  * SocketEventProc --
  1852.  *
  1853.  *    This procedure is called by Tcl_ServiceEvent when a socket event
  1854.  *    reaches the front of the event queue.  This procedure is
  1855.  *    responsible for notifying the generic channel code.
  1856.  *
  1857.  * Results:
  1858.  *    Returns 1 if the event was handled, meaning it should be removed
  1859.  *    from the queue.  Returns 0 if the event was not handled, meaning
  1860.  *    it should stay on the queue.  The only time the event isn't
  1861.  *    handled is if the TCL_FILE_EVENTS flag bit isn't set.
  1862.  *
  1863.  * Side effects:
  1864.  *    Whatever the channel callback procedures do.
  1865.  *
  1866.  *----------------------------------------------------------------------
  1867.  */
  1868.  
  1869. static int
  1870. SocketEventProc(
  1871.     Tcl_Event *evPtr,        /* Event to service. */
  1872.     int flags)            /* Flags that indicate what events to
  1873.                  * handle, such as TCL_FILE_EVENTS. */
  1874. {
  1875.     TcpState *statePtr;
  1876.     SocketEvent *eventPtr = (SocketEvent *) evPtr;
  1877.     int mask = 0;
  1878.  
  1879.     if (!(flags & TCL_FILE_EVENTS)) {
  1880.     return 0;
  1881.     }
  1882.  
  1883.     /*
  1884.      * Find the specified socket on the socket list.
  1885.      */
  1886.  
  1887.     for (statePtr = socketList; statePtr != NULL;
  1888.         statePtr = statePtr->nextPtr) {
  1889.     if ((statePtr == eventPtr->statePtr) && 
  1890.         (statePtr->tcpStream == eventPtr->tcpStream)) {
  1891.         break;
  1892.     }
  1893.     }
  1894.  
  1895.     /*
  1896.      * Discard events that have gone stale.
  1897.      */
  1898.  
  1899.     if (!statePtr) {
  1900.     return 1;
  1901.     }
  1902.     statePtr->flags &= ~(TCP_PENDING);
  1903.     if (statePtr->flags & TCP_RELEASE) {
  1904.     SocketFreeProc(statePtr);
  1905.     return 1;
  1906.     }
  1907.  
  1908.  
  1909.     /*
  1910.      * Handle connection requests directly.
  1911.      */
  1912.  
  1913.     if (statePtr->flags & TCP_LISTEN_CONNECT) {
  1914.     if (statePtr->checkMask & TCL_READABLE) {
  1915.         TcpAccept(statePtr);
  1916.     }
  1917.     return 1;
  1918.     }
  1919.  
  1920.     /*
  1921.      * Mask off unwanted events then notify the channel.
  1922.      */
  1923.  
  1924.     mask = statePtr->checkMask & statePtr->watchMask;
  1925.     if (mask) {
  1926.     Tcl_NotifyChannel(statePtr->channel, mask);
  1927.     }
  1928.     return 1;
  1929. }
  1930.  
  1931. /*
  1932.  *----------------------------------------------------------------------
  1933.  *
  1934.  * WaitForSocketEvent --
  1935.  *
  1936.  *    Waits until one of the specified events occurs on a socket.
  1937.  *
  1938.  * Results:
  1939.  *    Returns 1 on success or 0 on failure, with an error code in
  1940.  *    errorCodePtr.
  1941.  *
  1942.  * Side effects:
  1943.  *    Processes socket events off the system queue.
  1944.  *
  1945.  *----------------------------------------------------------------------
  1946.  */
  1947.  
  1948. static int
  1949. WaitForSocketEvent(
  1950.     TcpState *statePtr,        /* Information about this socket. */
  1951.     int mask,            /* Events to look for. */
  1952.     int *errorCodePtr)        /* Where to store errors? */
  1953. {
  1954.     OSErr err;
  1955.     TCPiopb statusPB;
  1956.     EventRecord dummy;
  1957.  
  1958.     /*
  1959.      * Loop until we get the specified condition, unless the socket is
  1960.      * asynchronous.
  1961.      */
  1962.     
  1963.     do {
  1964.     statusPB.ioCRefNum = driverRefNum;
  1965.     statusPB.tcpStream = statePtr->tcpStream;
  1966.     statusPB.csCode = TCPStatus;
  1967.     err = PBControlSync((ParmBlkPtr) &statusPB);
  1968.     if (err != noErr) {
  1969.         statePtr->checkMask |= (TCL_READABLE | TCL_WRITABLE);
  1970.         return 1;
  1971.     }
  1972.     statePtr->checkMask = 0;
  1973.     if (statusPB.csParam.status.amtUnreadData > 0) {
  1974.         statePtr->checkMask |= TCL_READABLE;
  1975.     }
  1976.     if (!(statePtr->flags & TCP_WRITING)
  1977.         && (statusPB.csParam.status.sendWindow - 
  1978.             statusPB.csParam.status.amtUnackedData) > 0) {
  1979.         statePtr->flags &= ~(TCP_ASYNC_CONNECT);
  1980.         statePtr->checkMask |= TCL_WRITABLE;
  1981.     }
  1982.     if (mask & statePtr->checkMask) {
  1983.         return 1;
  1984.     }
  1985.  
  1986.     /*
  1987.      * Call the system to let other applications run while we
  1988.      * are waiting for this event to occur.
  1989.      */
  1990.     
  1991.     WaitNextEvent(0, &dummy, 1, NULL);
  1992.     } while (!(statePtr->flags & TCP_ASYNC_SOCKET));
  1993.     *errorCodePtr = EWOULDBLOCK;
  1994.     return 0;
  1995.  
  1996. /*
  1997.  *----------------------------------------------------------------------
  1998.  *
  1999.  * TcpAccept --
  2000.  *    Accept a TCP socket connection.  This is called by the event 
  2001.  *    loop, and it in turns calls any registered callbacks for this
  2002.  *    channel.
  2003.  *
  2004.  * Results:
  2005.  *    None.
  2006.  *
  2007.  * Side effects:
  2008.  *    Evals the Tcl script associated with the server socket.
  2009.  *
  2010.  *----------------------------------------------------------------------
  2011.  */
  2012.  
  2013. static void
  2014. TcpAccept(
  2015.     TcpState *statePtr)
  2016. {
  2017.     TcpState *newStatePtr;
  2018.     StreamPtr tcpStream;
  2019.     char remoteHostname[255];
  2020.     OSErr err;
  2021.     ip_addr remoteAddress;
  2022.     long remotePort;
  2023.     char channelName[20];
  2024.     
  2025.     statePtr->flags &= ~TCP_LISTEN_CONNECT;
  2026.     statePtr->checkMask &= ~TCL_READABLE;
  2027.  
  2028.     /*
  2029.      * Transfer sever stream to new connection.
  2030.      */
  2031.  
  2032.     tcpStream = statePtr->tcpStream;
  2033.     newStatePtr = NewSocketInfo(tcpStream);
  2034.     newStatePtr->tcpStream = tcpStream;
  2035.     sprintf(channelName, "sock%d", socketNumber++);
  2036.  
  2037.  
  2038.     newStatePtr->flags |= TCP_CONNECTED;
  2039.     newStatePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  2040.             (ClientData) newStatePtr, (TCL_READABLE | TCL_WRITABLE));
  2041.     Tcl_SetChannelBufferSize(newStatePtr->channel, socketBufferSize);
  2042.     Tcl_SetChannelOption(NULL, newStatePtr->channel, "-translation",
  2043.         "auto crlf");
  2044.  
  2045.     remoteAddress = statePtr->pb.csParam.open.remoteHost;
  2046.     remotePort = statePtr->pb.csParam.open.remotePort;
  2047.  
  2048.     /*
  2049.      * Reopen passive connect.  Make new tcpStream the server.
  2050.      */
  2051.  
  2052.     ClearZombieSockets();
  2053.     InitMacTCPParamBlock(&statePtr->pb, TCPCreate);
  2054.     statePtr->pb.csParam.create.rcvBuff = ckalloc(socketBufferSize);
  2055.     statePtr->pb.csParam.create.rcvBuffLen = socketBufferSize;
  2056.     err = PBControlSync((ParmBlkPtr) &statePtr->pb);
  2057.     if (err != noErr) {
  2058.     /* 
  2059.      * Hmmm...  We can't reopen the server.  We'll go ahead
  2060.      * an continue - but we are kind of broken now...
  2061.      */
  2062.      Debugger();
  2063.      statePtr->tcpStream = -1;
  2064.      statePtr->flags |= TCP_SERVER_ZOMBIE;
  2065.     }
  2066.  
  2067.     tcpStream = statePtr->tcpStream = statePtr->pb.tcpStream;
  2068.     
  2069.     InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen);
  2070.     statePtr->pb.tcpStream = tcpStream;
  2071.     statePtr->pb.csParam.open.localHost = 0;
  2072.     statePtr->pb.csParam.open.localPort = statePtr->port;
  2073.     statePtr->pb.ioCompletion = completeUPP; 
  2074.     statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr;
  2075.     statePtr->flags |= TCP_LISTENING;
  2076.     err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
  2077.     /*
  2078.      * TODO: deal with case where we can't recreate server socket...
  2079.      */
  2080.  
  2081.     /*
  2082.      * Finally we run the accept procedure.  We must do this last to make
  2083.      * sure we are in a nice clean state.  This Tcl code can do anything
  2084.      * including closing the server or client sockets we've just delt with.
  2085.      */
  2086.  
  2087.     if (statePtr->acceptProc != NULL) {
  2088.     sprintf(remoteHostname, "%d.%d.%d.%d", remoteAddress>>24,
  2089.         remoteAddress>>16 & 0xff, remoteAddress>>8 & 0xff,
  2090.         remoteAddress & 0xff);
  2091.         
  2092.     (statePtr->acceptProc)(statePtr->acceptProcData, newStatePtr->channel, 
  2093.         remoteHostname, remotePort);
  2094.     }
  2095. }
  2096.  
  2097. /*
  2098.  *----------------------------------------------------------------------
  2099.  *
  2100.  * Tcl_GetHostName --
  2101.  *
  2102.  *    Returns the name of the local host.  The result is cached to
  2103.  *    be speedy after the first call.
  2104.  *
  2105.  * Results:
  2106.  *    Returns a string containing the host name, or NULL on error.
  2107.  *    The returned string must be freed by the caller.
  2108.  *
  2109.  * Side effects:
  2110.  *    None.
  2111.  *
  2112.  *----------------------------------------------------------------------
  2113.  */
  2114.  
  2115. char *
  2116. Tcl_GetHostName()
  2117. {
  2118.     static int  hostnameInited = 0;
  2119.     static char hostname[255];
  2120.     ip_addr ourAddress;
  2121.     Tcl_DString dString;
  2122.     OSErr err;
  2123.     
  2124.     if (hostnameInited) {
  2125.         return hostname;
  2126.     }
  2127.     
  2128.     if (TclHasSockets(NULL) != TCL_OK) {
  2129.     hostname[0] = '\0';
  2130.         hostnameInited = 1;
  2131.     return hostname;
  2132.     }
  2133.  
  2134.     err = GetLocalAddress(&ourAddress);
  2135.  
  2136.     if (err == noErr) {
  2137.         /*
  2138.          * Search for the doman name and return it if found.  Otherwise, 
  2139.          * just print the IP number to a string and return that.
  2140.          */
  2141.  
  2142.         Tcl_DStringInit(&dString);
  2143.         err = ResolveAddress(ourAddress, &dString);
  2144.     if (err == noErr) {
  2145.         strcpy(hostname, dString.string);
  2146.     } else {
  2147.         sprintf(hostname, "%d.%d.%d.%d", ourAddress>>24, ourAddress>>16 & 0xff,
  2148.         ourAddress>>8 & 0xff, ourAddress & 0xff);
  2149.     }
  2150.     Tcl_DStringFree(&dString);
  2151.     
  2152.         hostnameInited = 1;
  2153.         return hostname;
  2154.     }
  2155.     
  2156.     return (char *) NULL;
  2157. }
  2158.  
  2159. /*
  2160.  *----------------------------------------------------------------------
  2161.  *
  2162.  * ResolveAddress --
  2163.  *
  2164.  *    This function is used to resolve an ip address to it's full 
  2165.  *    domain name address.
  2166.  *
  2167.  * Results:
  2168.  *    An os err value.
  2169.  *
  2170.  * Side effects:
  2171.  *    Treats client data as int we set to true.
  2172.  *
  2173.  *----------------------------------------------------------------------
  2174.  */
  2175.  
  2176. static OSErr 
  2177. ResolveAddress(
  2178.     ip_addr tcpAddress,     /* Address to resolve. */
  2179.     Tcl_DString *dsPtr)        /* Returned address in string. */
  2180. {
  2181.     int i;
  2182.     EventRecord dummy;
  2183.     DNRState dnrState;
  2184.     OSErr err;
  2185.  
  2186.     /*
  2187.      * Call AddrToName to resolve our ip address to our domain name.
  2188.      * The call is async, so we must wait for a callback to tell us
  2189.      * when to continue.
  2190.      */
  2191.  
  2192.      for (i = 0; i < NUM_ALT_ADDRS; i++) {
  2193.     dnrState.hostInfo.addr[i] = 0;
  2194.      }
  2195.     dnrState.done = 0;
  2196.     GetCurrentProcess(&(dnrState.psn));
  2197.     err = AddrToName(tcpAddress, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
  2198.     if (err == cacheFault) {
  2199.     while (!dnrState.done) {
  2200.         WaitNextEvent(0, &dummy, 1, NULL);
  2201.     }
  2202.     }
  2203.     
  2204.     /*
  2205.      * If there is no error in finding the domain name we set the
  2206.      * result into the dynamic string.  We also work around a bug in
  2207.      * MacTcp where an extranious '.' may be found at the end of the name.
  2208.      */
  2209.  
  2210.     if (dnrState.hostInfo.rtnCode == noErr) {
  2211.     i = strlen(dnrState.hostInfo.cname) - 1;
  2212.     if (dnrState.hostInfo.cname[i] == '.') {
  2213.         dnrState.hostInfo.cname[i] = '\0';
  2214.     }
  2215.     Tcl_DStringAppend(dsPtr, dnrState.hostInfo.cname, -1);
  2216.     }
  2217.     
  2218.     return dnrState.hostInfo.rtnCode;
  2219. }
  2220.  
  2221. /*
  2222.  *----------------------------------------------------------------------
  2223.  *
  2224.  * DNRCompletionRoutine --
  2225.  *
  2226.  *    This function is called when the Domain Name Server is done
  2227.  *    seviceing our request.  It just sets a flag that we can poll
  2228.  *    in functions like Tcl_GetHostName to let them know to continue.
  2229.  *
  2230.  * Results:
  2231.  *    None.
  2232.  *
  2233.  * Side effects:
  2234.  *    Treats client data as int we set to true.
  2235.  *
  2236.  *----------------------------------------------------------------------
  2237.  */
  2238.  
  2239. static pascal void 
  2240. DNRCompletionRoutine(
  2241.     struct hostInfo *hostinfoPtr,     /* Host infor struct. */
  2242.     DNRState *dnrStatePtr)        /* Completetion state. */
  2243. {
  2244.     dnrStatePtr->done = true;
  2245.     WakeUpProcess(&(dnrStatePtr->psn));
  2246. }
  2247.  
  2248. /*
  2249.  *----------------------------------------------------------------------
  2250.  *
  2251.  * CleanUpExitProc --
  2252.  *
  2253.  *    This procedure is invoked as an exit handler when ExitToShell
  2254.  *    is called.  It aborts any lingering socket connections.  This 
  2255.  *    must be called or the Mac OS will more than likely crash.
  2256.  *
  2257.  * Results:
  2258.  *    None.
  2259.  *
  2260.  * Side effects:
  2261.  *    None.
  2262.  *
  2263.  *----------------------------------------------------------------------
  2264.  */
  2265.  
  2266. static pascal void
  2267. CleanUpExitProc()
  2268. {
  2269.     TCPiopb exitPB;
  2270.     TcpState *statePtr;
  2271.  
  2272.     while (socketList != NULL) {
  2273.     statePtr = socketList;
  2274.     socketList = statePtr->nextPtr;
  2275.  
  2276.     /*
  2277.      * Close and Release the connection.
  2278.      */
  2279.  
  2280.     exitPB.ioCRefNum = driverRefNum;
  2281.     exitPB.csCode = TCPClose;
  2282.     exitPB.tcpStream = statePtr->tcpStream;
  2283.     exitPB.csParam.close.ulpTimeoutValue = 60 /* seconds */;
  2284.     exitPB.csParam.close.ulpTimeoutAction = 1 /* 1:abort 0:report */;
  2285.     exitPB.csParam.close.validityFlags = timeoutValue | timeoutAction;
  2286.     exitPB.ioCompletion = NULL; 
  2287.     PBControlSync((ParmBlkPtr) &exitPB);
  2288.  
  2289.     exitPB.ioCRefNum = driverRefNum;
  2290.     exitPB.csCode = TCPRelease;
  2291.     exitPB.tcpStream = statePtr->tcpStream;
  2292.     exitPB.ioCompletion = NULL; 
  2293.     PBControlSync((ParmBlkPtr) &exitPB);
  2294.     }
  2295. }
  2296.  
  2297. /*
  2298.  *----------------------------------------------------------------------
  2299.  *
  2300.  * GetHostFromString --
  2301.  *
  2302.  *    Looks up the passed in domain name in the domain resolver.  It
  2303.  *    can accept strings of two types: 1) the ip number in string
  2304.  *    format, or 2) the domain name.
  2305.  *
  2306.  * Results:
  2307.  *    We return a ip address or 0 if there was an error or the 
  2308.  *    domain does not exist.
  2309.  *
  2310.  * Side effects:
  2311.  *    None.
  2312.  *
  2313.  *----------------------------------------------------------------------
  2314.  */
  2315.  
  2316. static OSErr
  2317. GetHostFromString(
  2318.     char *name,         /* Host in string form. */
  2319.     ip_addr *address)        /* Returned IP address. */
  2320. {
  2321.     OSErr err;
  2322.     int i;
  2323.     EventRecord dummy;
  2324.     DNRState dnrState;
  2325.     
  2326.     if (TclHasSockets(NULL) != TCL_OK) {
  2327.     return 0;
  2328.     }
  2329.  
  2330.     /*
  2331.      * Call StrToAddr to get the ip number for the passed in domain
  2332.      * name.  The call is async, so we must wait for a callback to 
  2333.      * tell us when to continue.
  2334.      */
  2335.  
  2336.     for (i = 0; i < NUM_ALT_ADDRS; i++) {
  2337.     dnrState.hostInfo.addr[i] = 0;
  2338.     }
  2339.     dnrState.done = 0;
  2340.     GetCurrentProcess(&(dnrState.psn));
  2341.     err = StrToAddr(name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
  2342.     if (err == cacheFault) {
  2343.     while (!dnrState.done) {
  2344.         WaitNextEvent(0, &dummy, 1, NULL);
  2345.     }
  2346.     }
  2347.     
  2348.     /*
  2349.      * For some reason MacTcp may return a cachFault a second time via
  2350.      * the hostinfo block.  This seems to be a bug in MacTcp.  In this case 
  2351.      * we run StrToAddr again - which seems to then work just fine.
  2352.      */
  2353.  
  2354.     if (dnrState.hostInfo.rtnCode == cacheFault) {
  2355.     dnrState.done = 0;
  2356.     err = StrToAddr(name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
  2357.     if (err == cacheFault) {
  2358.         while (!dnrState.done) {
  2359.         WaitNextEvent(0, &dummy, 1, NULL);
  2360.         }
  2361.     }
  2362.     }
  2363.  
  2364.     if (dnrState.hostInfo.rtnCode == noErr) {
  2365.     *address = dnrState.hostInfo.addr[0];
  2366.     }
  2367.     
  2368.     return dnrState.hostInfo.rtnCode;
  2369. }
  2370.  
  2371. /*
  2372.  *----------------------------------------------------------------------
  2373.  *
  2374.  * IOCompletionRoutine --
  2375.  *
  2376.  *    This function is called when an asynchronous socket operation
  2377.  *    completes.  Since this routine runs as an interrupt handler, 
  2378.  *    it will simply set state to tell the notifier that this socket
  2379.  *    is now ready for action.  Note that this function is running at
  2380.  *    interupt time and can't allocate memory or do much else except 
  2381.  *      set state.
  2382.  *
  2383.  * Results:
  2384.  *    None.
  2385.  *
  2386.  * Side effects:
  2387.  *    Sets some state in the socket state.  May also wake the process
  2388.  *    if we are not currently running.
  2389.  *
  2390.  *----------------------------------------------------------------------
  2391.  */
  2392.  
  2393. static void
  2394. IOCompletionRoutine(
  2395.     TCPiopb *pbPtr)        /* Tcp parameter block. */
  2396. {
  2397.     TcpState *statePtr;
  2398.     
  2399.     if (pbPtr->csCode == TCPSend) {
  2400.         statePtr = (TcpState *) pbPtr->csParam.send.userDataPtr;
  2401.     } else {
  2402.     statePtr = (TcpState *) pbPtr->csParam.open.userDataPtr;
  2403.     }
  2404.     
  2405.     /*
  2406.      * Always wake the process in case it's in WaitNextEvent.
  2407.      * If an error has a occured - just return.  We will deal
  2408.      * with the problem later.
  2409.      */
  2410.  
  2411.     WakeUpProcess(&statePtr->psn);
  2412.     if (pbPtr->ioResult != noErr) {
  2413.     return;
  2414.     }
  2415.     
  2416.     if (statePtr->flags & TCP_ASYNC_CONNECT) {
  2417.     statePtr->flags &= ~TCP_ASYNC_CONNECT;
  2418.     statePtr->flags |= TCP_CONNECTED;
  2419.     statePtr->checkMask |= TCL_READABLE & TCL_WRITABLE;
  2420.     } else if (statePtr->flags & TCP_LISTENING) {
  2421.     if (statePtr->port == 0) {
  2422.         Debugger();
  2423.     }
  2424.     statePtr->flags &= ~TCP_LISTENING;
  2425.     statePtr->flags |= TCP_LISTEN_CONNECT;
  2426.     statePtr->checkMask |= TCL_READABLE;
  2427.     } else if (statePtr->flags & TCP_WRITING) {
  2428.     statePtr->flags &= ~TCP_WRITING;
  2429.     statePtr->checkMask |= TCL_WRITABLE;
  2430.     if (!(statePtr->flags & TCP_CONNECTED)) {
  2431.         InitMacTCPParamBlock(&statePtr->pb, TCPClose);
  2432.             statePtr->pb.tcpStream = statePtr->tcpStream;
  2433.             statePtr->pb.ioCompletion = closeUPP; 
  2434.             statePtr->pb.csParam.close.userDataPtr = (Ptr) statePtr;
  2435.             if (PBControlAsync((ParmBlkPtr) &statePtr->pb) != noErr) {
  2436.             statePtr->flags |= TCP_RELEASE;
  2437.             }
  2438.     }
  2439.     }
  2440. }
  2441.  
  2442. /*
  2443.  *----------------------------------------------------------------------
  2444.  *
  2445.  * GetLocalAddress --
  2446.  *
  2447.  *    Get the IP address for this machine.  The result is cached so
  2448.  *    the result is returned quickly after the first call.
  2449.  *
  2450.  * Results:
  2451.  *    Macintosh error code.
  2452.  *
  2453.  * Side effects:
  2454.  *    None.
  2455.  *
  2456.  *----------------------------------------------------------------------
  2457.  */
  2458.  
  2459. static OSErr 
  2460. GetLocalAddress(
  2461.     unsigned long *addr)    /* Returns host IP address. */
  2462. {
  2463.     struct GetAddrParamBlock pBlock;
  2464.     OSErr err = noErr;
  2465.     static unsigned long localAddress = 0;
  2466.  
  2467.     if (localAddress == 0) {
  2468.     memset(&pBlock, 0, sizeof(pBlock));
  2469.     pBlock.ioResult = 1;
  2470.     pBlock.csCode = ipctlGetAddr;
  2471.     pBlock.ioCRefNum = driverRefNum;
  2472.     err = PBControlSync((ParmBlkPtr) &pBlock);
  2473.  
  2474.     if (err != noErr) {
  2475.         return err;
  2476.     }
  2477.     localAddress = pBlock.ourAddress;
  2478.     }
  2479.     
  2480.     *addr = localAddress;
  2481.     return noErr;
  2482. }
  2483.  
  2484. /*
  2485.  *----------------------------------------------------------------------
  2486.  *
  2487.  * GetBufferSize --
  2488.  *
  2489.  *    Get the appropiate buffer size for our machine & network.  This
  2490.  *    value will be used by the rest of Tcl & the MacTcp driver for
  2491.  *    the size of its buffers.  If out method for determining the
  2492.  *    optimal buffer size fails for any reason - we return a 
  2493.  *    reasonable default.
  2494.  *
  2495.  * Results:
  2496.  *    Size of optimal buffer in bytes.
  2497.  *
  2498.  * Side effects:
  2499.  *    None.
  2500.  *
  2501.  *----------------------------------------------------------------------
  2502.  */
  2503.  
  2504. static long 
  2505. GetBufferSize()
  2506. {
  2507.     UDPiopb iopb;
  2508.     OSErr err = noErr;
  2509.     long bufferSize;
  2510.     
  2511.     memset(&iopb, 0, sizeof(iopb));
  2512.     err = GetLocalAddress(&iopb.csParam.mtu.remoteHost);
  2513.     if (err != noErr) {
  2514.     return CHANNEL_BUF_SIZE;
  2515.     }
  2516.     iopb.ioCRefNum = driverRefNum;
  2517.     iopb.csCode = UDPMaxMTUSize;
  2518.     err = PBControlSync((ParmBlkPtr)&iopb);
  2519.     if (err != noErr) {
  2520.     return CHANNEL_BUF_SIZE;
  2521.     }
  2522.     bufferSize = (iopb.csParam.mtu.mtuSize * 4) + 1024;
  2523.     if (bufferSize < CHANNEL_BUF_SIZE) {
  2524.     bufferSize = CHANNEL_BUF_SIZE;
  2525.     }
  2526.     return bufferSize;
  2527. }
  2528.  
  2529. /*
  2530.  *----------------------------------------------------------------------
  2531.  *
  2532.  * TclSockGetPort --
  2533.  *
  2534.  *    Maps from a string, which could be a service name, to a port.
  2535.  *    Used by socket creation code to get port numbers and resolve
  2536.  *    registered service names to port numbers.
  2537.  *
  2538.  * Results:
  2539.  *    A standard Tcl result.  On success, the port number is
  2540.  *    returned in portPtr. On failure, an error message is left in
  2541.  *    interp->result.
  2542.  *
  2543.  * Side effects:
  2544.  *    None.
  2545.  *
  2546.  *----------------------------------------------------------------------
  2547.  */
  2548.  
  2549. int
  2550. TclSockGetPort(
  2551.     Tcl_Interp *interp,     /* Interp for error messages. */
  2552.     char *string,         /* Integer or service name */
  2553.     char *proto,         /* "tcp" or "udp", typically - 
  2554.                      * ignored on Mac - assumed to be tcp */
  2555.     int *portPtr)        /* Return port number */
  2556. {
  2557.     PortInfo *portInfoPtr = NULL;
  2558.     
  2559.     if (Tcl_GetInt(interp, string, portPtr) == TCL_OK) {
  2560.     if (*portPtr > 0xFFFF) {
  2561.         Tcl_AppendResult(interp, "couldn't open socket: port number too high",
  2562.                 (char *) NULL);
  2563.         return TCL_ERROR;
  2564.     }
  2565.     if (*portPtr < 0) {
  2566.         Tcl_AppendResult(interp, "couldn't open socket: negative port number",
  2567.                 (char *) NULL);
  2568.         return TCL_ERROR;
  2569.     }
  2570.     return TCL_OK;
  2571.     }
  2572.     for (portInfoPtr = portServices; portInfoPtr->name != NULL; portInfoPtr++) {
  2573.     if (!strcmp(portInfoPtr->name, string)) {
  2574.         break;
  2575.     }
  2576.     }
  2577.     if (portInfoPtr != NULL && portInfoPtr->name != NULL) {
  2578.     *portPtr = portInfoPtr->port;
  2579.     Tcl_ResetResult(interp);
  2580.     return TCL_OK;
  2581.     }
  2582.     
  2583.     return TCL_ERROR;
  2584. }
  2585.  
  2586. /*
  2587.  *----------------------------------------------------------------------
  2588.  *
  2589.  * ClearZombieSockets --
  2590.  *
  2591.  *    This procedure looks through the socket list and removes the
  2592.  *    first stream it finds that is ready for release. This procedure 
  2593.  *    should be called before we ever try to create new Tcp streams
  2594.  *    to ensure we can least allocate one stream.
  2595.  *
  2596.  * Results:
  2597.  *    None.
  2598.  *
  2599.  * Side effects:
  2600.  *    Tcp streams may be released.
  2601.  *
  2602.  *----------------------------------------------------------------------
  2603.  */
  2604.  
  2605. static void
  2606. ClearZombieSockets()
  2607. {
  2608.     TcpState *statePtr;
  2609.  
  2610.     for (statePtr = socketList; statePtr != NULL;
  2611.         statePtr = statePtr->nextPtr) {
  2612.     if (statePtr->flags & TCP_RELEASE) {
  2613.         SocketFreeProc(statePtr);
  2614.         return;
  2615.     }
  2616.     }
  2617. }
  2618.