OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [mac/] [tclMacSock.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.