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