os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/mac/tclMacSock.c
Update contrib.
4 * Channel drivers for Macintosh sockets.
6 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 * RCS: @(#) $Id: tclMacSock.c,v 1.14.2.1 2006/03/10 14:27:41 vasiljevic Exp $
16 #include "tclMacInt.h"
17 #include <AddressXlation.h>
26 #include <Processes.h>
30 * The following variable is used to tell whether this module has been
34 static int initialized = 0;
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.
47 * The preferred buffer size for Macintosh channels.
50 #define CHANNEL_BUF_SIZE 8192
53 * Port information structure. Used to match service names
54 * to a Tcp/Ip port number.
58 char *name; /* Name of service. */
59 int port; /* Port number. */
63 * This structure describes per-instance state of a tcp based channel.
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
74 int checkMask; /* OR'ed combination of TCL_READABLE and
75 * TCL_WRITABLE as set by an asynchronous
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
93 * This structure is used by domain name resolver callback.
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. */
103 * The following macros may be used to set the flags field of
104 * a TcpState structure.
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
113 #define TCP_LISTEN_CONNECT (1<<5) /* Someone has connect to the
115 #define TCP_REMOTE_CLOSED (1<<6) /* The remote side has closed
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. */
122 * The following structure is what is added to the Tcl event queue when
123 * a socket event occurs.
126 typedef struct SocketEvent {
127 Tcl_Event header; /* Information that is standard for
129 TcpState *statePtr; /* Socket descriptor that is ready. */
130 StreamPtr tcpStream; /* Low level Macintosh stream. */
134 * Static routines for this file:
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,
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,
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,
160 static int SocketEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
162 static void SocketFreeProc _ANSI_ARGS_((ClientData clientData));
163 static int SocketReady _ANSI_ARGS_((TcpState *statePtr));
164 static void SocketSetupProc _ANSI_ARGS_((ClientData clientData,
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,
181 static int WaitForSocketEvent _ANSI_ARGS_((TcpState *infoPtr,
182 int mask, int *errorCodePtr));
184 pascal void NotifyRoutine (
186 unsigned short eventCode,
188 unsigned short terminReason,
189 struct ICMPReport *icmpMsg);
192 * This structure describes the channel type structure for TCP socket
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. */
211 * Universal Procedure Pointers (UPP) for various callback
212 * routines used by MacTcp code.
215 ResultUPP resultUPP = NULL;
216 TCPIOCompletionUPP completeUPP = NULL;
217 TCPIOCompletionUPP closeUPP = NULL;
218 TCPNotifyUPP notifyUPP = NULL;
221 * Built-in commands, and the procedures associated with them:
224 static PortInfo portServices[] = {
253 typedef struct ThreadSpecificData {
255 * Every open socket has an entry on the following list.
258 TcpState *socketList;
259 } ThreadSpecificData;
261 static Tcl_ThreadDataKey dataKey;
264 * Globals for holding information about OS support for sockets.
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;
275 *----------------------------------------------------------------------
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.
285 * 1 if successful, 0 on failure.
288 * Creates a new event source, loads the MacTCP driver,
289 * registers an exit to shell callback.
291 *----------------------------------------------------------------------
294 #define gestaltMacTCPVersion 'mtcp'
301 ThreadSpecificData *tsdPtr;
305 * Do process wide initialization.
310 if (Gestalt(gestaltMacTCPVersion, &response) == noErr) {
321 * Load MacTcp driver and name server resolver.
325 pb.ioParam.ioCompletion = 0L;
326 pb.ioParam.ioNamePtr = "\p.IPP";
327 pb.ioParam.ioPermssn = fsCurPerm;
328 err = PBOpenSync(&pb);
333 driverRefNum = pb.ioParam.ioRefNum;
335 socketBufferSize = GetBufferSize();
336 err = OpenResolver(NULL);
342 GetCurrentProcess(&applicationPSN);
344 * Create UPP's for various callback routines.
347 resultUPP = NewResultProc(DNRCompletionRoutine);
348 completeUPP = NewTCPIOCompletionProc(IOCompletionRoutine);
349 closeUPP = NewTCPIOCompletionProc(CloseCompletionRoutine);
350 notifyUPP = NewTCPNotifyProc(NotifyRoutine);
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.
360 TclMacInstallExitToShellPatch(CleanUpExitProc);
364 * Do per-thread initialization.
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);
376 *----------------------------------------------------------------------
378 * TclpFinalizeSockets --
380 * Invoked during exit clean up to deinitialize the socket module.
386 * Removed event source.
388 *----------------------------------------------------------------------
392 TclpFinalizeSockets()
394 ThreadSpecificData *tsdPtr;
396 tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
397 if (tsdPtr != NULL) {
398 Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL);
403 *----------------------------------------------------------------------
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.
412 * Returns TCL_OK if the system supports sockets, or TCL_ERROR with
413 * an error in interp.
418 *----------------------------------------------------------------------
423 Tcl_Interp *interp) /* Interp for error messages. */
430 if (interp != NULL) {
431 Tcl_AppendResult(interp, "sockets are not available on this system",
438 *----------------------------------------------------------------------
442 * This procedure is invoked before Tcl_DoOneEvent blocks waiting
449 * Adjusts the block time if needed.
451 *----------------------------------------------------------------------
456 ClientData data, /* Not used. */
457 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
460 Tcl_Time blockTime = { 0, 0 };
461 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
463 if (!(flags & TCL_FILE_EVENTS)) {
468 * Check to see if there is a ready socket. If so, poll.
471 for (statePtr = tsdPtr->socketList; statePtr != NULL;
472 statePtr = statePtr->nextPtr) {
473 if (statePtr->flags & TCP_RELEASE) {
476 if (SocketReady(statePtr)) {
477 Tcl_SetMaxBlockTime(&blockTime);
484 *----------------------------------------------------------------------
488 * This procedure is called by Tcl_DoOneEvent to check the socket
489 * event source for events.
495 * May queue an event.
497 *----------------------------------------------------------------------
502 ClientData data, /* Not used. */
503 int flags) /* Event flags as passed to Tcl_DoOneEvent. */
508 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
510 if (!(flags & TCL_FILE_EVENTS)) {
515 * Queue events for any ready sockets that don't already have events
516 * queued (caused by persistent states that won't generate WinSock
520 for (statePtr = tsdPtr->socketList; statePtr != NULL;
521 statePtr = statePtr->nextPtr) {
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
529 if (statePtr->flags & TCP_RELEASE) {
530 if (!(statePtr->flags & TCP_PENDING)) {
531 dummyState.nextPtr = statePtr->nextPtr;
532 SocketFreeProc(statePtr);
533 statePtr = &dummyState;
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);
550 *----------------------------------------------------------------------
554 * This function checks the current state of a socket to see
555 * if any interesting conditions are present.
558 * Returns 1 if an event that someone is watching is present, else
562 * Updates the checkMask for the socket to reflect any newly
565 *----------------------------------------------------------------------
573 int foundSomething = 0;
578 if (statePtr->flags & TCP_LISTEN_CONNECT) {
580 statePtr->checkMask |= TCL_READABLE;
582 if (statePtr->watchMask & TCL_READABLE) {
583 if (statePtr->checkMask & TCL_READABLE) {
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);
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.
598 if ((err != noErr) ||
599 (statusPB.csParam.status.amtUnreadData > 0) ||
600 (statusPB.csParam.status.connectionState == 14)) {
601 statePtr->checkMask |= TCL_READABLE;
606 if (statePtr->watchMask & TCL_WRITABLE) {
607 if (statePtr->checkMask & TCL_WRITABLE) {
609 } else if (statePtr->flags & TCP_CONNECTED) {
611 statusPB.ioCRefNum = driverRefNum;
612 statusPB.tcpStream = statePtr->tcpStream;
613 statusPB.csCode = TCPStatus;
614 err = PBControlSync((ParmBlkPtr) &statusPB);
618 * If there is an error or there if there is room to
619 * send more data we make the channel writeable.
622 amount = statusPB.csParam.status.sendWindow -
623 statusPB.csParam.status.amtUnackedData;
624 if ((err != noErr) || (amount > 0)) {
625 statePtr->checkMask |= TCL_WRITABLE;
630 return foundSomething;
634 *----------------------------------------------------------------------
636 * InitMacTCPParamBlock--
638 * Initialize a MacTCP parameter block.
644 * Initializes the parameter block.
646 *----------------------------------------------------------------------
650 InitMacTCPParamBlock(
651 TCPiopb *pBlock, /* Tcp parmeter block. */
652 int csCode) /* Tcp operation code. */
654 memset(pBlock, 0, sizeof(TCPiopb));
655 pBlock->ioResult = 1;
656 pBlock->ioCRefNum = driverRefNum;
657 pBlock->csCode = (short) csCode;
661 *----------------------------------------------------------------------
665 * Set blocking or non-blocking mode on channel.
668 * 0 if successful, errno when failed.
671 * Sets the device into blocking or non-blocking mode.
673 *----------------------------------------------------------------------
678 ClientData instanceData, /* Channel state. */
679 int mode) /* The mode to set. */
681 TcpState *statePtr = (TcpState *) instanceData;
683 if (mode == TCL_MODE_BLOCKING) {
684 statePtr->flags &= ~TCP_ASYNC_SOCKET;
686 statePtr->flags |= TCP_ASYNC_SOCKET;
692 *----------------------------------------------------------------------
699 * 0 if successful, the value of errno if failed.
704 *----------------------------------------------------------------------
709 ClientData instanceData, /* The socket to close. */
710 Tcl_Interp *interp) /* Interp for error messages. */
712 TcpState *statePtr = (TcpState *) instanceData;
717 tcpStream = statePtr->tcpStream;
718 statePtr->flags &= ~TCP_CONNECTED;
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.
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);
738 /* panic("error closing server socket"); */
740 statePtr->flags |= TCP_RELEASE;
743 * Server sockets are closed sync. Therefor, we know it is OK to
744 * release the socket now.
747 InitMacTCPParamBlock(&statePtr->pb, TCPRelease);
748 statePtr->pb.tcpStream = statePtr->tcpStream;
749 err = PBControlSync((ParmBlkPtr) &statePtr->pb);
751 panic("error releasing server socket");
755 * Free the buffer space used by the socket and the
756 * actual socket state data structure.
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
767 ckfree((char *) statePtr->pb.csParam.create.rcvBuff);
769 FreeSocketInfo(statePtr);
774 * If this socket is in the midddle on async connect we can just
775 * abort the connect and release the stream right now.
778 if (statePtr->flags & TCP_ASYNC_CONNECT) {
779 InitMacTCPParamBlock(&closePB, TCPClose);
780 closePB.tcpStream = tcpStream;
781 closePB.ioCompletion = NULL;
782 err = PBControlSync((ParmBlkPtr) &closePB);
784 statePtr->flags |= TCP_RELEASE;
786 InitMacTCPParamBlock(&closePB, TCPRelease);
787 closePB.tcpStream = tcpStream;
788 closePB.ioCompletion = NULL;
790 err = PBControlSync((ParmBlkPtr) &closePB);
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
804 ckfree((char *) closePB.csParam.create.rcvBuff);
807 FreeSocketInfo(statePtr);
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.
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);
826 statePtr->flags |= TCP_RELEASE;
831 SocketFreeProc(instanceData);
836 *----------------------------------------------------------------------
838 * CloseCompletionRoutine --
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.
851 * The buffers for the socket are flushed.
853 *----------------------------------------------------------------------
857 CloseCompletionRoutine(
858 TCPiopb *pbPtr) /* Tcp parameter block. */
863 if (pbPtr->csCode == TCPClose) {
864 statePtr = (TcpState *) (pbPtr->csParam.close.userDataPtr);
866 statePtr = (TcpState *) (pbPtr->csParam.receive.userDataPtr);
870 * It's very bad if the statePtr is nNULL - we should probably panic...
873 if (statePtr == NULL) {
878 WakeUpProcess(&statePtr->psn);
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.
886 if (pbPtr->ioResult != noErr) {
887 statePtr->flags |= TCP_RELEASE;
890 if (statePtr->flags & TCP_REMOTE_CLOSED) {
891 statePtr->flags |= TCP_RELEASE;
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).
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);
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);
920 statePtr->flags |= TCP_RELEASE;
924 *----------------------------------------------------------------------
928 * This callback is invoked in order to delete
929 * the notifier data associated with a file handle.
935 * Removes the SocketInfo from the global socket list.
937 *----------------------------------------------------------------------
942 ClientData clientData) /* Channel state. */
944 TcpState *statePtr = (TcpState *) clientData;
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.
953 if (!(statePtr->flags & TCP_RELEASE)) {
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)) {
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
975 * The Close request is made async. We know it's
976 * OK to release the socket when the TCP_RELEASE flag
980 InitMacTCPParamBlock(&statePtr->pb, TCPRelease);
981 statePtr->pb.tcpStream = statePtr->tcpStream;
982 err = PBControlSync((ParmBlkPtr) &statePtr->pb);
984 Debugger(); /* Ignoreing leaves stranded stream. Is there an
989 * Free the buffer space used by the socket and the
990 * actual socket state data structure.
993 ckfree((char *) statePtr->pb.csParam.create.rcvBuff);
994 FreeSocketInfo(statePtr);
998 *----------------------------------------------------------------------
1002 * Reads input from the IO channel into the buffer given. Returns
1003 * count of how many bytes were actually read, and an error
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).
1012 * Reads input from the actual channel.
1014 *----------------------------------------------------------------------
1019 ClientData instanceData, /* Channel state. */
1020 char *buf, /* Where to store data read. */
1021 int bufSize, /* How much space is available
1023 int *errorCodePtr) /* Where to store error code. */
1025 TcpState *statePtr = (TcpState *) instanceData;
1026 StreamPtr tcpStream;
1029 int toRead, dataAvail;
1033 tcpStream = statePtr->tcpStream;
1041 * First check to see if EOF was already detected, to prevent
1042 * calling the socket stack after the first time EOF is detected.
1045 if (statePtr->flags & TCP_REMOTE_CLOSED) {
1050 * If an asynchronous connect is in progress, attempt to wait for it
1051 * to complete before reading.
1054 if ((statePtr->flags & TCP_ASYNC_CONNECT)
1055 && ! WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) {
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.
1067 statusPB.ioCRefNum = driverRefNum;
1068 statusPB.tcpStream = tcpStream;
1069 statusPB.csCode = TCPStatus;
1070 err = PBControlSync((ParmBlkPtr) &statusPB);
1073 statePtr->flags |= TCP_REMOTE_CLOSED;
1076 dataAvail = statusPB.csParam.status.amtUnreadData;
1077 if (dataAvail < bufSize) {
1084 * Try to read the data.
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);
1093 statePtr->checkMask &= ~TCL_READABLE;
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
1103 if (dataAvail > bufSize) {
1104 statePtr->checkMask |= TCL_READABLE;
1106 return statusPB.csParam.receive.rcvBuffLen;
1107 case connectionClosing:
1108 *errorCodePtr = errno = ESHUTDOWN;
1109 statePtr->flags |= TCP_REMOTE_CLOSED;
1111 case connectionDoesntExist:
1112 case connectionTerminated:
1113 *errorCodePtr = errno = ENOTCONN;
1114 statePtr->flags |= TCP_REMOTE_CLOSED;
1116 case invalidStreamPtr:
1118 *errorCodePtr = EINVAL;
1124 * No data is available, so check the connection state to
1125 * see why this is the case.
1128 if (statusPB.csParam.status.connectionState == 14) {
1129 statePtr->flags |= TCP_REMOTE_CLOSED;
1132 if (statusPB.csParam.status.connectionState != 8) {
1135 statePtr->checkMask &= ~TCL_READABLE;
1136 if (statePtr->flags & TCP_ASYNC_SOCKET) {
1137 *errorCodePtr = EWOULDBLOCK;
1142 * In the blocking case, wait until the file becomes readable
1143 * or closed and try again.
1146 if (!WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) {
1153 *----------------------------------------------------------------------
1157 * Called from Tcl_GetChannelHandle to retrieve handles from inside
1158 * a file based channel.
1161 * The appropriate handle or NULL if not present.
1166 *----------------------------------------------------------------------
1171 ClientData instanceData, /* The file state. */
1172 int direction, /* Which handle to retrieve? */
1173 ClientData *handlePtr)
1175 TcpState *statePtr = (TcpState *) instanceData;
1177 *handlePtr = (ClientData) statePtr->tcpStream;
1182 *----------------------------------------------------------------------
1186 * Writes the given output on the IO channel. Returns count of how
1187 * many characters were actually written, and an error indication.
1190 * A count of how many characters were written is returned and an
1191 * error indication is returned in an output argument.
1194 * Writes output on the actual channel.
1196 *----------------------------------------------------------------------
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. */
1206 TcpState *statePtr = (TcpState *) instanceData;
1207 StreamPtr tcpStream;
1213 tcpStream = statePtr->tcpStream;
1216 * If an asynchronous connect is in progress, attempt to wait for it
1217 * to complete before writing.
1220 if ((statePtr->flags & TCP_ASYNC_CONNECT)
1221 && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) {
1226 * Loop until we have written some data, or an error occurs.
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))) {
1237 * The remote connection is gone away. Report an error
1238 * and don't write anything.
1241 *errorCodePtr = errno = EPIPE;
1243 } else if (err != noErr) {
1246 amount = statusPB.csParam.status.sendWindow
1247 - statusPB.csParam.status.amtUnackedData;
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.
1254 if (!(statePtr->flags & TCP_WRITING) && amount > 0) {
1255 if (toWrite < amount) {
1259 /* We need to copy the data, otherwise the caller may overwrite
1260 * the buffer in the middle of our asynchronous call
1263 if (amount > statePtr->writeBufferSize) {
1265 * need to grow write buffer
1268 if (statePtr->writeBuffer != (void *) NULL) {
1269 ckfree(statePtr->writeBuffer);
1271 statePtr->writeBuffer = (void *) ckalloc(amount);
1272 statePtr->writeBufferSize = amount;
1274 memcpy(statePtr->writeBuffer, buf, amount);
1275 statePtr->dataSegment[0].ptr = statePtr->writeBuffer;
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));
1290 case connectionClosing:
1291 *errorCodePtr = errno = ESHUTDOWN;
1292 statePtr->flags |= TCP_REMOTE_CLOSED;
1294 case connectionDoesntExist:
1295 case connectionTerminated:
1296 *errorCodePtr = errno = ENOTCONN;
1297 statePtr->flags |= TCP_REMOTE_CLOSED;
1299 case invalidStreamPtr:
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.
1312 if (statePtr->flags & TCP_ASYNC_SOCKET) {
1313 statePtr->checkMask &= ~TCL_WRITABLE;
1314 *errorCodePtr = EWOULDBLOCK;
1316 } else if (!WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) {
1323 *----------------------------------------------------------------------
1325 * TcpGetOptionProc --
1327 * Computes an option value for a TCP socket based channel, or a
1328 * list of all options and their values.
1330 * Note: This code is based on code contributed by John Haxby.
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
1340 *----------------------------------------------------------------------
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
1351 Tcl_DString *dsPtr) /* Where to store the computed
1352 * value; initialized by caller. */
1354 TcpState *statePtr = (TcpState *) instanceData;
1355 int doPeerName = false, doSockName = false, doError = false, doAll = false;
1359 Tcl_DString dString;
1365 * If an asynchronous connect is in progress, attempt to wait for it
1366 * to complete before accessing the socket state.
1369 if ((statePtr->flags & TCP_ASYNC_CONNECT)
1370 && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, &errorCode)) {
1373 * fix the error message.
1376 Tcl_AppendResult(interp, "connect is in progress and can't wait",
1383 * Determine which options we need to do. Do all of them
1384 * if optionName is NULL.
1387 if (optionName == (CONST char *) NULL || optionName[0] == '\0') {
1390 len = strlen(optionName);
1391 if (!strncmp(optionName, "-peername", len)) {
1393 } else if (!strncmp(optionName, "-sockname", len)) {
1395 } else if (!strncmp(optionName, "-error", len)) {
1396 /* SF Bug #483575 */
1399 return Tcl_BadChannelOption(interp, optionName,
1400 "error peername sockname");
1407 * Return error information. Currently we ignore
1408 * this option. IOW, we always return the empty
1409 * string, signaling 'no error'.
1411 * FIXME: Get a mac/socket expert to write a correct
1412 * FIXME: implementation.
1415 if (doAll || doError) {
1417 Tcl_DStringAppendElement(dsPtr, "-error");
1418 Tcl_DStringAppendElement(dsPtr, "");
1420 Tcl_DStringAppend (dsPtr, "", -1);
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.
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))) {
1437 * The socket was probably closed on the other side of the connection.
1441 Tcl_AppendResult(interp, "can't access socket info: ",
1442 "connection reset by peer", NULL);
1445 } else if (err != noErr) {
1447 Tcl_AppendResult(interp, "unknown socket error", NULL);
1455 * Get the sockname for the socket.
1458 Tcl_DStringInit(&dString);
1459 if (doAll || doSockName) {
1461 Tcl_DStringAppendElement(dsPtr, "-sockname");
1462 Tcl_DStringStartSublist(dsPtr);
1464 tcpAddress = statusPB.csParam.status.localHost;
1465 sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24,
1466 tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff,
1468 Tcl_DStringAppendElement(dsPtr, buffer);
1469 if (ResolveAddress(tcpAddress, &dString) == noErr) {
1470 Tcl_DStringAppendElement(dsPtr, dString.string);
1472 Tcl_DStringAppendElement(dsPtr, "<unknown>");
1474 sprintf(buffer, "%d", statusPB.csParam.status.localPort);
1475 Tcl_DStringAppendElement(dsPtr, buffer);
1477 Tcl_DStringEndSublist(dsPtr);
1482 * Get the peername for the socket.
1485 if ((doAll || doPeerName) && (statePtr->flags & TCP_CONNECTED)) {
1487 Tcl_DStringAppendElement(dsPtr, "-peername");
1488 Tcl_DStringStartSublist(dsPtr);
1490 tcpAddress = statusPB.csParam.status.remoteHost;
1491 sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24,
1492 tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff,
1494 Tcl_DStringAppendElement(dsPtr, buffer);
1495 Tcl_DStringSetLength(&dString, 0);
1496 if (ResolveAddress(tcpAddress, &dString) == noErr) {
1497 Tcl_DStringAppendElement(dsPtr, dString.string);
1499 Tcl_DStringAppendElement(dsPtr, "<unknown>");
1501 sprintf(buffer, "%d", statusPB.csParam.status.remotePort);
1502 Tcl_DStringAppendElement(dsPtr, buffer);
1504 Tcl_DStringEndSublist(dsPtr);
1508 Tcl_DStringFree(&dString);
1513 *----------------------------------------------------------------------
1517 * Initialize the notifier to watch this channel.
1523 * Sets the watchMask for the channel.
1525 *----------------------------------------------------------------------
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. */
1535 TcpState *statePtr = (TcpState *) instanceData;
1537 statePtr->watchMask = mask;
1541 *----------------------------------------------------------------------
1545 * This function allocates and initializes a new SocketInfo
1549 * Returns a newly allocated SocketInfo.
1552 * Adds the socket to the global socket list, allocates memory.
1554 *----------------------------------------------------------------------
1559 StreamPtr tcpStream)
1562 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
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;
1580 *----------------------------------------------------------------------
1584 * This function deallocates a SocketInfo structure that is no
1591 * Removes the socket from the global socket list, frees memory.
1593 *----------------------------------------------------------------------
1598 TcpState *statePtr) /* The state pointer to free. */
1600 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1602 if (statePtr == tsdPtr->socketList) {
1603 tsdPtr->socketList = statePtr->nextPtr;
1606 for (p = tsdPtr->socketList; p != NULL; p = p->nextPtr) {
1607 if (p->nextPtr == statePtr) {
1608 p->nextPtr = statePtr->nextPtr;
1614 if (statePtr->writeBuffer != (void *) NULL) {
1615 ckfree(statePtr->writeBuffer);
1618 ckfree((char *) statePtr);
1622 *----------------------------------------------------------------------
1624 * Tcl_MakeTcpClientChannel --
1626 * Creates a Tcl_Channel from an existing client TCP socket.
1629 * The Tcl_Channel wrapped around the preexisting TCP socket.
1634 *----------------------------------------------------------------------
1638 Tcl_MakeTcpClientChannel(
1639 ClientData sock) /* The socket to wrap up into a channel. */
1642 char channelName[20];
1644 if (TclpHasSockets(NULL) != TCL_OK) {
1648 statePtr = NewSocketInfo((StreamPtr) sock);
1649 /* TODO: do we need to set the port??? */
1651 sprintf(channelName, "sock%d", socketNumber++);
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;
1661 *----------------------------------------------------------------------
1665 * This function opens a new socket and initializes the
1666 * SocketInfo structure.
1669 * Returns a new SocketInfo, or NULL with an error in interp.
1672 * Adds a new socket to the socketList.
1674 *----------------------------------------------------------------------
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. */
1691 StreamPtr tcpStream;
1696 * Figure out the ip address from the host string.
1700 err = GetLocalAddress(&macAddr);
1702 err = GetHostFromString(host, &macAddr);
1705 Tcl_SetErrno(EHOSTUNREACH);
1706 if (interp != (Tcl_Interp *) NULL) {
1707 Tcl_AppendResult(interp, "couldn't open socket: ",
1708 Tcl_PosixError(interp), (char *) NULL);
1710 return (TcpState *) NULL;
1714 * Create a MacTCP stream and create the state used for socket
1715 * transactions from here on out.
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);
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);
1731 return (TcpState *) NULL;
1734 tcpStream = pb.tcpStream;
1735 statePtr = NewSocketInfo(tcpStream);
1736 statePtr->port = port;
1740 * Set up server connection.
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 */;
1752 statePtr->flags |= TCP_LISTENING;
1753 err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
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.
1760 if (statePtr->port == 0) {
1763 while (statePtr->pb.csParam.open.localPort == 0) {
1764 WaitNextEvent(0, &dummy, 1, NULL);
1765 if (statePtr->pb.ioResult != 0) {
1769 statePtr->port = statePtr->pb.csParam.open.localPort;
1771 Tcl_SetErrno(EINPROGRESS);
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.
1780 InitMacTCPParamBlock(&statePtr->pb, TCPActiveOpen);
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;
1793 statePtr->pb.ioCompletion = completeUPP;
1795 statePtr->flags |= TCP_ASYNC_CONNECT;
1796 err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
1797 Tcl_SetErrno(EINPROGRESS);
1799 err = PBControlSync((ParmBlkPtr) &(statePtr->pb));
1806 statePtr->flags |= TCP_CONNECTED;
1809 case duplicateSocket:
1810 Tcl_SetErrno(EADDRINUSE);
1813 case connectionTerminated:
1814 Tcl_SetErrno(ECONNREFUSED);
1816 case invalidStreamPtr:
1817 case connectionExists:
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.
1830 * We had error during the connection. Release the stream
1831 * and file handle. Also report to the interp.
1834 pb.ioCRefNum = driverRefNum;
1835 pb.csCode = TCPRelease;
1836 pb.tcpStream = tcpStream;
1837 pb.ioCompletion = NULL;
1838 err = PBControlSync((ParmBlkPtr) &pb);
1840 if (interp != (Tcl_Interp *) NULL) {
1841 Tcl_AppendResult(interp, "couldn't open socket: ",
1842 Tcl_PosixError(interp), (char *) NULL);
1846 FreeSocketInfo(statePtr);
1847 return (TcpState *) NULL;
1851 *----------------------------------------------------------------------
1853 * Tcl_OpenTcpClient --
1855 * Opens a TCP client socket and creates a channel around it.
1858 * The channel or NULL if failed. On failure, the routine also
1859 * sets the output argument errorCodePtr to the error code.
1862 * Opens a client socket and creates a new channel.
1864 *----------------------------------------------------------------------
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 */
1880 char channelName[20];
1882 if (TclpHasSockets(interp) != TCL_OK) {
1887 * Create a new client socket and wrap it in a channel.
1890 statePtr = CreateSocket(interp, port, host, myaddr, myport, 0, async);
1891 if (statePtr == NULL) {
1895 sprintf(channelName, "sock%d", socketNumber++);
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;
1905 *----------------------------------------------------------------------
1907 * Tcl_OpenTcpServer --
1909 * Opens a TCP server socket and creates a channel around it.
1912 * The channel or NULL if failed.
1915 * Opens a server socket and creates a new channel.
1917 *----------------------------------------------------------------------
1922 Tcl_Interp *interp, /* For error reporting - may be
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. */
1931 char channelName[20];
1933 if (TclpHasSockets(interp) != TCL_OK) {
1938 * Create a new client socket and wrap it in a channel.
1941 statePtr = CreateSocket(interp, port, host, NULL, 0, 1, 1);
1942 if (statePtr == NULL) {
1946 statePtr->acceptProc = acceptProc;
1947 statePtr->acceptProcData = acceptProcData;
1949 sprintf(channelName, "sock%d", socketNumber++);
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;
1959 *----------------------------------------------------------------------
1961 * SocketEventProc --
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.
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.
1974 * Whatever the channel callback procedures do.
1976 *----------------------------------------------------------------------
1981 Tcl_Event *evPtr, /* Event to service. */
1982 int flags) /* Flags that indicate what events to
1983 * handle, such as TCL_FILE_EVENTS. */
1986 SocketEvent *eventPtr = (SocketEvent *) evPtr;
1988 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
1990 if (!(flags & TCL_FILE_EVENTS)) {
1995 * Find the specified socket on the socket list.
1998 for (statePtr = tsdPtr->socketList; statePtr != NULL;
1999 statePtr = statePtr->nextPtr) {
2000 if ((statePtr == eventPtr->statePtr) &&
2001 (statePtr->tcpStream == eventPtr->tcpStream)) {
2007 * Discard events that have gone stale.
2013 statePtr->flags &= ~(TCP_PENDING);
2014 if (statePtr->flags & TCP_RELEASE) {
2015 SocketFreeProc(statePtr);
2021 * Handle connection requests directly.
2024 if (statePtr->flags & TCP_LISTEN_CONNECT) {
2025 if (statePtr->checkMask & TCL_READABLE) {
2026 TcpAccept(statePtr);
2032 * Mask off unwanted events then notify the channel.
2035 mask = statePtr->checkMask & statePtr->watchMask;
2037 Tcl_NotifyChannel(statePtr->channel, mask);
2043 *----------------------------------------------------------------------
2045 * WaitForSocketEvent --
2047 * Waits until one of the specified events occurs on a socket.
2050 * Returns 1 on success or 0 on failure, with an error code in
2054 * Processes socket events off the system queue.
2056 *----------------------------------------------------------------------
2061 TcpState *statePtr, /* Information about this socket. */
2062 int mask, /* Events to look for. */
2063 int *errorCodePtr) /* Where to store errors? */
2070 * Loop until we get the specified condition, unless the socket is
2075 statusPB.ioCRefNum = driverRefNum;
2076 statusPB.tcpStream = statePtr->tcpStream;
2077 statusPB.csCode = TCPStatus;
2078 err = PBControlSync((ParmBlkPtr) &statusPB);
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.
2087 if (statePtr->flags & TCP_ASYNC_CONNECT) {
2088 *errorCodePtr = EWOULDBLOCK;
2091 statePtr->checkMask |= (TCL_READABLE | TCL_WRITABLE);
2095 statePtr->checkMask = 0;
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
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;
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;
2115 if (mask & statePtr->checkMask) {
2123 * Call the system to let other applications run while we
2124 * are waiting for this event to occur.
2127 WaitNextEvent(0, &dummy, 1, NULL);
2128 } while (!(statePtr->flags & TCP_ASYNC_SOCKET));
2129 *errorCodePtr = EWOULDBLOCK;
2134 *----------------------------------------------------------------------
2137 * Accept a TCP socket connection. This is called by the event
2138 * loop, and it in turns calls any registered callbacks for this
2145 * Evals the Tcl script associated with the server socket.
2147 *----------------------------------------------------------------------
2154 TcpState *newStatePtr;
2155 StreamPtr tcpStream;
2156 char remoteHostname[255];
2158 ip_addr remoteAddress;
2160 char channelName[20];
2162 statePtr->flags &= ~TCP_LISTEN_CONNECT;
2163 statePtr->checkMask &= ~TCL_READABLE;
2166 * Transfer sever stream to new connection.
2169 tcpStream = statePtr->tcpStream;
2170 newStatePtr = NewSocketInfo(tcpStream);
2171 newStatePtr->tcpStream = tcpStream;
2172 sprintf(channelName, "sock%d", socketNumber++);
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",
2182 remoteAddress = statePtr->pb.csParam.open.remoteHost;
2183 remotePort = statePtr->pb.csParam.open.remotePort;
2186 * Reopen passive connect. Make new tcpStream the server.
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);
2196 * Hmmm... We can't reopen the server. We'll go ahead
2197 * an continue - but we are kind of broken now...
2200 statePtr->tcpStream = -1;
2201 statePtr->flags |= TCP_SERVER_ZOMBIE;
2204 tcpStream = statePtr->tcpStream = statePtr->pb.tcpStream;
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));
2215 * TODO: deal with case where we can't recreate server socket...
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.
2224 if (statePtr->acceptProc != NULL) {
2225 sprintf(remoteHostname, "%d.%d.%d.%d", remoteAddress>>24,
2226 remoteAddress>>16 & 0xff, remoteAddress>>8 & 0xff,
2227 remoteAddress & 0xff);
2229 (statePtr->acceptProc)(statePtr->acceptProcData, newStatePtr->channel,
2230 remoteHostname, remotePort);
2235 *----------------------------------------------------------------------
2237 * Tcl_GetHostName --
2239 * Returns the name of the local host.
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.
2249 *----------------------------------------------------------------------
2255 static int hostnameInited = 0;
2256 static char hostname[255];
2258 Tcl_DString dString;
2261 if (hostnameInited) {
2265 if (TclpHasSockets(NULL) == TCL_OK) {
2266 err = GetLocalAddress(&ourAddress);
2269 * Search for the doman name and return it if found. Otherwise,
2270 * just print the IP number to a string and return that.
2273 Tcl_DStringInit(&dString);
2274 err = ResolveAddress(ourAddress, &dString);
2276 strcpy(hostname, dString.string);
2278 sprintf(hostname, "%d.%d.%d.%d", ourAddress>>24, ourAddress>>16 & 0xff,
2279 ourAddress>>8 & 0xff, ourAddress & 0xff);
2281 Tcl_DStringFree(&dString);
2294 *----------------------------------------------------------------------
2298 * This function is used to resolve an ip address to it's full
2299 * domain name address.
2305 * Treats client data as int we set to true.
2307 *----------------------------------------------------------------------
2312 ip_addr tcpAddress, /* Address to resolve. */
2313 Tcl_DString *dsPtr) /* Returned address in string. */
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
2326 for (i = 0; i < NUM_ALT_ADDRS; i++) {
2327 dnrState.hostInfo.addr[i] = 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);
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.
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';
2349 Tcl_DStringAppend(dsPtr, dnrState.hostInfo.cname, -1);
2352 return dnrState.hostInfo.rtnCode;
2356 *----------------------------------------------------------------------
2358 * DNRCompletionRoutine --
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.
2368 * Treats client data as int we set to true.
2370 *----------------------------------------------------------------------
2374 DNRCompletionRoutine(
2375 struct hostInfo *hostinfoPtr, /* Host infor struct. */
2376 DNRState *dnrStatePtr) /* Completetion state. */
2378 dnrStatePtr->done = true;
2379 WakeUpProcess(&(dnrStatePtr->psn));
2383 *----------------------------------------------------------------------
2385 * CleanUpExitProc --
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.
2397 *----------------------------------------------------------------------
2405 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
2407 while (tsdPtr->socketList != NULL) {
2408 statePtr = tsdPtr->socketList;
2409 tsdPtr->socketList = statePtr->nextPtr;
2412 * Close and Release the connection.
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);
2424 exitPB.ioCRefNum = driverRefNum;
2425 exitPB.csCode = TCPRelease;
2426 exitPB.tcpStream = statePtr->tcpStream;
2427 exitPB.ioCompletion = NULL;
2428 PBControlSync((ParmBlkPtr) &exitPB);
2433 *----------------------------------------------------------------------
2435 * GetHostFromString --
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.
2442 * We return a ip address or 0 if there was an error or the
2443 * domain does not exist.
2448 *----------------------------------------------------------------------
2453 CONST char *name, /* Host in string form. */
2454 ip_addr *address) /* Returned IP address. */
2461 if (TclpHasSockets(NULL) != TCL_OK) {
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.
2471 for (i = 0; i < NUM_ALT_ADDRS; i++) {
2472 dnrState.hostInfo.addr[i] = 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);
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.
2489 if (dnrState.hostInfo.rtnCode == cacheFault) {
2491 err = StrToAddr((char*)name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
2492 if (err == cacheFault) {
2493 while (!dnrState.done) {
2494 WaitNextEvent(0, &dummy, 1, NULL);
2499 if (dnrState.hostInfo.rtnCode == noErr) {
2500 *address = dnrState.hostInfo.addr[0];
2503 return dnrState.hostInfo.rtnCode;
2507 *----------------------------------------------------------------------
2509 * IOCompletionRoutine --
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
2522 * Sets some state in the socket state. May also wake the process
2523 * if we are not currently running.
2525 *----------------------------------------------------------------------
2529 IOCompletionRoutine(
2530 TCPiopb *pbPtr) /* Tcp parameter block. */
2534 if (pbPtr->csCode == TCPSend) {
2535 statePtr = (TcpState *) pbPtr->csParam.send.userDataPtr;
2537 statePtr = (TcpState *) pbPtr->csParam.open.userDataPtr;
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.
2546 WakeUpProcess(&statePtr->psn);
2547 if (pbPtr->ioResult != noErr) {
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) {
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;
2578 *----------------------------------------------------------------------
2580 * GetLocalAddress --
2582 * Get the IP address for this machine. The result is cached so
2583 * the result is returned quickly after the first call.
2586 * Macintosh error code.
2591 *----------------------------------------------------------------------
2596 unsigned long *addr) /* Returns host IP address. */
2598 struct GetAddrParamBlock pBlock;
2600 static unsigned long localAddress = 0;
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);
2612 localAddress = pBlock.ourAddress;
2615 *addr = localAddress;
2620 *----------------------------------------------------------------------
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.
2631 * Size of optimal buffer in bytes.
2636 *----------------------------------------------------------------------
2646 memset(&iopb, 0, sizeof(iopb));
2647 err = GetLocalAddress(&iopb.csParam.mtu.remoteHost);
2649 return CHANNEL_BUF_SIZE;
2651 iopb.ioCRefNum = driverRefNum;
2652 iopb.csCode = UDPMaxMTUSize;
2653 err = PBControlSync((ParmBlkPtr)&iopb);
2655 return CHANNEL_BUF_SIZE;
2657 bufferSize = (iopb.csParam.mtu.mtuSize * 4) + 1024;
2658 if (bufferSize < CHANNEL_BUF_SIZE) {
2659 bufferSize = CHANNEL_BUF_SIZE;
2665 *----------------------------------------------------------------------
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.
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.
2681 *----------------------------------------------------------------------
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 */
2692 PortInfo *portInfoPtr = NULL;
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",
2701 Tcl_AppendResult(interp, "couldn't open socket: negative port number",
2707 for (portInfoPtr = portServices; portInfoPtr->name != NULL; portInfoPtr++) {
2708 if (!strcmp(portInfoPtr->name, string)) {
2712 if (portInfoPtr != NULL && portInfoPtr->name != NULL) {
2713 *portPtr = portInfoPtr->port;
2714 Tcl_ResetResult(interp);
2722 *----------------------------------------------------------------------
2724 * ClearZombieSockets --
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.
2735 * Tcp streams may be released.
2737 *----------------------------------------------------------------------
2741 ClearZombieSockets()
2744 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
2746 for (statePtr = tsdPtr->socketList; statePtr != NULL;
2747 statePtr = statePtr->nextPtr) {
2748 if (statePtr->flags & TCP_RELEASE) {
2749 SocketFreeProc(statePtr);
2757 *----------------------------------------------------------------------
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
2771 *----------------------------------------------------------------------
2773 pascal void NotifyRoutine (
2774 StreamPtr tcpStream,
2775 unsigned short eventCode,
2777 unsigned short terminReason,
2778 struct ICMPReport *icmpMsg)
2780 StreamPtr localTcpStream;
2781 unsigned short localEventCode;
2782 unsigned short localTerminReason;
2783 struct ICMPReport localIcmpMsg;
2785 localTcpStream = tcpStream;
2786 localEventCode = eventCode;
2787 localTerminReason = terminReason;
2788 localIcmpMsg = *icmpMsg;