Back to home page

MITgcm

 
 

    


File indexing completed on 2018-03-02 18:44:55 UTC

view on githubraw file Latest commit ec6cf3b0 on 2003-08-26 20:45:25 UTC
ec6cf3b09d Ed H*0001 /* macntcp.c -- macintosh nifty application library async TCP routines
                0002  */
                0003 /* (C) Copyright 1995 by Carnegie Mellon University
                0004  * All Rights Reserved.
                0005  *
                0006  * Permission to use, copy, modify, distribute, and sell this software
                0007  * and its documentation for any purpose is hereby granted without
                0008  * fee, provided that the above copyright notice appear in all copies
                0009  * and that both that copyright notice and this permission notice
                0010  * appear in supporting documentation, and that the name of Carnegie
                0011  * Mellon University not be used in advertising or publicity
                0012  * pertaining to distribution of the software without specific,
                0013  * written prior permission.  Carnegie Mellon University makes no
                0014  * representations about the suitability of this software for any
                0015  * purpose.  It is provided "as is" without express or implied
                0016  * warranty.
                0017  *
                0018  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
                0019  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
                0020  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
                0021  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                0022  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
                0023  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
                0024  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
                0025  * SOFTWARE.
                0026  */
                0027 /* (C) Copyright 1994-1995 by Christopher J. Newman
                0028  * All Rights Reserved.
                0029  *
                0030  * Permission to use, copy, modify, distribute, and sell this software and its
                0031  * documentation for any purpose is hereby granted without fee, provided that
                0032  * the above copyright notice appear in all copies and that both that
                0033  * copyright notice and this permission notice appear in supporting
                0034  * documentation, and that the name of Christopher J. Newman not be used in
                0035  * advertising or publicity pertaining to distribution of the software without
                0036  * specific, written prior permission.  Christopher J. Newman makes no
                0037  * representations about the suitability of this software for any purpose.  It
                0038  * is provided "as is" without express or implied warranty.
                0039  *
                0040  * CHRISTOPHER J. NEWMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
                0041  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
                0042  * SHALL CHRISTOPHER J. NEWMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
                0043  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
                0044  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
                0045  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
                0046  * OF THIS SOFTWARE.
                0047  *
                0048  * Author:  Christopher J. Newman
                0049  * Message: This is a nifty program.
                0050  */
                0051 
                0052 #include <string.h>
                0053 #include <MacTCPCommonTypes.h>
                0054 #include <TCPPB.h>
                0055 #include <AddressXlation.h>
                0056 #include <GetMyIPAddr.h>
                0057 #include "macnapp.h"
                0058 
                0059 #define DEF_STREAM_SIZE 8192
                0060 #define MAX_TCPCON  16
                0061 #define RDS         8
                0062 
                0063 /* write buffer for TCP writes
                0064  */
                0065 typedef struct tcpwb {
                0066     short rused;            /* number of RDS used (-1 == in TCPsend) */
                0067     short wused;            /* amount of small buffer used */
                0068     rdsEntry rds[RDS+1];    /* array of RDS pointers */
                0069     char fflag[RDS+1];      /* free flags for RDS pointers (1 = call DisposPtr) */
                0070     char *buf;              /* write buffer */
                0071 } tcpwb;
                0072 
                0073 /* structure describing a TCP connection
                0074  */
                0075 typedef struct tcpinfo {
                0076     short state;            /* current state */
                0077     short rclose;           /* remote host wants to close */
                0078     short lclose;           /* local host wants to close */
                0079     int havedata:1;         /* data is available to read */
                0080     int urgent:1;           /* TCP in urgent mode */
                0081     int push:1;             /* next write should be pushed */
                0082     int pushed:1;           /* last write was pushed */
                0083     int reading:1;          /* reading data */
                0084     int server:1;           /* is a server, rather than client */
                0085     int gethost:1;          /* getting hostname -- not a real connection */
                0086     int dnrdone:1;          /* DNR query is done */
                0087     short wbnum;            /* write buffer for next write */
                0088     unsigned short wbsize;  /* size of write buffer */
                0089     unsigned short reason;  /* reason for TCP termination */
                0090     StreamPtr stream;       /* TCP stream */
                0091     ip_port port;           /* TCP port number to connect to */
                0092     void *context;          /* user context */
                0093     na_tcpreadp *callback;  /* callback function */
                0094     rdsEntry rrds[RDS+1];   /* read RDS structure */
                0095     tcpwb wb[2];            /* write buffers */
                0096     struct hostInfo host;   /* hostname & ip_addr of host */
                0097     TCPiopb pb;             /* parameter block for TCP connect/write/close */
                0098     TCPiopb rpb;            /* parameter block for TCP read */
                0099     char buf[1];            /* stream buffer follows */
                0100 } tcpinfo;
                0101 
                0102 /* TCP task state information
                0103  */
                0104 struct tcpstate {
                0105     na_win win;
                0106     short tcp_driver;
                0107     na_tcpinitp *tcp_initp;
                0108     tcpinfo *tcpbufs[MAX_TCPCON];
                0109     IOParam *open_pb;
                0110     long waiticks, waitstart;
                0111     short waitpercent;
                0112     long streamsize;
                0113     byte TOS, precedence;
                0114     unsigned short wbsize;
                0115     char localhost[256];
                0116 } **tcpstate = NULL;
                0117 #define tstate ((struct tcpstate *) win)
                0118 
                0119 /* tcp state bitmasks */
                0120 #define TCP_PBINUSE   0x04      /* tcp->pb in use */
                0121 #define TCP_DNSINUSE  0x08      /* DNS in use */
                0122 #define TCP_NOTREADY  0x10      /* not ready for reading */
                0123 /* tcp states */
                0124 #define TCP_READY     1                                 /* inactive */
                0125 #define TCP_RESOLVE   (TCP_NOTREADY + TCP_DNSINUSE + 0) /* resolving hostname */
                0126 #define TCP_GETHOST   (TCP_NOTREADY + TCP_DNSINUSE + 1) /* looking up hostname */
                0127 #define TCP_WRITING   (TCP_PBINUSE + 0)                 /* writing data */
                0128 #define TCP_CONNECT   (TCP_NOTREADY + TCP_PBINUSE + 0)  /* waiting for a connection */
                0129 #define TCP_CLOSING   (TCP_NOTREADY + TCP_PBINUSE + 1)  /* waiting for TCPclose */
                0130 #define TCP_CLOSED    2                                 /* closed */
                0131 
                0132 /* free write buffer storage
                0133  */
                0134 static void freewb(tcpwb *wb)
                0135 {
                0136     short i;
                0137     
                0138     for (i = 0; wb->rds[i].length; ++i) {
                0139         if (wb->fflag[i]) {
                0140             DisposPtr(wb->rds[i].ptr);
                0141             wb->fflag[i] = 0;
                0142         }
                0143     }
                0144     wb->rused = 0;
                0145     wb->wused = 0;
                0146 }
                0147 
                0148 /* make sure tcp_driver is open
                0149  */
                0150 static short tcp_checkdriver()
                0151 {
                0152     short msg = NATCP_nodriver;
                0153     struct tcpstate *ts = *tcpstate;
                0154     IOParam *opb = ts->open_pb;
                0155     
                0156     if (!opb) return (1);
                0157     if (opb->ioResult == 1) return (0);
                0158     if (opb->ioResult == noErr && OpenResolver(nil) == noErr) {
                0159         ts->tcp_driver = opb->ioRefNum;
                0160         msg = NATCP_connect;
                0161     }
                0162     DisposPtr((Ptr) opb);
                0163     ts = *tcpstate;
                0164     ts->open_pb = NULL;
                0165     (*ts->tcp_initp)(msg);
                0166     
                0167     return (1);
                0168 }
                0169 
                0170 /* wait for MacTCP to finish whatever it's doing, with user cancel
                0171  */
                0172 static short tcp_wait(struct tcpstate *ts, tcpinfo *tcp)
                0173 {
                0174     KeyMap mapkeys;
                0175 #define keys ((unsigned char *)mapkeys)
                0176     short percent;
                0177 
                0178     while (!tcp_checkdriver()
                0179         || (tcp && (tcp->state & TCP_DNSINUSE) && ! (volatile) tcp->dnrdone)
                0180         || (tcp && (tcp->state & TCP_PBINUSE) && (volatile short) tcp->pb.ioResult == 1)) {
                0181         if (ts) {
                0182             if (!ts->waiticks) return (0);
                0183             percent = ((TickCount() - ts->waitstart) * 100) / ts->waiticks;
                0184             if (percent > 100) percent = 100;
                0185             if (percent != ts->waitpercent) {
                0186                 (*ts->tcp_initp)(ts->waitpercent = percent);
                0187             }
                0188             if (percent == 100) return (0);
                0189         }
                0190         SystemTask();
                0191         GetKeys(mapkeys);
                0192         if ((keys[0x37 >> 3] >> (0x37 & 7))
                0193             & (keys[0x2f >> 3] >> (0x2f & 7)) & 1) {
                0194             return (0);
                0195         }
                0196     }
                0197     
                0198     return (1);
                0199 }
                0200 
                0201 /* clean up the TCP task
                0202  */
                0203 static short tcp_closep(na_win *win)
                0204 {
                0205     short i, j;
                0206     tcpinfo *tcp;
                0207     tcpwb *wb;
                0208     TCPiopb pb;
                0209     
                0210     tstate->waitstart = TickCount();
                0211     tstate->waitpercent = 0;
                0212     tcp_wait(tstate, NULL);
                0213     memset((void *) &pb, 0, sizeof (pb));
                0214     for (i = 0; i < MAX_TCPCON; ++i) {
                0215         if ((tcp = tstate->tcpbufs[i]) != NULL) {
                0216             /* wait for MacTCP to finish what it's doing, but permit user cancel */
                0217             if (!tcp->server || tcp->state != TCP_CONNECT) tcp_wait(tstate, tcp);
                0218             if (!tcp->gethost) {
                0219                 pb.ioCRefNum = tstate->tcp_driver;
                0220                 pb.tcpStream = tcp->stream;
                0221                 pb.csCode = TCPRelease;
                0222                 PBControl((ParmBlkPtr) &pb, false);
                0223             }
                0224             freewb(tcp->wb);
                0225             freewb(tcp->wb + 1);
                0226             DisposPtr((Ptr) tcp);
                0227             tstate->tcpbufs[i] = NULL;
                0228         }
                0229     }
                0230     tcpstate = NULL;
                0231     if (tstate->tcp_driver) CloseResolver();
                0232     
                0233     return (NA_CLOSED);
                0234 }
                0235 
                0236 /* begin writing data
                0237  */
                0238 static short beginwrite(tcpinfo *tcp)
                0239 {
                0240     tcpwb *wb = tcp->wb + tcp->wbnum;
                0241     
                0242     /* if connection terminated, or we've sent a TCPclose, we can't write */
                0243     if (tcp->rclose == 3 || tcp->lclose > 1) return (0);
                0244     memset((void *) &tcp->pb.csParam, 0, sizeof (tcp->pb.csParam));
                0245     tcp->pb.csCode = TCPSend;
                0246     tcp->pb.csParam.send.ulpTimeoutValue = 60; /* 1 minute to send data */
                0247     tcp->pb.csParam.send.ulpTimeoutAction = 0;
                0248     tcp->pb.csParam.send.validityFlags = 0xC0;
                0249     tcp->pb.csParam.send.wdsPtr = (Ptr) wb->rds;
                0250     tcp->pb.csParam.send.pushFlag = tcp->pushed = tcp->push;
                0251     PBControl((ParmBlkPtr) &tcp->pb, true);
                0252     tcp->push = 0;
                0253     tcp->wbnum = 1 - tcp->wbnum;
                0254     wb->rused = -1;
                0255     
                0256     return (tcp->state = TCP_WRITING);
                0257 }
                0258 
                0259 /* do I/O polling
                0260  */
                0261 short NATCPtask(na_win *win)
                0262 {
                0263     tcpinfo *tcp;
                0264     rdsEntry *rds, *trds;
                0265     short result, newstate;
                0266     short processed = NA_PROCESSED;
                0267     na_tcp i;
                0268     tcpwb *wb;
                0269     int j;
                0270     
                0271     /* finish off driver initialization: */
                0272     if (!tstate->tcp_driver) {
                0273         if (!tcp_checkdriver()) return (NA_NOTPROCESSED);
                0274         if (!tstate->tcp_driver) return (NA_REQCLOSE);
                0275     }
                0276     /* loop through connections */
                0277     for (i = 0; i < MAX_TCPCON; ++i) {
                0278         if ((tcp = tstate->tcpbufs[i]) != NULL) do {
                0279             /* read data if we have it */
                0280             if (!tcp->reading && !tcp->rclose && tcp->havedata && tcp->state != TCP_CONNECT) {
                0281                 tcp->rpb.ioCRefNum = tstate->tcp_driver;
                0282                 tcp->rpb.tcpStream = tcp->stream;
                0283                 tcp->rpb.csCode = TCPNoCopyRcv;
                0284                 tcp->rpb.csParam.receive.rdsPtr = (Ptr) tcp->rrds;
                0285                 tcp->rpb.csParam.receive.commandTimeoutValue = 5;
                0286                 tcp->rpb.csParam.receive.rdsLength = RDS;
                0287                 if (tcp->pushed) {
                0288                     tcp->rpb.csParam.receive.commandTimeoutValue = 1;
                0289                     tcp->rpb.csParam.receive.rdsLength = 1;
                0290                 }
                0291                 tcp->havedata = 0;
                0292                 PBControl((ParmBlkPtr) &tcp->rpb, tcp->pushed ? false : true);
                0293                 tcp->reading = 1;
                0294                 tcp->pushed = 0;
                0295             }
                0296             if (tcp->reading) {
                0297                 if ((result = tcp->rpb.ioResult) == 1) {
                0298                     processed = NA_NOTPROCESSED;
                0299                 } else {
                0300                     tcp->reading = 0;
                0301                     if (result != noErr) {
                0302                         if (result != commandTimeout) {
                0303                             (*tcp->callback)(tcp->context, i, NATCP_noread, result, NULL);
                0304                         }
                0305                     } else {
                0306                         result = NATCP_data | NATCP_more;
                0307                         if (tcp->rpb.csParam.receive.urgentFlag) tcp->urgent = 1;
                0308                         if (tcp->urgent) result |= NATCP_urgent;
                0309                         if (tcp->rpb.csParam.receive.markFlag) tcp->urgent = 0;
                0310                         for (rds = tcp->rrds; rds->length; ++rds) {
                0311                             if (!rds[1].length) result &= ~NATCP_more;
                0312                             (*tcp->callback)(tcp->context, i, result,
                0313                                 rds->length, rds->ptr);
                0314                         }
                0315                         tcp->rpb.csCode = TCPRcvBfrReturn;
                0316                         PBControl((ParmBlkPtr) &tcp->rpb, false);
                0317                     }
                0318                 }
                0319             }
                0320             result = tcp->pb.ioResult;
                0321             newstate = 0;
                0322             switch (tcp->state) {
                0323                 case TCP_GETHOST:
                0324                     if (tcp->dnrdone) {
                0325                         tcp->rclose = 3;
                0326                         newstate = TCP_CLOSED;
                0327                         if (tcp->host.rtnCode != noErr) {
                0328                             (*tcp->callback)(tcp->context, i, NATCP_nohost,
                0329                                 tcp->host.rtnCode, NULL);
                0330                         } else {
                0331                             (*tcp->callback)(tcp->context, i, NATCP_connect,
                0332                                 strlen(tcp->host.cname), tcp->host.cname);
                0333                             strcpy(tstate->localhost, tcp->host.cname);
                0334                         }
                0335                     }
                0336                     break;
                0337                 case TCP_RESOLVE:
                0338                     if (tcp->dnrdone) {
                0339                         if (tcp->host.rtnCode != noErr) {
                0340                             tcp->rclose = 3;
                0341                             newstate = TCP_CLOSED;
                0342                             (*tcp->callback)(tcp->context, i, NATCP_nohost,
                0343                                 tcp->host.rtnCode, NULL);
                0344                         } else if (!tcp->lclose) {
                0345                             memset((void *) &tcp->pb, 0, sizeof (tcp->pb));
                0346                             tcp->pb.ioCRefNum = tstate->tcp_driver;
                0347                             tcp->pb.tcpStream = tcp->stream;
                0348                             tcp->pb.csParam.open.ulpTimeoutValue = 30;
                0349                             tcp->pb.csParam.open.ulpTimeoutAction = 1; /* Abort on timeout */
                0350                             tcp->pb.csParam.open.tosFlags = tstate->TOS;
                0351                             tcp->pb.csParam.open.precedence = tstate->precedence;
                0352                             tcp->pb.csParam.open.validityFlags = 
                0353                                 timeoutValue|timeoutAction|typeOfService|precedence;
                0354                             tcp->pb.csParam.open.remoteHost = tcp->host.addr[0];
                0355                             if (tcp->server) {
                0356                                 tcp->pb.csCode = TCPPassiveOpen;
                0357                                 tcp->pb.csParam.open.commandTimeoutValue = 0;
                0358                                 tcp->pb.csParam.open.remotePort = 0;
                0359                                 tcp->pb.csParam.open.localPort = tcp->port;
                0360                             } else {
                0361                                 tcp->pb.csCode = TCPActiveOpen;
                0362                                 tcp->pb.csParam.open.remotePort = tcp->port;
                0363                                 tcp->pb.csParam.open.localPort = 0;
                0364                             }
                0365                             PBControl((ParmBlkPtr) &tcp->pb, true);
                0366                             newstate = TCP_CONNECT;
                0367                         }
                0368                     }
                0369                     break;
                0370                 case TCP_CONNECT:
                0371                     if (result == 1) {
                0372                         processed = NA_NOTPROCESSED;
                0373                         break;
                0374                     }
                0375                     if (result != noErr) {
                0376                         tcp->rclose = 3;
                0377                         newstate = TCP_CLOSED;
                0378                         (*tcp->callback)(tcp->context, i, NATCP_nocon, result, NULL);
                0379                     } else {
                0380                         newstate = TCP_READY;
                0381                         if (tcp->server) {
                0382                             tcp->port = tcp->pb.csParam.open.remotePort;
                0383                             if (!*tcp->host.cname) {
                0384                                 AddrToStr(tcp->pb.csParam.open.remoteHost, tcp->host.cname);
                0385                             }
                0386                         }
                0387                         (*tcp->callback)(tcp->context, i, NATCP_connect,
                0388                             tcp->port, tcp->host.cname);
                0389                     }
                0390                     break;
                0391                 case TCP_READY:
                0392                     /* Write data if we have it */
                0393                     wb = tcp->wb + tcp->wbnum;
                0394                     if (wb->rused && (newstate = beginwrite(tcp))) {
                0395                         break;
                0396                     }
                0397                     /* check if other side wants to close */
                0398                     if (tcp->rclose == 1) {
                0399                         tcp->rclose = 2;
                0400                         (*tcp->callback)(tcp->context, i, NATCP_closing, 0, NULL);
                0401                     }
                0402                     /* check if connection needs closing at this end */
                0403                     if (tcp->lclose == 1) {
                0404                         tcp->lclose = 2;
                0405                         tcp->pb.csCode = TCPClose;
                0406                         tcp->pb.csParam.close.validityFlags = 0xC0;
                0407                         tcp->pb.csParam.close.ulpTimeoutValue = 30; /* give 30 secs to close */
                0408                         tcp->pb.csParam.close.ulpTimeoutAction = 0;
                0409                         PBControl((ParmBlkPtr) &tcp->pb, true);
                0410                         newstate = TCP_CLOSING;
                0411                         break;
                0412                     }
                0413                     /* check if connection closed at both ends */
                0414                     if (tcp->rclose == 3) {
                0415                         (*tcp->callback)(tcp->context, i, NATCP_closed, tcp->reason, NULL);
                0416                         newstate = TCP_CLOSED;
                0417                     }
                0418                     break;
                0419                 case TCP_WRITING:
                0420                     if (result == 1) {
                0421                         processed = NA_NOTPROCESSED;
                0422                         break;
                0423                     }
                0424                     wb = tcp->wb;
                0425                     if (wb->rused != -1) ++wb;
                0426                     freewb(wb);
                0427                     if (result != noErr) {
                0428                         tcp->pushed = 0;
                0429                         (*tcp->callback)(tcp->context, i, NATCP_nowrite, result, NULL);
                0430                     }
                0431                     newstate = TCP_READY;
                0432                     break;
                0433                 case TCP_CLOSING:
                0434                     if (result == 1) {
                0435                         processed = NA_NOTPROCESSED;
                0436                         break;
                0437                     }
                0438                     newstate = TCP_READY;
                0439                     break;
                0440                 case TCP_CLOSED:
                0441                     if (!tcp->rclose) break;
                0442                     if (!tcp->gethost) {
                0443                         tcp->pb.csCode = TCPRelease;
                0444                         PBControl((ParmBlkPtr)&tcp->pb, false);
                0445                     }
                0446                     freewb(tcp->wb);
                0447                     freewb(tcp->wb + 1);
                0448                     DisposPtr((Ptr) tcp);
                0449                     tstate->tcpbufs[i] = NULL;
                0450                     break;
                0451             }
                0452             if (newstate) tcp->state = newstate;
                0453         } while (newstate);
                0454     }
                0455     
                0456     return (processed);
                0457 }
                0458 
                0459 /* Async notification routine
                0460  */
                0461 static pascal void myTCPNotifyProc(StreamPtr stream, unsigned short eventCode,
                0462     Ptr userDataPtr, unsigned short terminReason, struct ICMPReport *icmpMsg)
                0463 {
                0464     tcpinfo *tcp = (tcpinfo *) userDataPtr;
                0465     
                0466     switch (eventCode) {
                0467         case TCPTerminate:
                0468             tcp->rclose = 3;
                0469             tcp->reason = terminReason;
                0470             break;
                0471         case TCPClosing:
                0472             tcp->rclose = 1;
                0473             break;
                0474         case TCPDataArrival:
                0475             tcp->havedata = 1;
                0476             break;
                0477         case TCPUrgent:
                0478             tcp->urgent = 1;
                0479             break;
                0480     }
                0481 }
                0482 
                0483 /* DNR resultproc */
                0484 static pascal void addrproc(struct hostInfo *hinfop, char *udata)
                0485 {
                0486     ((tcpinfo *) udata)->dnrdone = 1;
                0487 }
                0488 
                0489 /* callback to pass TCP info to window
                0490  */
                0491 static void winreadp(void *context, na_tcp i, short status, long len, char *buf)
                0492 {
                0493     natcp_win *w;
                0494     
                0495     w = (natcp_win *) NAlockWindow((na_win **) context);
                0496     w->s = i;
                0497     (*w->readp)(&w->winp, status, len, buf);
                0498     NAunlockWindowh((na_win **) context, &w->winp);
                0499 }
                0500 
                0501 /* adjust TCP settings
                0502  */
                0503 void NATCPsettings(long streamsize, short type_of_service, short precedence, unsigned short wbsize)
                0504 {
                0505     if (!streamsize) streamsize = DEF_STREAM_SIZE;
                0506     (*tcpstate)->streamsize = streamsize ? streamsize : DEF_STREAM_SIZE;
                0507     (*tcpstate)->TOS = type_of_service;
                0508     (*tcpstate)->precedence = precedence;
                0509     if (!wbsize) wbsize = 1024;
                0510     (*tcpstate)->wbsize = wbsize;
                0511 }
                0512 
                0513 /* initialize TCP system
                0514  */
                0515 void NATCPinit(na_tcpinitp *initp)
                0516 {
                0517     IOParam *pb;
                0518     int i;
                0519     struct tcpstate *ts;
                0520 
                0521     pb = (IOParam *) NewPtrClear(sizeof (IOParam));
                0522     tcpstate = (struct tcpstate **) NAaddtask(NATCPtask, sizeof (struct tcpstate));
                0523     if (!tcpstate || !pb) {
                0524         (*initp)(NATCP_nomem);
                0525     } else {
                0526         pb->ioNamePtr = "\p.IPP";
                0527         PBOpen((ParmBlkPtr) pb, true);
                0528         ts = *tcpstate;
                0529         for (i = 0; i < MAX_TCPCON; ++i) ts->tcpbufs[i] = NULL;
                0530         ts->waiticks = 60; /* wait 1 sec for TCP close by default */
                0531         ts->win.type = NA_TCPTYPE;
                0532         ts->win.closep = tcp_closep;
                0533         ts->win.priority = -1;
                0534         ts->tcp_initp = initp;
                0535         ts->open_pb = pb;
                0536         NATCPsettings(0, 0, 0, 0);
                0537     }
                0538 }
                0539 
                0540 /* get a TCP buffer block
                0541  */
                0542 static tcpinfo *getTCPbuf(na_tcpreadp *callback, void *context, int *id)
                0543 {
                0544     int i;
                0545     tcpinfo *tcp;
                0546     
                0547     /* make sure driver is open */
                0548     if (!(*tcpstate)->tcp_driver
                0549         && (!tcp_wait(NULL, NULL) || !(*tcpstate)->tcp_driver)) {
                0550         (*callback)(context, -1, NATCP_nodriver, 0, NULL);
                0551         return (NULL);
                0552     }
                0553     
                0554     /* find pointer slot and create buffer */
                0555     for (i = 0; i < MAX_TCPCON && (*tcpstate)->tcpbufs[i]; ++i);
                0556     if (i == MAX_TCPCON) {
                0557         (*callback)(context, -1, NATCP_notcpbuf, 0, NULL);
                0558         return (NULL);
                0559     }
                0560     tcp = (tcpinfo *) NewPtr(sizeof (tcpinfo) - 1
                0561         + (*tcpstate)->streamsize + (unsigned long) (*tcpstate)->wbsize * 2);
                0562     if (!tcp) {
                0563         (*callback)(context, -1, NATCP_nomem, 0, NULL);
                0564         return (NULL);
                0565     };
                0566     *id = i;
                0567     (*tcpstate)->tcpbufs[i] = tcp;
                0568     memset((char *) tcp, 0, sizeof (tcpinfo));
                0569     
                0570     /* initialize fields from global state */
                0571     tcp->wbsize = (*tcpstate)->wbsize;
                0572     tcp->wb[0].buf = tcp->buf + (*tcpstate)->streamsize;
                0573     tcp->wb[1].buf = tcp->wb[0].buf + tcp->wbsize;
                0574     tcp->pb.ioCRefNum = (*tcpstate)->tcp_driver;
                0575     tcp->context = context;
                0576     tcp->callback = callback;
                0577     
                0578     return (tcp);
                0579 }
                0580 
                0581 /* get host name
                0582  */
                0583 void NATCPgethost(na_tcpreadp *callback, void *context)
                0584 {
                0585     int id;
                0586     tcpinfo *tcp;
                0587     struct IPParamBlock *ippb;
                0588     na_win *win;
                0589     
                0590     if ((*tcpstate)->localhost[0]) {
                0591         win = NAlockWindow((na_win **) tcpstate);
                0592         (*callback)(context, -1, NATCP_connect, strlen(tstate->localhost),
                0593             tstate->localhost);
                0594         NAunlockWindowh((na_win **) tcpstate, win);
                0595     } else if ((tcp = getTCPbuf(callback, context, &id)) != NULL) {
                0596         /* here we make the assumption that an IP param block is smaller than
                0597          * a TCP param block.  This seems like a safe assumption to me.
                0598          */
                0599         ippb = (struct IPParamBlock *) &tcp->pb;
                0600         /* get IP address */
                0601         ippb->ioCRefNum = (*tcpstate)->tcp_driver;
                0602         ippb->csCode = ipctlGetAddr;
                0603         PBControl((ParmBlkPtr)ippb, false);
                0604         if (ippb->ioResult != 0) {
                0605             (*callback)(context, -1, NATCP_notcpbuf, ippb->ioResult, NULL);
                0606             DisposPtr((Ptr) tcp);
                0607             (*tcpstate)->tcpbufs[id] = NULL;
                0608         } else {
                0609             /* begin IP address lookup */
                0610             tcp->dnrdone = 0;
                0611             AddrToName(ippb->ourAddress, &tcp->host, addrproc, (char *) tcp);
                0612             tcp->state = TCP_GETHOST;
                0613             tcp->gethost = 1;
                0614         }
                0615     }
                0616 }
                0617 
                0618 /* open a TCP connection
                0619  */
                0620 na_tcp NATCPopen(na_tcpreadp *callback, void *context, char *host, long port, short flags)
                0621 {
                0622     int i, err = NATCP_notcpbuf;
                0623     OSErr resolve = noErr;
                0624     tcpinfo *tcp;
                0625     
                0626     if ((tcp = getTCPbuf(callback, context, &i)) == NULL) return (-1);
                0627     if (flags & NATCP_server) tcp->server = 1;
                0628     tcp->port = port;
                0629     tcp->pb.csCode = TCPCreate;
                0630     tcp->pb.csParam.create.rcvBuff = (Ptr) tcp->buf;
                0631     tcp->pb.csParam.create.rcvBuffLen = (*tcpstate)->streamsize;
                0632     tcp->pb.csParam.create.notifyProc = myTCPNotifyProc;
                0633     tcp->pb.csParam.create.userDataPtr = (Ptr) tcp;
                0634     PBControl((ParmBlkPtr)&tcp->pb, false);
                0635     if (tcp->pb.ioResult == 0) {
                0636         tcp->state = TCP_RESOLVE;
                0637         tcp->stream = tcp->pb.tcpStream;
                0638         /* a server isn't required to have a hostname */
                0639         if (!host && tcp->server) return (i);
                0640         tcp->dnrdone = 0;
                0641         resolve = StrToAddr(host, &tcp->host, addrproc, (char *) tcp);
                0642         if (resolve == noErr) tcp->dnrdone = 1;
                0643         if (resolve == cacheFault || resolve == noErr) {
                0644             return (i);
                0645         }
                0646         err = NATCP_nohost;
                0647     }
                0648     DisposPtr((Ptr) tcp);
                0649     (*tcpstate)->tcpbufs[i] = NULL;
                0650     (*callback)(context, -1, err, resolve, NULL);
                0651     
                0652     return (-1);
                0653 }
                0654 
                0655 /* open a connection to a tcp window
                0656  */
                0657 na_tcp NATCPwinopen(natcp_win *w, char *host, long port, short flags)
                0658 {
                0659     return (NATCPopen(winreadp, (void *) RecoverHandle((Ptr) w), host, port, flags));
                0660 }
                0661 
                0662 /* pass a buffer to tcp connection for writing
                0663  *  dispose of -1 = copy data
                0664  */
                0665 short NATCPwrite(na_tcp i, Ptr data, long len, short dispose)
                0666 {
                0667     tcpinfo *tcp = (*tcpstate)->tcpbufs[i];
                0668     rdsEntry *rds = NULL;
                0669     tcpwb *wb;
                0670     long totallen = 0;
                0671     int j;
                0672     
                0673     if (tcp == NULL || tcp->lclose > 0 || tcp->rclose == 3) {
                0674         return (NATCP_nocon);
                0675     }
                0676     wb = tcp->wb + tcp->wbnum;
                0677     if (wb->rused == RDS) wb = tcp->wb + (1 - tcp->wbnum);
                0678     if (wb->rused == -1 || wb->rused == RDS) return (NATCP_notcpbuf);
                0679     for (j = 0; j < wb->rused; ++j) {
                0680         totallen += wb->rds[j].length;
                0681     }
                0682     if (totallen + len >= 65535) return (NATCP_notcpbuf);
                0683     rds = wb->rds + wb->rused;
                0684     rds->length = len;
                0685     rds->ptr = data;
                0686     rds[1].length = 0;
                0687     if (dispose < 0) {
                0688         if (len < tcp->wbsize - wb->wused) {
                0689             /* if data short enough, use small internal buffer */
                0690             rds->ptr = wb->buf + wb->wused;
                0691             wb->wused += len;
                0692             dispose = 0;
                0693             /* If adjacent to last rds, attach to it */
                0694             if (wb->rused && rds[-1].ptr + rds[-1].length == rds->ptr) {
                0695                 --wb->rused;
                0696                 rds[-1].length += len;
                0697                 rds->length = 0;
                0698             }
                0699         } else {
                0700             rds->ptr = NewPtr(len);
                0701             if (!rds->ptr) return (NATCP_nomem);
                0702             dispose = 1;
                0703         }
                0704         memcpy(rds->ptr, data, len);
                0705     }
                0706     wb->fflag[wb->rused++] = dispose;
                0707     if (tcp->push && tcp->state == TCP_READY) {
                0708         (void) beginwrite(tcp);
                0709     }
                0710     
                0711     return (NATCP_data);
                0712 }
                0713 
                0714 /* put a character on the TCP connection -- optimized for fast turnaround
                0715  */
                0716 short NATCPputchar(na_tcp i, char c)
                0717 {
                0718     (*tcpstate)->tcpbufs[i]->push = 1;
                0719     
                0720     return (NATCPwrite(i, (Ptr) &c, 1, -1));
                0721 }
                0722 
                0723 /* close a TCP connection
                0724  */
                0725 void NATCPclose(na_tcp i)
                0726 {
                0727     tcpinfo *tcp = (*tcpstate)->tcpbufs[i];
                0728     
                0729     if (tcp && tcp->lclose < 1) tcp->lclose = 1;
                0730 }
                0731 
                0732 /* dispose of all TCP system resources
                0733  */
                0734 void NATCPdone(long waiticks)
                0735 {
                0736     struct tcpstate *ts;
                0737     
                0738     if (tcpstate) {
                0739         ts = (struct tcpstate *) NAlockWindow((na_win **) tcpstate);
                0740         ts->waiticks = waiticks;
                0741         NAcloseWindow((na_win *) ts, NA_REQCLOSE);
                0742     }
                0743 }