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