tclMacSock.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:74k
- /*
- * tclMacSock.c
- *
- * Channel drivers for Macintosh sockets.
- *
- * Copyright (c) 1996-1997 Sun Microsystems, Inc.
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tclMacSock.c,v 1.14.2.1 2006/03/10 14:27:41 vasiljevic Exp $
- */
- #include "tclInt.h"
- #include "tclPort.h"
- #include "tclMacInt.h"
- #include <AddressXlation.h>
- #include <Aliases.h>
- #undef Status
- #include <Devices.h>
- #include <Errors.h>
- #include <Events.h>
- #include <Files.h>
- #include <Gestalt.h>
- #include <MacTCP.h>
- #include <Processes.h>
- #include <Strings.h>
- /*
- * The following variable is used to tell whether this module has been
- * initialized.
- */
- static int initialized = 0;
- /*
- * If debugging is on we may drop into the debugger to handle certain cases
- * that are not supposed to happen. Otherwise, we change ignore the error
- * and most code should handle such errors ok.
- */
- #ifndef TCL_DEBUG
- #define Debugger()
- #endif
- /*
- * The preferred buffer size for Macintosh channels.
- */
- #define CHANNEL_BUF_SIZE 8192
- /*
- * Port information structure. Used to match service names
- * to a Tcp/Ip port number.
- */
- typedef struct {
- char *name; /* Name of service. */
- int port; /* Port number. */
- } PortInfo;
- /*
- * This structure describes per-instance state of a tcp based channel.
- */
- typedef struct TcpState {
- TCPiopb pb; /* Parameter block used by this stream.
- * This must be in the first position. */
- ProcessSerialNumber psn; /* PSN used to wake up process. */
- StreamPtr tcpStream; /* Macintosh tcp stream pointer. */
- int port; /* The port we are connected to. */
- int flags; /* Bit field comprised of the flags
- * described below. */
- int checkMask; /* OR'ed combination of TCL_READABLE and
- * TCL_WRITABLE as set by an asynchronous
- * event handler. */
- int watchMask; /* OR'ed combination of TCL_READABLE and
- * TCL_WRITABLE as set by TcpWatch. */
- Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */
- ClientData acceptProcData; /* The data for the accept proc. */
- wdsEntry dataSegment[2]; /* List of buffers to be written async. */
- rdsEntry rdsarray[5+1]; /* Array used when cleaning out recieve
- * buffers on a closing socket. */
- Tcl_Channel channel; /* Channel associated with this socket. */
- int writeBufferSize; /* Size of buffer to hold data for
- * asynchronous writes. */
- void *writeBuffer; /* Buffer for async write data. */
- struct TcpState *nextPtr; /* The next socket on the global socket
- * list. */
- } TcpState;
- /*
- * This structure is used by domain name resolver callback.
- */
- typedef struct DNRState {
- struct hostInfo hostInfo; /* Data structure used by DNR functions. */
- int done; /* Flag to determine when we are done. */
- ProcessSerialNumber psn; /* Process to wake up when we are done. */
- } DNRState;
- /*
- * The following macros may be used to set the flags field of
- * a TcpState structure.
- */
- #define TCP_ASYNC_SOCKET (1<<0) /* The socket is in async mode. */
- #define TCP_ASYNC_CONNECT (1<<1) /* The socket is trying to connect. */
- #define TCP_CONNECTED (1<<2) /* The socket is connected. */
- #define TCP_PENDING (1<<3) /* A SocketEvent is on the queue. */
- #define TCP_LISTENING (1<<4) /* This socket is listening for
- * a connection. */
- #define TCP_LISTEN_CONNECT (1<<5) /* Someone has connect to the
- * listening port. */
- #define TCP_REMOTE_CLOSED (1<<6) /* The remote side has closed
- * the connection. */
- #define TCP_RELEASE (1<<7) /* The socket may now be released. */
- #define TCP_WRITING (1<<8) /* A background write is in progress. */
- #define TCP_SERVER_ZOMBIE (1<<9) /* The server can no longer accept connects. */
- /*
- * The following structure is what is added to the Tcl event queue when
- * a socket event occurs.
- */
- typedef struct SocketEvent {
- Tcl_Event header; /* Information that is standard for
- * all events. */
- TcpState *statePtr; /* Socket descriptor that is ready. */
- StreamPtr tcpStream; /* Low level Macintosh stream. */
- } SocketEvent;
- /*
- * Static routines for this file:
- */
- static pascal void CleanUpExitProc _ANSI_ARGS_((void));
- static void ClearZombieSockets _ANSI_ARGS_((void));
- static void CloseCompletionRoutine _ANSI_ARGS_((TCPiopb *pb));
- static TcpState * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
- int port, CONST char *host, CONST char *myAddr,
- int myPort, int server, int async));
- static pascal void DNRCompletionRoutine _ANSI_ARGS_((
- struct hostInfo *hostinfoPtr,
- DNRState *dnrStatePtr));
- static void FreeSocketInfo _ANSI_ARGS_((TcpState *statePtr));
- static long GetBufferSize _ANSI_ARGS_((void));
- static OSErr GetHostFromString _ANSI_ARGS_((CONST char *name,
- ip_addr *address));
- static OSErr GetLocalAddress _ANSI_ARGS_((unsigned long *addr));
- static void IOCompletionRoutine _ANSI_ARGS_((TCPiopb *pb));
- static void InitMacTCPParamBlock _ANSI_ARGS_((TCPiopb *pBlock,
- int csCode));
- static void InitSockets _ANSI_ARGS_((void));
- static TcpState * NewSocketInfo _ANSI_ARGS_((StreamPtr stream));
- static OSErr ResolveAddress _ANSI_ARGS_((ip_addr tcpAddress,
- Tcl_DString *dsPtr));
- static void SocketCheckProc _ANSI_ARGS_((ClientData clientData,
- int flags));
- static int SocketEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
- int flags));
- static void SocketFreeProc _ANSI_ARGS_((ClientData clientData));
- static int SocketReady _ANSI_ARGS_((TcpState *statePtr));
- static void SocketSetupProc _ANSI_ARGS_((ClientData clientData,
- int flags));
- static void TcpAccept _ANSI_ARGS_((TcpState *statePtr));
- static int TcpBlockMode _ANSI_ARGS_((ClientData instanceData, int mode));
- static int TcpClose _ANSI_ARGS_((ClientData instanceData,
- Tcl_Interp *interp));
- static int TcpGetHandle _ANSI_ARGS_((ClientData instanceData,
- int direction, ClientData *handlePtr));
- static int TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
- Tcl_Interp *interp, CONST char *optionName,
- Tcl_DString *dsPtr));
- static int TcpInput _ANSI_ARGS_((ClientData instanceData,
- char *buf, int toRead, int *errorCodePtr));
- static int TcpOutput _ANSI_ARGS_((ClientData instanceData,
- CONST char *buf, int toWrite, int *errorCodePtr));
- static void TcpWatch _ANSI_ARGS_((ClientData instanceData,
- int mask));
- static int WaitForSocketEvent _ANSI_ARGS_((TcpState *infoPtr,
- int mask, int *errorCodePtr));
- pascal void NotifyRoutine (
- StreamPtr tcpStream,
- unsigned short eventCode,
- Ptr userDataPtr,
- unsigned short terminReason,
- struct ICMPReport *icmpMsg);
-
- /*
- * This structure describes the channel type structure for TCP socket
- * based IO:
- */
- static Tcl_ChannelType tcpChannelType = {
- "tcp", /* Type name. */
- (Tcl_ChannelTypeVersion)TcpBlockMode, /* Set blocking or
- * non-blocking mode.*/
- TcpClose, /* Close proc. */
- TcpInput, /* Input proc. */
- TcpOutput, /* Output proc. */
- NULL, /* Seek proc. */
- NULL, /* Set option proc. */
- TcpGetOptionProc, /* Get option proc. */
- TcpWatch, /* Initialize notifier. */
- TcpGetHandle /* Get handles out of channel. */
- };
- /*
- * Universal Procedure Pointers (UPP) for various callback
- * routines used by MacTcp code.
- */
- ResultUPP resultUPP = NULL;
- TCPIOCompletionUPP completeUPP = NULL;
- TCPIOCompletionUPP closeUPP = NULL;
- TCPNotifyUPP notifyUPP = NULL;
- /*
- * Built-in commands, and the procedures associated with them:
- */
- static PortInfo portServices[] = {
- {"echo", 7},
- {"discard", 9},
- {"systat", 11},
- {"daytime", 13},
- {"netstat", 15},
- {"chargen", 19},
- {"ftp-data", 20},
- {"ftp", 21},
- {"telnet", 23},
- {"telneto", 24},
- {"smtp", 25},
- {"time", 37},
- {"whois", 43},
- {"domain", 53},
- {"gopher", 70},
- {"finger", 79},
- {"hostnames", 101},
- {"sunrpc", 111},
- {"nntp", 119},
- {"exec", 512},
- {"login", 513},
- {"shell", 514},
- {"printer", 515},
- {"courier", 530},
- {"uucp", 540},
- {NULL, 0},
- };
- typedef struct ThreadSpecificData {
- /*
- * Every open socket has an entry on the following list.
- */
-
- TcpState *socketList;
- } ThreadSpecificData;
- static Tcl_ThreadDataKey dataKey;
- /*
- * Globals for holding information about OS support for sockets.
- */
- static int socketsTestInited = false;
- static int hasSockets = false;
- static short driverRefNum = 0;
- static int socketNumber = 0;
- static int socketBufferSize = CHANNEL_BUF_SIZE;
- static ProcessSerialNumber applicationPSN;
- /*
- *----------------------------------------------------------------------
- *
- * InitSockets --
- *
- * Load the MacTCP driver and open the name resolver. We also
- * create several UPP's used by our code. Lastly, we install
- * a patch to ExitToShell to clean up socket connections if
- * we are about to exit.
- *
- * Results:
- * 1 if successful, 0 on failure.
- *
- * Side effects:
- * Creates a new event source, loads the MacTCP driver,
- * registers an exit to shell callback.
- *
- *----------------------------------------------------------------------
- */
- #define gestaltMacTCPVersion 'mtcp'
- static void
- InitSockets()
- {
- ParamBlockRec pb;
- OSErr err;
- long response;
- ThreadSpecificData *tsdPtr;
-
- if (! initialized) {
- /*
- * Do process wide initialization.
- */
- initialized = 1;
-
- if (Gestalt(gestaltMacTCPVersion, &response) == noErr) {
- hasSockets = true;
- } else {
- hasSockets = false;
- }
-
- if (!hasSockets) {
- return;
- }
-
- /*
- * Load MacTcp driver and name server resolver.
- */
-
-
- pb.ioParam.ioCompletion = 0L;
- pb.ioParam.ioNamePtr = "p.IPP";
- pb.ioParam.ioPermssn = fsCurPerm;
- err = PBOpenSync(&pb);
- if (err != noErr) {
- hasSockets = 0;
- return;
- }
- driverRefNum = pb.ioParam.ioRefNum;
-
- socketBufferSize = GetBufferSize();
- err = OpenResolver(NULL);
- if (err != noErr) {
- hasSockets = 0;
- return;
- }
-
- GetCurrentProcess(&applicationPSN);
- /*
- * Create UPP's for various callback routines.
- */
-
- resultUPP = NewResultProc(DNRCompletionRoutine);
- completeUPP = NewTCPIOCompletionProc(IOCompletionRoutine);
- closeUPP = NewTCPIOCompletionProc(CloseCompletionRoutine);
- notifyUPP = NewTCPNotifyProc(NotifyRoutine);
-
- /*
- * Install an ExitToShell patch. We use this patch instead
- * of the Tcl exit mechanism because we need to ensure that
- * these routines are cleaned up even if we crash or are forced
- * to quit. There are some circumstances when the Tcl exit
- * handlers may not fire.
- */
-
- TclMacInstallExitToShellPatch(CleanUpExitProc);
- }
- /*
- * Do per-thread initialization.
- */
- tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
- if (tsdPtr == NULL) {
- tsdPtr = TCL_TSD_INIT(&dataKey);
- tsdPtr->socketList = NULL;
- Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclpFinalizeSockets --
- *
- * Invoked during exit clean up to deinitialize the socket module.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Removed event source.
- *
- *----------------------------------------------------------------------
- */
- void
- TclpFinalizeSockets()
- {
- ThreadSpecificData *tsdPtr;
- tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
- if (tsdPtr != NULL) {
- Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclpHasSockets --
- *
- * This function determines whether sockets are available on the
- * current system and returns an error in interp if they are not.
- * Note that interp may be NULL.
- *
- * Results:
- * Returns TCL_OK if the system supports sockets, or TCL_ERROR with
- * an error in interp.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- TclpHasSockets(
- Tcl_Interp *interp) /* Interp for error messages. */
- {
- InitSockets();
- if (hasSockets) {
- return TCL_OK;
- }
- if (interp != NULL) {
- Tcl_AppendResult(interp, "sockets are not available on this system",
- NULL);
- }
- return TCL_ERROR;
- }
- /*
- *----------------------------------------------------------------------
- *
- * SocketSetupProc --
- *
- * This procedure is invoked before Tcl_DoOneEvent blocks waiting
- * for an event.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Adjusts the block time if needed.
- *
- *----------------------------------------------------------------------
- */
- static void
- SocketSetupProc(
- ClientData data, /* Not used. */
- int flags) /* Event flags as passed to Tcl_DoOneEvent. */
- {
- TcpState *statePtr;
- Tcl_Time blockTime = { 0, 0 };
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- if (!(flags & TCL_FILE_EVENTS)) {
- return;
- }
-
- /*
- * Check to see if there is a ready socket. If so, poll.
- */
- for (statePtr = tsdPtr->socketList; statePtr != NULL;
- statePtr = statePtr->nextPtr) {
- if (statePtr->flags & TCP_RELEASE) {
- continue;
- }
- if (SocketReady(statePtr)) {
- Tcl_SetMaxBlockTime(&blockTime);
- break;
- }
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * SocketCheckProc --
- *
- * This procedure is called by Tcl_DoOneEvent to check the socket
- * event source for events.
- *
- * Results:
- * None.
- *
- * Side effects:
- * May queue an event.
- *
- *----------------------------------------------------------------------
- */
- static void
- SocketCheckProc(
- ClientData data, /* Not used. */
- int flags) /* Event flags as passed to Tcl_DoOneEvent. */
- {
- TcpState *statePtr;
- SocketEvent *evPtr;
- TcpState dummyState;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- if (!(flags & TCL_FILE_EVENTS)) {
- return;
- }
-
- /*
- * Queue events for any ready sockets that don't already have events
- * queued (caused by persistent states that won't generate WinSock
- * events).
- */
- for (statePtr = tsdPtr->socketList; statePtr != NULL;
- statePtr = statePtr->nextPtr) {
- /*
- * Check to see if this socket is dead and needs to be cleaned
- * up. We use a dummy statePtr whose only valid field is the
- * nextPtr to allow the loop to continue even if the element
- * is deleted.
- */
- if (statePtr->flags & TCP_RELEASE) {
- if (!(statePtr->flags & TCP_PENDING)) {
- dummyState.nextPtr = statePtr->nextPtr;
- SocketFreeProc(statePtr);
- statePtr = &dummyState;
- }
- continue;
- }
- if (!(statePtr->flags & TCP_PENDING) && SocketReady(statePtr)) {
- statePtr->flags |= TCP_PENDING;
- evPtr = (SocketEvent *) ckalloc(sizeof(SocketEvent));
- evPtr->header.proc = SocketEventProc;
- evPtr->statePtr = statePtr;
- evPtr->tcpStream = statePtr->tcpStream;
- Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
- }
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * SocketReady --
- *
- * This function checks the current state of a socket to see
- * if any interesting conditions are present.
- *
- * Results:
- * Returns 1 if an event that someone is watching is present, else
- * returns 0.
- *
- * Side effects:
- * Updates the checkMask for the socket to reflect any newly
- * detected events.
- *
- *----------------------------------------------------------------------
- */
- static int
- SocketReady(
- TcpState *statePtr)
- {
- TCPiopb statusPB;
- int foundSomething = 0;
- int didStatus = 0;
- int amount;
- OSErr err;
- if (statePtr->flags & TCP_LISTEN_CONNECT) {
- foundSomething = 1;
- statePtr->checkMask |= TCL_READABLE;
- }
- if (statePtr->watchMask & TCL_READABLE) {
- if (statePtr->checkMask & TCL_READABLE) {
- foundSomething = 1;
- } else if (statePtr->flags & TCP_CONNECTED) {
- statusPB.ioCRefNum = driverRefNum;
- statusPB.tcpStream = statePtr->tcpStream;
- statusPB.csCode = TCPStatus;
- err = PBControlSync((ParmBlkPtr) &statusPB);
- didStatus = 1;
- /*
- * We make the fchannel readable if 1) we get an error,
- * 2) there is more data available, or 3) we detect
- * that a close from the remote connection has arrived.
- */
- if ((err != noErr) ||
- (statusPB.csParam.status.amtUnreadData > 0) ||
- (statusPB.csParam.status.connectionState == 14)) {
- statePtr->checkMask |= TCL_READABLE;
- foundSomething = 1;
- }
- }
- }
- if (statePtr->watchMask & TCL_WRITABLE) {
- if (statePtr->checkMask & TCL_WRITABLE) {
- foundSomething = 1;
- } else if (statePtr->flags & TCP_CONNECTED) {
- if (!didStatus) {
- statusPB.ioCRefNum = driverRefNum;
- statusPB.tcpStream = statePtr->tcpStream;
- statusPB.csCode = TCPStatus;
- err = PBControlSync((ParmBlkPtr) &statusPB);
- }
- /*
- * If there is an error or there if there is room to
- * send more data we make the channel writeable.
- */
- amount = statusPB.csParam.status.sendWindow -
- statusPB.csParam.status.amtUnackedData;
- if ((err != noErr) || (amount > 0)) {
- statePtr->checkMask |= TCL_WRITABLE;
- foundSomething = 1;
- }
- }
- }
- return foundSomething;
- }
- /*
- *----------------------------------------------------------------------
- *
- * InitMacTCPParamBlock--
- *
- * Initialize a MacTCP parameter block.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Initializes the parameter block.
- *
- *----------------------------------------------------------------------
- */
- static void
- InitMacTCPParamBlock(
- TCPiopb *pBlock, /* Tcp parmeter block. */
- int csCode) /* Tcp operation code. */
- {
- memset(pBlock, 0, sizeof(TCPiopb));
- pBlock->ioResult = 1;
- pBlock->ioCRefNum = driverRefNum;
- pBlock->csCode = (short) csCode;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TcpBlockMode --
- *
- * Set blocking or non-blocking mode on channel.
- *
- * Results:
- * 0 if successful, errno when failed.
- *
- * Side effects:
- * Sets the device into blocking or non-blocking mode.
- *
- *----------------------------------------------------------------------
- */
- static int
- TcpBlockMode(
- ClientData instanceData, /* Channel state. */
- int mode) /* The mode to set. */
- {
- TcpState *statePtr = (TcpState *) instanceData;
-
- if (mode == TCL_MODE_BLOCKING) {
- statePtr->flags &= ~TCP_ASYNC_SOCKET;
- } else {
- statePtr->flags |= TCP_ASYNC_SOCKET;
- }
- return 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TcpClose --
- *
- * Close the socket.
- *
- * Results:
- * 0 if successful, the value of errno if failed.
- *
- * Side effects:
- * Closes the socket.
- *
- *----------------------------------------------------------------------
- */
- static int
- TcpClose(
- ClientData instanceData, /* The socket to close. */
- Tcl_Interp *interp) /* Interp for error messages. */
- {
- TcpState *statePtr = (TcpState *) instanceData;
- StreamPtr tcpStream;
- TCPiopb closePB;
- OSErr err;
- tcpStream = statePtr->tcpStream;
- statePtr->flags &= ~TCP_CONNECTED;
-
- /*
- * If this is a server socket we can't use the statePtr
- * param block because it is in use. However, we can
- * close syncronously.
- */
- if ((statePtr->flags & TCP_LISTENING) ||
- (statePtr->flags & TCP_LISTEN_CONNECT)) {
- InitMacTCPParamBlock(&closePB, TCPClose);
- closePB.tcpStream = tcpStream;
- closePB.ioCompletion = NULL;
- closePB.csParam.close.ulpTimeoutValue = 60 /* seconds */;
- closePB.csParam.close.ulpTimeoutAction = 1 /* 1:abort 0:report */;
- closePB.csParam.close.validityFlags = timeoutValue | timeoutAction;
- err = PBControlSync((ParmBlkPtr) &closePB);
- if (err != noErr) {
- Debugger();
- goto afterRelease;
- /* panic("error closing server socket"); */
- }
- statePtr->flags |= TCP_RELEASE;
- /*
- * Server sockets are closed sync. Therefor, we know it is OK to
- * release the socket now.
- */
- InitMacTCPParamBlock(&statePtr->pb, TCPRelease);
- statePtr->pb.tcpStream = statePtr->tcpStream;
- err = PBControlSync((ParmBlkPtr) &statePtr->pb);
- if (err != noErr) {
- panic("error releasing server socket");
- }
- /*
- * Free the buffer space used by the socket and the
- * actual socket state data structure.
- */
- afterRelease:
-
- /*
- * Have to check whether the pointer is NULL, since we could get here
- * on a failed socket open, and then the rcvBuff would never have been
- * allocated.
- */
-
- if (err == noErr) {
- ckfree((char *) statePtr->pb.csParam.create.rcvBuff);
- }
- FreeSocketInfo(statePtr);
- return 0;
- }
- /*
- * If this socket is in the midddle on async connect we can just
- * abort the connect and release the stream right now.
- */
-
- if (statePtr->flags & TCP_ASYNC_CONNECT) {
- InitMacTCPParamBlock(&closePB, TCPClose);
- closePB.tcpStream = tcpStream;
- closePB.ioCompletion = NULL;
- err = PBControlSync((ParmBlkPtr) &closePB);
- if (err == noErr) {
- statePtr->flags |= TCP_RELEASE;
- InitMacTCPParamBlock(&closePB, TCPRelease);
- closePB.tcpStream = tcpStream;
- closePB.ioCompletion = NULL;
- err = PBControlSync((ParmBlkPtr) &closePB);
- }
- /*
- * Free the buffer space used by the socket and the
- * actual socket state data structure. However, if the
- * RELEASE returns an error, then the rcvBuff is usually
- * bad, so we can't release it. I think this means we will
- * leak the buffer, so in the future, we may want to track the
- * buffers separately, and nuke them on our own (or just not
- * use MacTCP!).
- */
- if (err == noErr) {
- ckfree((char *) closePB.csParam.create.rcvBuff);
- }
-
- FreeSocketInfo(statePtr);
- return err;
- }
- /*
- * Client sockets:
- * If a background write is in progress, don't close
- * the socket yet. The completion routine for the
- * write will take care of it.
- */
-
- if (!(statePtr->flags & TCP_WRITING)) {
- InitMacTCPParamBlock(&statePtr->pb, TCPClose);
- statePtr->pb.tcpStream = tcpStream;
- statePtr->pb.ioCompletion = closeUPP;
- statePtr->pb.csParam.close.userDataPtr = (Ptr) statePtr;
- err = PBControlAsync((ParmBlkPtr) &statePtr->pb);
- if (err != noErr) {
- Debugger();
- statePtr->flags |= TCP_RELEASE;
- /* return 0; */
- }
- }
- SocketFreeProc(instanceData);
- return 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * CloseCompletionRoutine --
- *
- * Handles the close protocol for a Tcp socket. This will do
- * a series of calls to release all data currently buffered for
- * the socket. This is important to do to as it allows the remote
- * connection to recieve and issue it's own close on the socket.
- * Note that this function is running at interupt time and can't
- * allocate memory or do much else except set state.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The buffers for the socket are flushed.
- *
- *----------------------------------------------------------------------
- */
- static void
- CloseCompletionRoutine(
- TCPiopb *pbPtr) /* Tcp parameter block. */
- {
- TcpState *statePtr;
- OSErr err;
-
- if (pbPtr->csCode == TCPClose) {
- statePtr = (TcpState *) (pbPtr->csParam.close.userDataPtr);
- } else {
- statePtr = (TcpState *) (pbPtr->csParam.receive.userDataPtr);
- }
- /*
- * It's very bad if the statePtr is nNULL - we should probably panic...
- */
- if (statePtr == NULL) {
- Debugger();
- return;
- }
-
- WakeUpProcess(&statePtr->psn);
- /*
- * If there is an error we assume the remote side has already
- * close. We are done closing as soon as we decide that the
- * remote connection has closed.
- */
-
- if (pbPtr->ioResult != noErr) {
- statePtr->flags |= TCP_RELEASE;
- return;
- }
- if (statePtr->flags & TCP_REMOTE_CLOSED) {
- statePtr->flags |= TCP_RELEASE;
- return;
- }
-
- /*
- * If we just did a recieve we need to return the buffers.
- * Otherwise, attempt to recieve more data until we recieve an
- * error (usually because we have no more data).
- */
- if (statePtr->pb.csCode == TCPNoCopyRcv) {
- InitMacTCPParamBlock(&statePtr->pb, TCPRcvBfrReturn);
- statePtr->pb.tcpStream = statePtr->tcpStream;
- statePtr->pb.ioCompletion = closeUPP;
- statePtr->pb.csParam.receive.rdsPtr = (Ptr) statePtr->rdsarray;
- statePtr->pb.csParam.receive.userDataPtr = (Ptr) statePtr;
- err = PBControlAsync((ParmBlkPtr) &statePtr->pb);
- } else {
- InitMacTCPParamBlock(&statePtr->pb, TCPNoCopyRcv);
- statePtr->pb.tcpStream = statePtr->tcpStream;
- statePtr->pb.ioCompletion = closeUPP;
- statePtr->pb.csParam.receive.commandTimeoutValue = 1;
- statePtr->pb.csParam.receive.rdsPtr = (Ptr) statePtr->rdsarray;
- statePtr->pb.csParam.receive.rdsLength = 5;
- statePtr->pb.csParam.receive.userDataPtr = (Ptr) statePtr;
- err = PBControlAsync((ParmBlkPtr) &statePtr->pb);
- }
- if (err != noErr) {
- statePtr->flags |= TCP_RELEASE;
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * SocketFreeProc --
- *
- * This callback is invoked in order to delete
- * the notifier data associated with a file handle.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Removes the SocketInfo from the global socket list.
- *
- *----------------------------------------------------------------------
- */
- static void
- SocketFreeProc(
- ClientData clientData) /* Channel state. */
- {
- TcpState *statePtr = (TcpState *) clientData;
- OSErr err;
- TCPiopb statusPB;
- /*
- * Get the status of this connection. We need to do a
- * few tests to see if it's OK to release the stream now.
- */
- if (!(statePtr->flags & TCP_RELEASE)) {
- return;
- }
- statusPB.ioCRefNum = driverRefNum;
- statusPB.tcpStream = statePtr->tcpStream;
- statusPB.csCode = TCPStatus;
- err = PBControlSync((ParmBlkPtr) &statusPB);
- if ((statusPB.csParam.status.connectionState == 0) ||
- (statusPB.csParam.status.connectionState == 2)) {
- /*
- * If the conection state is 0 then this was a client
- * connection and it's closed. If it is 2 then this a
- * server client and we may release it. If it isn't
- * one of those values then we return and we'll try to
- * clean up later.
- */
- } else {
- return;
- }
-
- /*
- * The Close request is made async. We know it's
- * OK to release the socket when the TCP_RELEASE flag
- * gets set.
- */
- InitMacTCPParamBlock(&statePtr->pb, TCPRelease);
- statePtr->pb.tcpStream = statePtr->tcpStream;
- err = PBControlSync((ParmBlkPtr) &statePtr->pb);
- if (err != noErr) {
- Debugger(); /* Ignoreing leaves stranded stream. Is there an
- alternative? */
- }
- /*
- * Free the buffer space used by the socket and the
- * actual socket state data structure.
- */
- ckfree((char *) statePtr->pb.csParam.create.rcvBuff);
- FreeSocketInfo(statePtr);
- }
- /*
- *----------------------------------------------------------------------
- *
- * TcpInput --
- *
- * Reads input from the IO channel into the buffer given. Returns
- * count of how many bytes were actually read, and an error
- * indication.
- *
- * Results:
- * A count of how many bytes were read is returned. A value of -1
- * implies an error occured. A value of zero means we have reached
- * the end of data (EOF).
- *
- * Side effects:
- * Reads input from the actual channel.
- *
- *----------------------------------------------------------------------
- */
- int
- TcpInput(
- ClientData instanceData, /* Channel state. */
- char *buf, /* Where to store data read. */
- int bufSize, /* How much space is available
- * in the buffer? */
- int *errorCodePtr) /* Where to store error code. */
- {
- TcpState *statePtr = (TcpState *) instanceData;
- StreamPtr tcpStream;
- OSErr err;
- TCPiopb statusPB;
- int toRead, dataAvail;
- *errorCodePtr = 0;
- errno = 0;
- tcpStream = statePtr->tcpStream;
- if (bufSize == 0) {
- return 0;
- }
- toRead = bufSize;
- /*
- * First check to see if EOF was already detected, to prevent
- * calling the socket stack after the first time EOF is detected.
- */
- if (statePtr->flags & TCP_REMOTE_CLOSED) {
- return 0;
- }
- /*
- * If an asynchronous connect is in progress, attempt to wait for it
- * to complete before reading.
- */
-
- if ((statePtr->flags & TCP_ASYNC_CONNECT)
- && ! WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) {
- return -1;
- }
- /*
- * No EOF, and it is connected, so try to read more from the socket.
- * If the socket is blocking, we keep trying until there is data
- * available or the socket is closed.
- */
- while (1) {
- statusPB.ioCRefNum = driverRefNum;
- statusPB.tcpStream = tcpStream;
- statusPB.csCode = TCPStatus;
- err = PBControlSync((ParmBlkPtr) &statusPB);
- if (err != noErr) {
- Debugger();
- statePtr->flags |= TCP_REMOTE_CLOSED;
- return 0; /* EOF */
- }
- dataAvail = statusPB.csParam.status.amtUnreadData;
- if (dataAvail < bufSize) {
- toRead = dataAvail;
- } else {
- toRead = bufSize;
- }
- if (toRead != 0) {
- /*
- * Try to read the data.
- */
-
- InitMacTCPParamBlock(&statusPB, TCPRcv);
- statusPB.tcpStream = tcpStream;
- statusPB.csParam.receive.rcvBuff = buf;
- statusPB.csParam.receive.rcvBuffLen = toRead;
- err = PBControlSync((ParmBlkPtr) &statusPB);
- statePtr->checkMask &= ~TCL_READABLE;
- switch (err) {
- case noErr:
- /*
- * The channel remains readable only if this read succeds
- * and we had more data then the size of the buffer we were
- * trying to fill. Use the info from the call to status to
- * determine this.
- */
- if (dataAvail > bufSize) {
- statePtr->checkMask |= TCL_READABLE;
- }
- return statusPB.csParam.receive.rcvBuffLen;
- case connectionClosing:
- *errorCodePtr = errno = ESHUTDOWN;
- statePtr->flags |= TCP_REMOTE_CLOSED;
- return 0;
- case connectionDoesntExist:
- case connectionTerminated:
- *errorCodePtr = errno = ENOTCONN;
- statePtr->flags |= TCP_REMOTE_CLOSED;
- return 0;
- case invalidStreamPtr:
- default:
- *errorCodePtr = EINVAL;
- return -1;
- }
- }
- /*
- * No data is available, so check the connection state to
- * see why this is the case.
- */
- if (statusPB.csParam.status.connectionState == 14) {
- statePtr->flags |= TCP_REMOTE_CLOSED;
- return 0;
- }
- if (statusPB.csParam.status.connectionState != 8) {
- Debugger();
- }
- statePtr->checkMask &= ~TCL_READABLE;
- if (statePtr->flags & TCP_ASYNC_SOCKET) {
- *errorCodePtr = EWOULDBLOCK;
- return -1;
- }
- /*
- * In the blocking case, wait until the file becomes readable
- * or closed and try again.
- */
- if (!WaitForSocketEvent(statePtr, TCL_READABLE, errorCodePtr)) {
- return -1;
- }
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * TcpGetHandle --
- *
- * Called from Tcl_GetChannelHandle to retrieve handles from inside
- * a file based channel.
- *
- * Results:
- * The appropriate handle or NULL if not present.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static int
- TcpGetHandle(
- ClientData instanceData, /* The file state. */
- int direction, /* Which handle to retrieve? */
- ClientData *handlePtr)
- {
- TcpState *statePtr = (TcpState *) instanceData;
- *handlePtr = (ClientData) statePtr->tcpStream;
- return TCL_OK;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TcpOutput--
- *
- * Writes the given output on the IO channel. Returns count of how
- * many characters were actually written, and an error indication.
- *
- * Results:
- * A count of how many characters were written is returned and an
- * error indication is returned in an output argument.
- *
- * Side effects:
- * Writes output on the actual channel.
- *
- *----------------------------------------------------------------------
- */
- static int
- TcpOutput(
- ClientData instanceData, /* Channel state. */
- CONST char *buf, /* The data buffer. */
- int toWrite, /* How many bytes to write? */
- int *errorCodePtr) /* Where to store error code. */
- {
- TcpState *statePtr = (TcpState *) instanceData;
- StreamPtr tcpStream;
- OSErr err;
- int amount;
- TCPiopb statusPB;
- *errorCodePtr = 0;
- tcpStream = statePtr->tcpStream;
- /*
- * If an asynchronous connect is in progress, attempt to wait for it
- * to complete before writing.
- */
-
- if ((statePtr->flags & TCP_ASYNC_CONNECT)
- && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) {
- return -1;
- }
- /*
- * Loop until we have written some data, or an error occurs.
- */
- while (1) {
- statusPB.ioCRefNum = driverRefNum;
- statusPB.tcpStream = tcpStream;
- statusPB.csCode = TCPStatus;
- err = PBControlSync((ParmBlkPtr) &statusPB);
- if ((err == connectionDoesntExist) || ((err == noErr) &&
- (statusPB.csParam.status.connectionState == 14))) {
- /*
- * The remote connection is gone away. Report an error
- * and don't write anything.
- */
- *errorCodePtr = errno = EPIPE;
- return -1;
- } else if (err != noErr) {
- return -1;
- }
- amount = statusPB.csParam.status.sendWindow
- - statusPB.csParam.status.amtUnackedData;
- /*
- * Attempt to write the data to the socket if a background
- * write isn't in progress and there is room in the output buffers.
- */
- if (!(statePtr->flags & TCP_WRITING) && amount > 0) {
- if (toWrite < amount) {
- amount = toWrite;
- }
- /* We need to copy the data, otherwise the caller may overwrite
- * the buffer in the middle of our asynchronous call
- */
-
- if (amount > statePtr->writeBufferSize) {
- /*
- * need to grow write buffer
- */
-
- if (statePtr->writeBuffer != (void *) NULL) {
- ckfree(statePtr->writeBuffer);
- }
- statePtr->writeBuffer = (void *) ckalloc(amount);
- statePtr->writeBufferSize = amount;
- }
- memcpy(statePtr->writeBuffer, buf, amount);
- statePtr->dataSegment[0].ptr = statePtr->writeBuffer;
- statePtr->dataSegment[0].length = amount;
- statePtr->dataSegment[1].length = 0;
- InitMacTCPParamBlock(&statePtr->pb, TCPSend);
- statePtr->pb.ioCompletion = completeUPP;
- statePtr->pb.tcpStream = tcpStream;
- statePtr->pb.csParam.send.wdsPtr = (Ptr) statePtr->dataSegment;
- statePtr->pb.csParam.send.pushFlag = 1;
- statePtr->pb.csParam.send.userDataPtr = (Ptr) statePtr;
- statePtr->flags |= TCP_WRITING;
- err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
- switch (err) {
- case noErr:
- return amount;
- case connectionClosing:
- *errorCodePtr = errno = ESHUTDOWN;
- statePtr->flags |= TCP_REMOTE_CLOSED;
- return -1;
- case connectionDoesntExist:
- case connectionTerminated:
- *errorCodePtr = errno = ENOTCONN;
- statePtr->flags |= TCP_REMOTE_CLOSED;
- return -1;
- case invalidStreamPtr:
- default:
- return -1;
- }
- }
- /*
- * The socket wasn't writable. In the non-blocking case, return
- * immediately, otherwise wait until the file becomes writable
- * or closed and try again.
- */
- if (statePtr->flags & TCP_ASYNC_SOCKET) {
- statePtr->checkMask &= ~TCL_WRITABLE;
- *errorCodePtr = EWOULDBLOCK;
- return -1;
- } else if (!WaitForSocketEvent(statePtr, TCL_WRITABLE, errorCodePtr)) {
- return -1;
- }
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * TcpGetOptionProc --
- *
- * Computes an option value for a TCP socket based channel, or a
- * list of all options and their values.
- *
- * Note: This code is based on code contributed by John Haxby.
- *
- * Results:
- * A standard Tcl result. The value of the specified option or a
- * list of all options and their values is returned in the
- * supplied DString.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static int
- TcpGetOptionProc(
- ClientData instanceData, /* Socket state. */
- Tcl_Interp *interp, /* For error reporting - can be NULL.*/
- CONST char *optionName, /* Name of the option to
- * retrieve the value for, or
- * NULL to get all options and
- * their values. */
- Tcl_DString *dsPtr) /* Where to store the computed
- * value; initialized by caller. */
- {
- TcpState *statePtr = (TcpState *) instanceData;
- int doPeerName = false, doSockName = false, doError = false, doAll = false;
- ip_addr tcpAddress;
- char buffer[128];
- OSErr err;
- Tcl_DString dString;
- TCPiopb statusPB;
- int errorCode;
- size_t len = 0;
- /*
- * If an asynchronous connect is in progress, attempt to wait for it
- * to complete before accessing the socket state.
- */
-
- if ((statePtr->flags & TCP_ASYNC_CONNECT)
- && ! WaitForSocketEvent(statePtr, TCL_WRITABLE, &errorCode)) {
- if (interp) {
- /*
- * fix the error message.
- */
- Tcl_AppendResult(interp, "connect is in progress and can't wait",
- NULL);
- }
- return TCL_ERROR;
- }
-
- /*
- * Determine which options we need to do. Do all of them
- * if optionName is NULL.
- */
- if (optionName == (CONST char *) NULL || optionName[0] == ' ') {
- doAll = true;
- } else {
- len = strlen(optionName);
- if (!strncmp(optionName, "-peername", len)) {
- doPeerName = true;
- } else if (!strncmp(optionName, "-sockname", len)) {
- doSockName = true;
- } else if (!strncmp(optionName, "-error", len)) {
- /* SF Bug #483575 */
- doError = true;
- } else {
- return Tcl_BadChannelOption(interp, optionName,
- "error peername sockname");
- }
- }
- /*
- * SF Bug #483575
- *
- * Return error information. Currently we ignore
- * this option. IOW, we always return the empty
- * string, signaling 'no error'.
- *
- * FIXME: Get a mac/socket expert to write a correct
- * FIXME: implementation.
- */
- if (doAll || doError) {
- if (doAll) {
- Tcl_DStringAppendElement(dsPtr, "-error");
- Tcl_DStringAppendElement(dsPtr, "");
- } else {
- Tcl_DStringAppend (dsPtr, "", -1);
- return TCL_OK;
- }
- }
- /*
- * Get status on the stream. Make sure to use a new pb struct because
- * the struct in the statePtr may be part of an asyncronous call.
- */
- statusPB.ioCRefNum = driverRefNum;
- statusPB.tcpStream = statePtr->tcpStream;
- statusPB.csCode = TCPStatus;
- err = PBControlSync((ParmBlkPtr) &statusPB);
- if ((err == connectionDoesntExist) ||
- ((err == noErr) && (statusPB.csParam.status.connectionState == 14))) {
- /*
- * The socket was probably closed on the other side of the connection.
- */
- if (interp) {
- Tcl_AppendResult(interp, "can't access socket info: ",
- "connection reset by peer", NULL);
- }
- return TCL_ERROR;
- } else if (err != noErr) {
- if (interp) {
- Tcl_AppendResult(interp, "unknown socket error", NULL);
- }
- Debugger();
- return TCL_ERROR;
- }
- /*
- * Get the sockname for the socket.
- */
- Tcl_DStringInit(&dString);
- if (doAll || doSockName) {
- if (doAll) {
- Tcl_DStringAppendElement(dsPtr, "-sockname");
- Tcl_DStringStartSublist(dsPtr);
- }
- tcpAddress = statusPB.csParam.status.localHost;
- sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24,
- tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff,
- tcpAddress & 0xff);
- Tcl_DStringAppendElement(dsPtr, buffer);
- if (ResolveAddress(tcpAddress, &dString) == noErr) {
- Tcl_DStringAppendElement(dsPtr, dString.string);
- } else {
- Tcl_DStringAppendElement(dsPtr, "<unknown>");
- }
- sprintf(buffer, "%d", statusPB.csParam.status.localPort);
- Tcl_DStringAppendElement(dsPtr, buffer);
- if (doAll) {
- Tcl_DStringEndSublist(dsPtr);
- }
- }
- /*
- * Get the peername for the socket.
- */
- if ((doAll || doPeerName) && (statePtr->flags & TCP_CONNECTED)) {
- if (doAll) {
- Tcl_DStringAppendElement(dsPtr, "-peername");
- Tcl_DStringStartSublist(dsPtr);
- }
- tcpAddress = statusPB.csParam.status.remoteHost;
- sprintf(buffer, "%d.%d.%d.%d", tcpAddress>>24,
- tcpAddress>>16 & 0xff, tcpAddress>>8 & 0xff,
- tcpAddress & 0xff);
- Tcl_DStringAppendElement(dsPtr, buffer);
- Tcl_DStringSetLength(&dString, 0);
- if (ResolveAddress(tcpAddress, &dString) == noErr) {
- Tcl_DStringAppendElement(dsPtr, dString.string);
- } else {
- Tcl_DStringAppendElement(dsPtr, "<unknown>");
- }
- sprintf(buffer, "%d", statusPB.csParam.status.remotePort);
- Tcl_DStringAppendElement(dsPtr, buffer);
- if (doAll) {
- Tcl_DStringEndSublist(dsPtr);
- }
- }
- Tcl_DStringFree(&dString);
- return TCL_OK;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TcpWatch --
- *
- * Initialize the notifier to watch this channel.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Sets the watchMask for the channel.
- *
- *----------------------------------------------------------------------
- */
- static void
- TcpWatch(instanceData, mask)
- ClientData instanceData; /* The file state. */
- int mask; /* Events of interest; an OR-ed
- * combination of TCL_READABLE,
- * TCL_WRITABLE and TCL_EXCEPTION. */
- {
- TcpState *statePtr = (TcpState *) instanceData;
- statePtr->watchMask = mask;
- }
- /*
- *----------------------------------------------------------------------
- *
- * NewSocketInfo --
- *
- * This function allocates and initializes a new SocketInfo
- * structure.
- *
- * Results:
- * Returns a newly allocated SocketInfo.
- *
- * Side effects:
- * Adds the socket to the global socket list, allocates memory.
- *
- *----------------------------------------------------------------------
- */
- static TcpState *
- NewSocketInfo(
- StreamPtr tcpStream)
- {
- TcpState *statePtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
- statePtr->tcpStream = tcpStream;
- statePtr->psn = applicationPSN;
- statePtr->flags = 0;
- statePtr->checkMask = 0;
- statePtr->watchMask = 0;
- statePtr->acceptProc = (Tcl_TcpAcceptProc *) NULL;
- statePtr->acceptProcData = (ClientData) NULL;
- statePtr->writeBuffer = (void *) NULL;
- statePtr->writeBufferSize = 0;
- statePtr->nextPtr = tsdPtr->socketList;
- tsdPtr->socketList = statePtr;
- return statePtr;
- }
- /*
- *----------------------------------------------------------------------
- *
- * FreeSocketInfo --
- *
- * This function deallocates a SocketInfo structure that is no
- * longer needed.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Removes the socket from the global socket list, frees memory.
- *
- *----------------------------------------------------------------------
- */
- static void
- FreeSocketInfo(
- TcpState *statePtr) /* The state pointer to free. */
- {
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- if (statePtr == tsdPtr->socketList) {
- tsdPtr->socketList = statePtr->nextPtr;
- } else {
- TcpState *p;
- for (p = tsdPtr->socketList; p != NULL; p = p->nextPtr) {
- if (p->nextPtr == statePtr) {
- p->nextPtr = statePtr->nextPtr;
- break;
- }
- }
- }
-
- if (statePtr->writeBuffer != (void *) NULL) {
- ckfree(statePtr->writeBuffer);
- }
-
- ckfree((char *) statePtr);
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_MakeTcpClientChannel --
- *
- * Creates a Tcl_Channel from an existing client TCP socket.
- *
- * Results:
- * The Tcl_Channel wrapped around the preexisting TCP socket.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- Tcl_Channel
- Tcl_MakeTcpClientChannel(
- ClientData sock) /* The socket to wrap up into a channel. */
- {
- TcpState *statePtr;
- char channelName[20];
- if (TclpHasSockets(NULL) != TCL_OK) {
- return NULL;
- }
-
- statePtr = NewSocketInfo((StreamPtr) sock);
- /* TODO: do we need to set the port??? */
-
- sprintf(channelName, "sock%d", socketNumber++);
-
- statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
- (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
- Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize);
- Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
- return statePtr->channel;
- }
- /*
- *----------------------------------------------------------------------
- *
- * CreateSocket --
- *
- * This function opens a new socket and initializes the
- * SocketInfo structure.
- *
- * Results:
- * Returns a new SocketInfo, or NULL with an error in interp.
- *
- * Side effects:
- * Adds a new socket to the socketList.
- *
- *----------------------------------------------------------------------
- */
- static TcpState *
- CreateSocket(
- Tcl_Interp *interp, /* For error reporting; can be NULL. */
- int port, /* Port number to open. */
- CONST char *host, /* Name of host on which to open port. */
- CONST char *myaddr, /* Optional client-side address */
- int myport, /* Optional client-side port */
- int server, /* 1 if socket should be a server socket,
- * else 0 for a client socket. */
- int async) /* 1 create async, 0 do sync. */
- {
- ip_addr macAddr;
- OSErr err;
- TCPiopb pb;
- StreamPtr tcpStream;
- TcpState *statePtr;
- char * buffer;
-
- /*
- * Figure out the ip address from the host string.
- */
- if (host == NULL) {
- err = GetLocalAddress(&macAddr);
- } else {
- err = GetHostFromString(host, &macAddr);
- }
- if (err != noErr) {
- Tcl_SetErrno(EHOSTUNREACH);
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "couldn't open socket: ",
- Tcl_PosixError(interp), (char *) NULL);
- }
- return (TcpState *) NULL;
- }
-
- /*
- * Create a MacTCP stream and create the state used for socket
- * transactions from here on out.
- */
- ClearZombieSockets();
- buffer = ckalloc(socketBufferSize);
- InitMacTCPParamBlock(&pb, TCPCreate);
- pb.csParam.create.rcvBuff = buffer;
- pb.csParam.create.rcvBuffLen = socketBufferSize;
- pb.csParam.create.notifyProc = nil /* notifyUPP */;
- err = PBControlSync((ParmBlkPtr) &pb);
- if (err != noErr) {
- Tcl_SetErrno(0); /* TODO: set to ENOSR - maybe?*/
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "couldn't open socket: ",
- Tcl_PosixError(interp), (char *) NULL);
- }
- return (TcpState *) NULL;
- }
- tcpStream = pb.tcpStream;
- statePtr = NewSocketInfo(tcpStream);
- statePtr->port = port;
-
- if (server) {
- /*
- * Set up server connection.
- */
- InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen);
- statePtr->pb.tcpStream = tcpStream;
- statePtr->pb.csParam.open.localPort = statePtr->port;
- statePtr->pb.ioCompletion = completeUPP;
- statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr;
- statePtr->pb.csParam.open.ulpTimeoutValue = 100;
- statePtr->pb.csParam.open.ulpTimeoutAction = 1 /* 1:abort 0:report */;
- statePtr->pb.csParam.open.commandTimeoutValue = 0 /* infinity */;
- statePtr->flags |= TCP_LISTENING;
- err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
- /*
- * If this is a server on port 0 then we need to wait until
- * the dynamic port allocation is made by the MacTcp driver.
- */
- if (statePtr->port == 0) {
- EventRecord dummy;
- while (statePtr->pb.csParam.open.localPort == 0) {
- WaitNextEvent(0, &dummy, 1, NULL);
- if (statePtr->pb.ioResult != 0) {
- break;
- }
- }
- statePtr->port = statePtr->pb.csParam.open.localPort;
- }
- Tcl_SetErrno(EINPROGRESS);
- } else {
- /*
- * Attempt to connect. The connect may fail at present with an
- * EINPROGRESS but at a later time it will complete. The caller
- * will set up a file handler on the socket if she is interested in
- * being informed when the connect completes.
- */
- InitMacTCPParamBlock(&statePtr->pb, TCPActiveOpen);
-
- statePtr->pb.tcpStream = tcpStream;
- statePtr->pb.csParam.open.remoteHost = macAddr;
- statePtr->pb.csParam.open.remotePort = port;
- statePtr->pb.csParam.open.localHost = 0;
- statePtr->pb.csParam.open.localPort = myport;
- statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr;
- statePtr->pb.csParam.open.validityFlags = timeoutValue | timeoutAction;
- statePtr->pb.csParam.open.ulpTimeoutValue = 60 /* seconds */;
- statePtr->pb.csParam.open.ulpTimeoutAction = 1 /* 1:abort 0:report */;
- statePtr->pb.csParam.open.commandTimeoutValue = 0;
- statePtr->pb.ioCompletion = completeUPP;
- if (async) {
- statePtr->flags |= TCP_ASYNC_CONNECT;
- err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
- Tcl_SetErrno(EINPROGRESS);
- } else {
- err = PBControlSync((ParmBlkPtr) &(statePtr->pb));
- }
- }
-
- switch (err) {
- case noErr:
- if (!async) {
- statePtr->flags |= TCP_CONNECTED;
- }
- return statePtr;
- case duplicateSocket:
- Tcl_SetErrno(EADDRINUSE);
- break;
- case openFailed:
- case connectionTerminated:
- Tcl_SetErrno(ECONNREFUSED);
- break;
- case invalidStreamPtr:
- case connectionExists:
- default:
- /*
- * These cases should never occur. However, we will fail
- * gracefully and hope Tcl can resume. The alternative is to panic
- * which is probably a bit drastic.
- */
- Debugger();
- Tcl_SetErrno(err);
- }
- /*
- * We had error during the connection. Release the stream
- * and file handle. Also report to the interp.
- */
- pb.ioCRefNum = driverRefNum;
- pb.csCode = TCPRelease;
- pb.tcpStream = tcpStream;
- pb.ioCompletion = NULL;
- err = PBControlSync((ParmBlkPtr) &pb);
- if (interp != (Tcl_Interp *) NULL) {
- Tcl_AppendResult(interp, "couldn't open socket: ",
- Tcl_PosixError(interp), (char *) NULL);
- }
- ckfree(buffer);
- FreeSocketInfo(statePtr);
- return (TcpState *) NULL;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_OpenTcpClient --
- *
- * Opens a TCP client socket and creates a channel around it.
- *
- * Results:
- * The channel or NULL if failed. On failure, the routine also
- * sets the output argument errorCodePtr to the error code.
- *
- * Side effects:
- * Opens a client socket and creates a new channel.
- *
- *----------------------------------------------------------------------
- */
- Tcl_Channel
- Tcl_OpenTcpClient(
- Tcl_Interp *interp, /* For error reporting; can be NULL. */
- int port, /* Port number to open. */
- CONST char *host, /* Host on which to open port. */
- CONST char *myaddr, /* Client-side address */
- int myport, /* Client-side port */
- int async) /* If nonzero, attempt to do an
- * asynchronous connect. Otherwise
- * we do a blocking connect.
- * - currently ignored */
- {
- TcpState *statePtr;
- char channelName[20];
- if (TclpHasSockets(interp) != TCL_OK) {
- return NULL;
- }
-
- /*
- * Create a new client socket and wrap it in a channel.
- */
- statePtr = CreateSocket(interp, port, host, myaddr, myport, 0, async);
- if (statePtr == NULL) {
- return NULL;
- }
-
- sprintf(channelName, "sock%d", socketNumber++);
- statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
- (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
- Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize);
- Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
- return statePtr->channel;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_OpenTcpServer --
- *
- * Opens a TCP server socket and creates a channel around it.
- *
- * Results:
- * The channel or NULL if failed.
- *
- * Side effects:
- * Opens a server socket and creates a new channel.
- *
- *----------------------------------------------------------------------
- */
- Tcl_Channel
- Tcl_OpenTcpServer(
- Tcl_Interp *interp, /* For error reporting - may be
- * NULL. */
- int port, /* Port number to open. */
- CONST char *host, /* Name of local host. */
- Tcl_TcpAcceptProc *acceptProc, /* Callback for accepting connections
- * from new clients. */
- ClientData acceptProcData) /* Data for the callback. */
- {
- TcpState *statePtr;
- char channelName[20];
- if (TclpHasSockets(interp) != TCL_OK) {
- return NULL;
- }
- /*
- * Create a new client socket and wrap it in a channel.
- */
- statePtr = CreateSocket(interp, port, host, NULL, 0, 1, 1);
- if (statePtr == NULL) {
- return NULL;
- }
- statePtr->acceptProc = acceptProc;
- statePtr->acceptProcData = acceptProcData;
- sprintf(channelName, "sock%d", socketNumber++);
- statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
- (ClientData) statePtr, 0);
- Tcl_SetChannelBufferSize(statePtr->channel, socketBufferSize);
- Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf");
- return statePtr->channel;
- }
- /*
- *----------------------------------------------------------------------
- *
- * SocketEventProc --
- *
- * This procedure is called by Tcl_ServiceEvent when a socket event
- * reaches the front of the event queue. This procedure is
- * responsible for notifying the generic channel code.
- *
- * Results:
- * Returns 1 if the event was handled, meaning it should be removed
- * from the queue. Returns 0 if the event was not handled, meaning
- * it should stay on the queue. The only time the event isn't
- * handled is if the TCL_FILE_EVENTS flag bit isn't set.
- *
- * Side effects:
- * Whatever the channel callback procedures do.
- *
- *----------------------------------------------------------------------
- */
- static int
- SocketEventProc(
- Tcl_Event *evPtr, /* Event to service. */
- int flags) /* Flags that indicate what events to
- * handle, such as TCL_FILE_EVENTS. */
- {
- TcpState *statePtr;
- SocketEvent *eventPtr = (SocketEvent *) evPtr;
- int mask = 0;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- if (!(flags & TCL_FILE_EVENTS)) {
- return 0;
- }
- /*
- * Find the specified socket on the socket list.
- */
- for (statePtr = tsdPtr->socketList; statePtr != NULL;
- statePtr = statePtr->nextPtr) {
- if ((statePtr == eventPtr->statePtr) &&
- (statePtr->tcpStream == eventPtr->tcpStream)) {
- break;
- }
- }
- /*
- * Discard events that have gone stale.
- */
- if (!statePtr) {
- return 1;
- }
- statePtr->flags &= ~(TCP_PENDING);
- if (statePtr->flags & TCP_RELEASE) {
- SocketFreeProc(statePtr);
- return 1;
- }
- /*
- * Handle connection requests directly.
- */
- if (statePtr->flags & TCP_LISTEN_CONNECT) {
- if (statePtr->checkMask & TCL_READABLE) {
- TcpAccept(statePtr);
- }
- return 1;
- }
- /*
- * Mask off unwanted events then notify the channel.
- */
- mask = statePtr->checkMask & statePtr->watchMask;
- if (mask) {
- Tcl_NotifyChannel(statePtr->channel, mask);
- }
- return 1;
- }
- /*
- *----------------------------------------------------------------------
- *
- * WaitForSocketEvent --
- *
- * Waits until one of the specified events occurs on a socket.
- *
- * Results:
- * Returns 1 on success or 0 on failure, with an error code in
- * errorCodePtr.
- *
- * Side effects:
- * Processes socket events off the system queue.
- *
- *----------------------------------------------------------------------
- */
- static int
- WaitForSocketEvent(
- TcpState *statePtr, /* Information about this socket. */
- int mask, /* Events to look for. */
- int *errorCodePtr) /* Where to store errors? */
- {
- OSErr err;
- TCPiopb statusPB;
- EventRecord dummy;
- /*
- * Loop until we get the specified condition, unless the socket is
- * asynchronous.
- */
-
- do {
- statusPB.ioCRefNum = driverRefNum;
- statusPB.tcpStream = statePtr->tcpStream;
- statusPB.csCode = TCPStatus;
- err = PBControlSync((ParmBlkPtr) &statusPB);
- if (err != noErr) {
- /*
- * I am not sure why it is right to return 1 - indicating success
- * for synchronous sockets when an attempt to get status on the
- * driver yeilds an error. But it is CERTAINLY wrong for async
- * sockect which have not yet connected.
- */
-
- if (statePtr->flags & TCP_ASYNC_CONNECT) {
- *errorCodePtr = EWOULDBLOCK;
- return 0;
- } else {
- statePtr->checkMask |= (TCL_READABLE | TCL_WRITABLE);
- return 1;
- }
- }
- statePtr->checkMask = 0;
-
- /*
- * The "6" below is the "connection being established" flag. I couldn't
- * find a define for this in MacTCP.h, but that's what the programmer's
- * guide says.
- */
-
- if ((statusPB.csParam.status.connectionState != 0)
- && (statusPB.csParam.status.connectionState != 4)
- && (statusPB.csParam.status.connectionState != 6)) {
- if (statusPB.csParam.status.amtUnreadData > 0) {
- statePtr->checkMask |= TCL_READABLE;
- }
- if (!(statePtr->flags & TCP_WRITING)
- && (statusPB.csParam.status.sendWindow -
- statusPB.csParam.status.amtUnackedData) > 0) {
- statePtr->flags &= ~(TCP_ASYNC_CONNECT);
- statePtr->checkMask |= TCL_WRITABLE;
- }
- if (mask & statePtr->checkMask) {
- return 1;
- }
- } else {
- break;
- }
-
- /*
- * Call the system to let other applications run while we
- * are waiting for this event to occur.
- */
-
- WaitNextEvent(0, &dummy, 1, NULL);
- } while (!(statePtr->flags & TCP_ASYNC_SOCKET));
- *errorCodePtr = EWOULDBLOCK;
- return 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TcpAccept --
- * Accept a TCP socket connection. This is called by the event
- * loop, and it in turns calls any registered callbacks for this
- * channel.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Evals the Tcl script associated with the server socket.
- *
- *----------------------------------------------------------------------
- */
- static void
- TcpAccept(
- TcpState *statePtr)
- {
- TcpState *newStatePtr;
- StreamPtr tcpStream;
- char remoteHostname[255];
- OSErr err;
- ip_addr remoteAddress;
- long remotePort;
- char channelName[20];
-
- statePtr->flags &= ~TCP_LISTEN_CONNECT;
- statePtr->checkMask &= ~TCL_READABLE;
- /*
- * Transfer sever stream to new connection.
- */
- tcpStream = statePtr->tcpStream;
- newStatePtr = NewSocketInfo(tcpStream);
- newStatePtr->tcpStream = tcpStream;
- sprintf(channelName, "sock%d", socketNumber++);
- newStatePtr->flags |= TCP_CONNECTED;
- newStatePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
- (ClientData) newStatePtr, (TCL_READABLE | TCL_WRITABLE));
- Tcl_SetChannelBufferSize(newStatePtr->channel, socketBufferSize);
- Tcl_SetChannelOption(NULL, newStatePtr->channel, "-translation",
- "auto crlf");
- remoteAddress = statePtr->pb.csParam.open.remoteHost;
- remotePort = statePtr->pb.csParam.open.remotePort;
- /*
- * Reopen passive connect. Make new tcpStream the server.
- */
- ClearZombieSockets();
- InitMacTCPParamBlock(&statePtr->pb, TCPCreate);
- statePtr->pb.csParam.create.rcvBuff = ckalloc(socketBufferSize);
- statePtr->pb.csParam.create.rcvBuffLen = socketBufferSize;
- err = PBControlSync((ParmBlkPtr) &statePtr->pb);
- if (err != noErr) {
- /*
- * Hmmm... We can't reopen the server. We'll go ahead
- * an continue - but we are kind of broken now...
- */
- Debugger();
- statePtr->tcpStream = -1;
- statePtr->flags |= TCP_SERVER_ZOMBIE;
- }
- tcpStream = statePtr->tcpStream = statePtr->pb.tcpStream;
-
- InitMacTCPParamBlock(&statePtr->pb, TCPPassiveOpen);
- statePtr->pb.tcpStream = tcpStream;
- statePtr->pb.csParam.open.localHost = 0;
- statePtr->pb.csParam.open.localPort = statePtr->port;
- statePtr->pb.ioCompletion = completeUPP;
- statePtr->pb.csParam.open.userDataPtr = (Ptr) statePtr;
- statePtr->flags |= TCP_LISTENING;
- err = PBControlAsync((ParmBlkPtr) &(statePtr->pb));
- /*
- * TODO: deal with case where we can't recreate server socket...
- */
- /*
- * Finally we run the accept procedure. We must do this last to make
- * sure we are in a nice clean state. This Tcl code can do anything
- * including closing the server or client sockets we've just delt with.
- */
- if (statePtr->acceptProc != NULL) {
- sprintf(remoteHostname, "%d.%d.%d.%d", remoteAddress>>24,
- remoteAddress>>16 & 0xff, remoteAddress>>8 & 0xff,
- remoteAddress & 0xff);
-
- (statePtr->acceptProc)(statePtr->acceptProcData, newStatePtr->channel,
- remoteHostname, remotePort);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_GetHostName --
- *
- * Returns the name of the local host.
- *
- * Results:
- * A string containing the network name for this machine, or
- * an empty string if we can't figure out the name. The caller
- * must not modify or free this string.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- CONST char *
- Tcl_GetHostName()
- {
- static int hostnameInited = 0;
- static char hostname[255];
- ip_addr ourAddress;
- Tcl_DString dString;
- OSErr err;
-
- if (hostnameInited) {
- return hostname;
- }
-
- if (TclpHasSockets(NULL) == TCL_OK) {
- err = GetLocalAddress(&ourAddress);
- if (err == noErr) {
- /*
- * Search for the doman name and return it if found. Otherwise,
- * just print the IP number to a string and return that.
- */
- Tcl_DStringInit(&dString);
- err = ResolveAddress(ourAddress, &dString);
- if (err == noErr) {
- strcpy(hostname, dString.string);
- } else {
- sprintf(hostname, "%d.%d.%d.%d", ourAddress>>24, ourAddress>>16 & 0xff,
- ourAddress>>8 & 0xff, ourAddress & 0xff);
- }
- Tcl_DStringFree(&dString);
-
- hostnameInited = 1;
- return hostname;
- }
- }
- hostname[0] = ' ';
- hostnameInited = 1;
- return hostname;
- }
- /*
- *----------------------------------------------------------------------
- *
- * ResolveAddress --
- *
- * This function is used to resolve an ip address to it's full
- * domain name address.
- *
- * Results:
- * An os err value.
- *
- * Side effects:
- * Treats client data as int we set to true.
- *
- *----------------------------------------------------------------------
- */
- static OSErr
- ResolveAddress(
- ip_addr tcpAddress, /* Address to resolve. */
- Tcl_DString *dsPtr) /* Returned address in string. */
- {
- int i;
- EventRecord dummy;
- DNRState dnrState;
- OSErr err;
- /*
- * Call AddrToName to resolve our ip address to our domain name.
- * The call is async, so we must wait for a callback to tell us
- * when to continue.
- */
- for (i = 0; i < NUM_ALT_ADDRS; i++) {
- dnrState.hostInfo.addr[i] = 0;
- }
- dnrState.done = 0;
- GetCurrentProcess(&(dnrState.psn));
- err = AddrToName(tcpAddress, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
- if (err == cacheFault) {
- while (!dnrState.done) {
- WaitNextEvent(0, &dummy, 1, NULL);
- }
- }
-
- /*
- * If there is no error in finding the domain name we set the
- * result into the dynamic string. We also work around a bug in
- * MacTcp where an extranious '.' may be found at the end of the name.
- */
- if (dnrState.hostInfo.rtnCode == noErr) {
- i = strlen(dnrState.hostInfo.cname) - 1;
- if (dnrState.hostInfo.cname[i] == '.') {
- dnrState.hostInfo.cname[i] = ' ';
- }
- Tcl_DStringAppend(dsPtr, dnrState.hostInfo.cname, -1);
- }
-
- return dnrState.hostInfo.rtnCode;
- }
- /*
- *----------------------------------------------------------------------
- *
- * DNRCompletionRoutine --
- *
- * This function is called when the Domain Name Server is done
- * seviceing our request. It just sets a flag that we can poll
- * in functions like Tcl_GetHostName to let them know to continue.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Treats client data as int we set to true.
- *
- *----------------------------------------------------------------------
- */
- static pascal void
- DNRCompletionRoutine(
- struct hostInfo *hostinfoPtr, /* Host infor struct. */
- DNRState *dnrStatePtr) /* Completetion state. */
- {
- dnrStatePtr->done = true;
- WakeUpProcess(&(dnrStatePtr->psn));
- }
- /*
- *----------------------------------------------------------------------
- *
- * CleanUpExitProc --
- *
- * This procedure is invoked as an exit handler when ExitToShell
- * is called. It aborts any lingering socket connections. This
- * must be called or the Mac OS will more than likely crash.
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static pascal void
- CleanUpExitProc()
- {
- TCPiopb exitPB;
- TcpState *statePtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- while (tsdPtr->socketList != NULL) {
- statePtr = tsdPtr->socketList;
- tsdPtr->socketList = statePtr->nextPtr;
- /*
- * Close and Release the connection.
- */
- exitPB.ioCRefNum = driverRefNum;
- exitPB.csCode = TCPClose;
- exitPB.tcpStream = statePtr->tcpStream;
- exitPB.csParam.close.ulpTimeoutValue = 60 /* seconds */;
- exitPB.csParam.close.ulpTimeoutAction = 1 /* 1:abort 0:report */;
- exitPB.csParam.close.validityFlags = timeoutValue | timeoutAction;
- exitPB.ioCompletion = NULL;
- PBControlSync((ParmBlkPtr) &exitPB);
- exitPB.ioCRefNum = driverRefNum;
- exitPB.csCode = TCPRelease;
- exitPB.tcpStream = statePtr->tcpStream;
- exitPB.ioCompletion = NULL;
- PBControlSync((ParmBlkPtr) &exitPB);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * GetHostFromString --
- *
- * Looks up the passed in domain name in the domain resolver. It
- * can accept strings of two types: 1) the ip number in string
- * format, or 2) the domain name.
- *
- * Results:
- * We return a ip address or 0 if there was an error or the
- * domain does not exist.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static OSErr
- GetHostFromString(
- CONST char *name, /* Host in string form. */
- ip_addr *address) /* Returned IP address. */
- {
- OSErr err;
- int i;
- EventRecord dummy;
- DNRState dnrState;
-
- if (TclpHasSockets(NULL) != TCL_OK) {
- return 0;
- }
- /*
- * Call StrToAddr to get the ip number for the passed in domain
- * name. The call is async, so we must wait for a callback to
- * tell us when to continue.
- */
- for (i = 0; i < NUM_ALT_ADDRS; i++) {
- dnrState.hostInfo.addr[i] = 0;
- }
- dnrState.done = 0;
- GetCurrentProcess(&(dnrState.psn));
- err = StrToAddr((char*)name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
- if (err == cacheFault) {
- while (!dnrState.done) {
- WaitNextEvent(0, &dummy, 1, NULL);
- }
- }
-
- /*
- * For some reason MacTcp may return a cachFault a second time via
- * the hostinfo block. This seems to be a bug in MacTcp. In this case
- * we run StrToAddr again - which seems to then work just fine.
- */
- if (dnrState.hostInfo.rtnCode == cacheFault) {
- dnrState.done = 0;
- err = StrToAddr((char*)name, &dnrState.hostInfo, resultUPP, (Ptr) &dnrState);
- if (err == cacheFault) {
- while (!dnrState.done) {
- WaitNextEvent(0, &dummy, 1, NULL);
- }
- }
- }
- if (dnrState.hostInfo.rtnCode == noErr) {
- *address = dnrState.hostInfo.addr[0];
- }
-
- return dnrState.hostInfo.rtnCode;
- }
- /*
- *----------------------------------------------------------------------
- *
- * IOCompletionRoutine --
- *
- * This function is called when an asynchronous socket operation
- * completes. Since this routine runs as an interrupt handler,
- * it will simply set state to tell the notifier that this socket
- * is now ready for action. Note that this function is running at
- * interupt time and can't allocate memory or do much else except
- * set state.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Sets some state in the socket state. May also wake the process
- * if we are not currently running.
- *
- *----------------------------------------------------------------------
- */
- static void
- IOCompletionRoutine(
- TCPiopb *pbPtr) /* Tcp parameter block. */
- {
- TcpState *statePtr;
-
- if (pbPtr->csCode == TCPSend) {
- statePtr = (TcpState *) pbPtr->csParam.send.userDataPtr;
- } else {
- statePtr = (TcpState *) pbPtr->csParam.open.userDataPtr;
- }
-
- /*
- * Always wake the process in case it's in WaitNextEvent.
- * If an error has a occured - just return. We will deal
- * with the problem later.
- */
- WakeUpProcess(&statePtr->psn);
- if (pbPtr->ioResult != noErr) {
- return;
- }
-
- if (statePtr->flags & TCP_ASYNC_CONNECT) {
- statePtr->flags &= ~TCP_ASYNC_CONNECT;
- statePtr->flags |= TCP_CONNECTED;
- statePtr->checkMask |= TCL_READABLE & TCL_WRITABLE;
- } else if (statePtr->flags & TCP_LISTENING) {
- if (statePtr->port == 0) {
- Debugger();
- }
- statePtr->flags &= ~TCP_LISTENING;
- statePtr->flags |= TCP_LISTEN_CONNECT;
- statePtr->checkMask |= TCL_READABLE;
- } else if (statePtr->flags & TCP_WRITING) {
- statePtr->flags &= ~TCP_WRITING;
- statePtr->checkMask |= TCL_WRITABLE;
- if (!(statePtr->flags & TCP_CONNECTED)) {
- InitMacTCPParamBlock(&statePtr->pb, TCPClose);
- statePtr->pb.tcpStream = statePtr->tcpStream;
- statePtr->pb.ioCompletion = closeUPP;
- statePtr->pb.csParam.close.userDataPtr = (Ptr) statePtr;
- if (PBControlAsync((ParmBlkPtr) &statePtr->pb) != noErr) {
- statePtr->flags |= TCP_RELEASE;
- }
- }
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * GetLocalAddress --
- *
- * Get the IP address for this machine. The result is cached so
- * the result is returned quickly after the first call.
- *
- * Results:
- * Macintosh error code.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static OSErr
- GetLocalAddress(
- unsigned long *addr) /* Returns host IP address. */
- {
- struct GetAddrParamBlock pBlock;
- OSErr err = noErr;
- static unsigned long localAddress = 0;
- if (localAddress == 0) {
- memset(&pBlock, 0, sizeof(pBlock));
- pBlock.ioResult = 1;
- pBlock.csCode = ipctlGetAddr;
- pBlock.ioCRefNum = driverRefNum;
- err = PBControlSync((ParmBlkPtr) &pBlock);
- if (err != noErr) {
- return err;
- }
- localAddress = pBlock.ourAddress;
- }
-
- *addr = localAddress;
- return noErr;
- }
- /*
- *----------------------------------------------------------------------
- *
- * GetBufferSize --
- *
- * Get the appropiate buffer size for our machine & network. This
- * value will be used by the rest of Tcl & the MacTcp driver for
- * the size of its buffers. If out method for determining the
- * optimal buffer size fails for any reason - we return a
- * reasonable default.
- *
- * Results:
- * Size of optimal buffer in bytes.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static long
- GetBufferSize()
- {
- UDPiopb iopb;
- OSErr err = noErr;
- long bufferSize;
-
- memset(&iopb, 0, sizeof(iopb));
- err = GetLocalAddress(&iopb.csParam.mtu.remoteHost);
- if (err != noErr) {
- return CHANNEL_BUF_SIZE;
- }
- iopb.ioCRefNum = driverRefNum;
- iopb.csCode = UDPMaxMTUSize;
- err = PBControlSync((ParmBlkPtr)&iopb);
- if (err != noErr) {
- return CHANNEL_BUF_SIZE;
- }
- bufferSize = (iopb.csParam.mtu.mtuSize * 4) + 1024;
- if (bufferSize < CHANNEL_BUF_SIZE) {
- bufferSize = CHANNEL_BUF_SIZE;
- }
- return bufferSize;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TclSockGetPort --
- *
- * Maps from a string, which could be a service name, to a port.
- * Used by socket creation code to get port numbers and resolve
- * registered service names to port numbers.
- *
- * Results:
- * A standard Tcl result. On success, the port number is
- * returned in portPtr. On failure, an error message is left in
- * the interp's result.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- TclSockGetPort(
- Tcl_Interp *interp, /* Interp for error messages. */
- char *string, /* Integer or service name */
- char *proto, /* "tcp" or "udp", typically -
- * ignored on Mac - assumed to be tcp */
- int *portPtr) /* Return port number */
- {
- PortInfo *portInfoPtr = NULL;
-
- if (Tcl_GetInt(interp, string, portPtr) == TCL_OK) {
- if (*portPtr > 0xFFFF) {
- Tcl_AppendResult(interp, "couldn't open socket: port number too high",
- (char *) NULL);
- return TCL_ERROR;
- }
- if (*portPtr < 0) {
- Tcl_AppendResult(interp, "couldn't open socket: negative port number",
- (char *) NULL);
- return TCL_ERROR;
- }
- return TCL_OK;
- }
- for (portInfoPtr = portServices; portInfoPtr->name != NULL; portInfoPtr++) {
- if (!strcmp(portInfoPtr->name, string)) {
- break;
- }
- }
- if (portInfoPtr != NULL && portInfoPtr->name != NULL) {
- *portPtr = portInfoPtr->port;
- Tcl_ResetResult(interp);
- return TCL_OK;
- }
-
- return TCL_ERROR;
- }
- /*
- *----------------------------------------------------------------------
- *
- * ClearZombieSockets --
- *
- * This procedure looks through the socket list and removes the
- * first stream it finds that is ready for release. This procedure
- * should be called before we ever try to create new Tcp streams
- * to ensure we can least allocate one stream.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Tcp streams may be released.
- *
- *----------------------------------------------------------------------
- */
- static void
- ClearZombieSockets()
- {
- TcpState *statePtr;
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- for (statePtr = tsdPtr->socketList; statePtr != NULL;
- statePtr = statePtr->nextPtr) {
- if (statePtr->flags & TCP_RELEASE) {
- SocketFreeProc(statePtr);
- return;
- }
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * NotifyRoutine --
- *
- * This routine does nothing currently, and is not being used. But
- * it is useful if you want to experiment with what MacTCP thinks that
- * it is doing...
- *
- * Results:
- * None.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- pascal void NotifyRoutine (
- StreamPtr tcpStream,
- unsigned short eventCode,
- Ptr userDataPtr,
- unsigned short terminReason,
- struct ICMPReport *icmpMsg)
- {
- StreamPtr localTcpStream;
- unsigned short localEventCode;
- unsigned short localTerminReason;
- struct ICMPReport localIcmpMsg;
- localTcpStream = tcpStream;
- localEventCode = eventCode;
- localTerminReason = terminReason;
- localIcmpMsg = *icmpMsg;
-
- }