tclMacSock.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:74k
源码类别:

通讯编程

开发平台:

Visual C++

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