home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Amiga / Workbench / Archivers / mpackPPC.lha / mpackPPC / src / macntcp.c < prev    next >
C/C++ Source or Header  |  1998-04-08  |  22KB  |  744 lines

  1. /* macntcp.c -- macintosh nifty application library async TCP routines
  2.  */
  3. /* (C) Copyright 1995 by Carnegie Mellon University
  4.  * All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software
  7.  * and its documentation for any purpose is hereby granted without
  8.  * fee, provided that the above copyright notice appear in all copies
  9.  * and that both that copyright notice and this permission notice
  10.  * appear in supporting documentation, and that the name of Carnegie
  11.  * Mellon University not be used in advertising or publicity
  12.  * pertaining to distribution of the software without specific,
  13.  * written prior permission.  Carnegie Mellon University makes no
  14.  * representations about the suitability of this software for any
  15.  * purpose.  It is provided "as is" without express or implied
  16.  * warranty.
  17.  *
  18.  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
  19.  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  20.  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
  21.  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  22.  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  23.  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  24.  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  25.  * SOFTWARE.
  26.  */
  27. /* (C) Copyright 1994-1995 by Christopher J. Newman
  28.  * All Rights Reserved.
  29.  *
  30.  * Permission to use, copy, modify, distribute, and sell this software and its
  31.  * documentation for any purpose is hereby granted without fee, provided that
  32.  * the above copyright notice appear in all copies and that both that
  33.  * copyright notice and this permission notice appear in supporting
  34.  * documentation, and that the name of Christopher J. Newman not be used in
  35.  * advertising or publicity pertaining to distribution of the software without
  36.  * specific, written prior permission.  Christopher J. Newman makes no
  37.  * representations about the suitability of this software for any purpose.  It
  38.  * is provided "as is" without express or implied warranty.
  39.  *
  40.  * CHRISTOPHER J. NEWMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  41.  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
  42.  * SHALL CHRISTOPHER J. NEWMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  43.  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  44.  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  45.  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  46.  * OF THIS SOFTWARE.
  47.  *
  48.  * Author:    Christopher J. Newman
  49.  * Message:    This is a nifty program.
  50.  */
  51.  
  52. #include <string.h>
  53. #include <MacTCPCommonTypes.h>
  54. #include <TCPPB.h>
  55. #include <AddressXlation.h>
  56. #include <GetMyIPAddr.h>
  57. #include "macnapp.h"
  58.  
  59. #define DEF_STREAM_SIZE 8192
  60. #define MAX_TCPCON  16
  61. #define RDS            8
  62.  
  63. /* write buffer for TCP writes
  64.  */
  65. typedef struct tcpwb {
  66.     short rused;            /* number of RDS used (-1 == in TCPsend) */
  67.     short wused;            /* amount of small buffer used */
  68.     rdsEntry rds[RDS+1];    /* array of RDS pointers */
  69.     char fflag[RDS+1];        /* free flags for RDS pointers (1 = call DisposPtr) */
  70.     char *buf;                /* write buffer */
  71. } tcpwb;
  72.  
  73. /* structure describing a TCP connection
  74.  */
  75. typedef struct tcpinfo {
  76.     short state;            /* current state */
  77.     short rclose;            /* remote host wants to close */
  78.     short lclose;            /* local host wants to close */
  79.     int havedata:1;            /* data is available to read */
  80.     int urgent:1;            /* TCP in urgent mode */
  81.     int push:1;                /* next write should be pushed */
  82.     int pushed:1;            /* last write was pushed */
  83.     int reading:1;            /* reading data */
  84.     int server:1;            /* is a server, rather than client */
  85.     int gethost:1;            /* getting hostname -- not a real connection */
  86.     int dnrdone:1;            /* DNR query is done */
  87.     short wbnum;            /* write buffer for next write */
  88.     unsigned short wbsize;    /* size of write buffer */
  89.     unsigned short reason;    /* reason for TCP termination */
  90.     StreamPtr stream;        /* TCP stream */
  91.     ip_port port;            /* TCP port number to connect to */
  92.     void *context;            /* user context */
  93.     na_tcpreadp *callback;    /* callback function */
  94.     rdsEntry rrds[RDS+1];    /* read RDS structure */
  95.     tcpwb wb[2];            /* write buffers */
  96.     struct hostInfo host;    /* hostname & ip_addr of host */
  97.     TCPiopb pb;                /* parameter block for TCP connect/write/close */
  98.     TCPiopb rpb;            /* parameter block for TCP read */
  99.     char buf[1];            /* stream buffer follows */
  100. } tcpinfo;
  101.  
  102. /* TCP task state information
  103.  */
  104. struct tcpstate {
  105.     na_win win;
  106.     short tcp_driver;
  107.     na_tcpinitp *tcp_initp;
  108.     tcpinfo *tcpbufs[MAX_TCPCON];
  109.     IOParam *open_pb;
  110.     long waiticks, waitstart;
  111.     short waitpercent;
  112.     long streamsize;
  113.     byte TOS, precedence;
  114.     unsigned short wbsize;
  115.     char localhost[256];
  116. } **tcpstate = NULL;
  117. #define tstate ((struct tcpstate *) win)
  118.  
  119. /* tcp state bitmasks */
  120. #define TCP_PBINUSE   0x04        /* tcp->pb in use */
  121. #define TCP_DNSINUSE  0x08        /* DNS in use */
  122. #define TCP_NOTREADY  0x10      /* not ready for reading */
  123. /* tcp states */
  124. #define TCP_READY     1                                    /* inactive */
  125. #define TCP_RESOLVE   (TCP_NOTREADY + TCP_DNSINUSE + 0)    /* resolving hostname */
  126. #define TCP_GETHOST   (TCP_NOTREADY + TCP_DNSINUSE + 1)    /* looking up hostname */
  127. #define TCP_WRITING   (TCP_PBINUSE + 0)                    /* writing data */
  128. #define TCP_CONNECT   (TCP_NOTREADY + TCP_PBINUSE + 0)    /* waiting for a connection */
  129. #define TCP_CLOSING   (TCP_NOTREADY + TCP_PBINUSE + 1)    /* waiting for TCPclose */
  130. #define TCP_CLOSED    2                                    /* closed */
  131.  
  132. /* free write buffer storage
  133.  */
  134. static void freewb(tcpwb *wb)
  135. {
  136.     short i;
  137.     
  138.     for (i = 0; wb->rds[i].length; ++i) {
  139.         if (wb->fflag[i]) {
  140.             DisposPtr(wb->rds[i].ptr);
  141.             wb->fflag[i] = 0;
  142.         }
  143.     }
  144.     wb->rused = 0;
  145.     wb->wused = 0;
  146. }
  147.  
  148. /* make sure tcp_driver is open
  149.  */
  150. static short tcp_checkdriver()
  151. {
  152.     short msg = NATCP_nodriver;
  153.     struct tcpstate *ts = *tcpstate;
  154.     IOParam *opb = ts->open_pb;
  155.     
  156.     if (!opb) return (1);
  157.     if (opb->ioResult == 1) return (0);
  158.     if (opb->ioResult == noErr && OpenResolver(nil) == noErr) {
  159.         ts->tcp_driver = opb->ioRefNum;
  160.         msg = NATCP_connect;
  161.     }
  162.     DisposPtr((Ptr) opb);
  163.     ts = *tcpstate;
  164.     ts->open_pb = NULL;
  165.     (*ts->tcp_initp)(msg);
  166.     
  167.     return (1);
  168. }
  169.  
  170. /* wait for MacTCP to finish whatever it's doing, with user cancel
  171.  */
  172. static short tcp_wait(struct tcpstate *ts, tcpinfo *tcp)
  173. {
  174.     KeyMap mapkeys;
  175. #define keys ((unsigned char *)mapkeys)
  176.     short percent;
  177.  
  178.     while (!tcp_checkdriver()
  179.         || (tcp && (tcp->state & TCP_DNSINUSE) && ! (volatile) tcp->dnrdone)
  180.         || (tcp && (tcp->state & TCP_PBINUSE) && (volatile short) tcp->pb.ioResult == 1)) {
  181.         if (ts) {
  182.             if (!ts->waiticks) return (0);
  183.             percent = ((TickCount() - ts->waitstart) * 100) / ts->waiticks;
  184.             if (percent > 100) percent = 100;
  185.             if (percent != ts->waitpercent) {
  186.                 (*ts->tcp_initp)(ts->waitpercent = percent);
  187.             }
  188.             if (percent == 100) return (0);
  189.         }
  190.         SystemTask();
  191.         GetKeys(mapkeys);
  192.         if ((keys[0x37 >> 3] >> (0x37 & 7))
  193.             & (keys[0x2f >> 3] >> (0x2f & 7)) & 1) {
  194.             return (0);
  195.         }
  196.     }
  197.     
  198.     return (1);
  199. }
  200.  
  201. /* clean up the TCP task
  202.  */
  203. static short tcp_closep(na_win *win)
  204. {
  205.     short i, j;
  206.     tcpinfo *tcp;
  207.     tcpwb *wb;
  208.     TCPiopb pb;
  209.     
  210.     tstate->waitstart = TickCount();
  211.     tstate->waitpercent = 0;
  212.     tcp_wait(tstate, NULL);
  213.     memset((void *) &pb, 0, sizeof (pb));
  214.     for (i = 0; i < MAX_TCPCON; ++i) {
  215.         if ((tcp = tstate->tcpbufs[i]) != NULL) {
  216.             /* wait for MacTCP to finish what it's doing, but permit user cancel */
  217.             if (!tcp->server || tcp->state != TCP_CONNECT) tcp_wait(tstate, tcp);
  218.             if (!tcp->gethost) {
  219.                 pb.ioCRefNum = tstate->tcp_driver;
  220.                 pb.tcpStream = tcp->stream;
  221.                 pb.csCode = TCPRelease;
  222.                 PBControl((ParmBlkPtr) &pb, false);
  223.             }
  224.             freewb(tcp->wb);
  225.             freewb(tcp->wb + 1);
  226.             DisposPtr((Ptr) tcp);
  227.             tstate->tcpbufs[i] = NULL;
  228.         }
  229.     }
  230.     tcpstate = NULL;
  231.     if (tstate->tcp_driver) CloseResolver();
  232.     
  233.     return (NA_CLOSED);
  234. }
  235.  
  236. /* begin writing data
  237.  */
  238. static short beginwrite(tcpinfo *tcp)
  239. {
  240.     tcpwb *wb = tcp->wb + tcp->wbnum;
  241.     
  242.     /* if connection terminated, or we've sent a TCPclose, we can't write */
  243.     if (tcp->rclose == 3 || tcp->lclose > 1) return (0);
  244.     memset((void *) &tcp->pb.csParam, 0, sizeof (tcp->pb.csParam));
  245.     tcp->pb.csCode = TCPSend;
  246.     tcp->pb.csParam.send.ulpTimeoutValue = 60; /* 1 minute to send data */
  247.     tcp->pb.csParam.send.ulpTimeoutAction = 0;
  248.     tcp->pb.csParam.send.validityFlags = 0xC0;
  249.     tcp->pb.csParam.send.wdsPtr = (Ptr) wb->rds;
  250.     tcp->pb.csParam.send.pushFlag = tcp->pushed = tcp->push;
  251.     PBControl((ParmBlkPtr) &tcp->pb, true);
  252.     tcp->push = 0;
  253.     tcp->wbnum = 1 - tcp->wbnum;
  254.     wb->rused = -1;
  255.     
  256.     return (tcp->state = TCP_WRITING);
  257. }
  258.  
  259. /* do I/O polling
  260.  */
  261. short NATCPtask(na_win *win)
  262. {
  263.     tcpinfo *tcp;
  264.     rdsEntry *rds, *trds;
  265.     short result, newstate;
  266.     short processed = NA_PROCESSED;
  267.     na_tcp i;
  268.     tcpwb *wb;
  269.     int j;
  270.     
  271.     /* finish off driver initialization: */
  272.     if (!tstate->tcp_driver) {
  273.         if (!tcp_checkdriver()) return (NA_NOTPROCESSED);
  274.         if (!tstate->tcp_driver) return (NA_REQCLOSE);
  275.     }
  276.     /* loop through connections */
  277.     for (i = 0; i < MAX_TCPCON; ++i) {
  278.         if ((tcp = tstate->tcpbufs[i]) != NULL) do {
  279.             /* read data if we have it */
  280.             if (!tcp->reading && !tcp->rclose && tcp->havedata && tcp->state != TCP_CONNECT) {
  281.                 tcp->rpb.ioCRefNum = tstate->tcp_driver;
  282.                 tcp->rpb.tcpStream = tcp->stream;
  283.                 tcp->rpb.csCode = TCPNoCopyRcv;
  284.                 tcp->rpb.csParam.receive.rdsPtr = (Ptr) tcp->rrds;
  285.                 tcp->rpb.csParam.receive.commandTimeoutValue = 5;
  286.                 tcp->rpb.csParam.receive.rdsLength = RDS;
  287.                 if (tcp->pushed) {
  288.                     tcp->rpb.csParam.receive.commandTimeoutValue = 1;
  289.                     tcp->rpb.csParam.receive.rdsLength = 1;
  290.                 }
  291.                 tcp->havedata = 0;
  292.                 PBControl((ParmBlkPtr) &tcp->rpb, tcp->pushed ? false : true);
  293.                 tcp->reading = 1;
  294.                 tcp->pushed = 0;
  295.             }
  296.             if (tcp->reading) {
  297.                 if ((result = tcp->rpb.ioResult) == 1) {
  298.                     processed = NA_NOTPROCESSED;
  299.                 } else {
  300.                     tcp->reading = 0;
  301.                     if (result != noErr) {
  302.                         if (result != commandTimeout) {
  303.                             (*tcp->callback)(tcp->context, i, NATCP_noread, result, NULL);
  304.                         }
  305.                     } else {
  306.                         result = NATCP_data | NATCP_more;
  307.                         if (tcp->rpb.csParam.receive.urgentFlag) tcp->urgent = 1;
  308.                         if (tcp->urgent) result |= NATCP_urgent;
  309.                         if (tcp->rpb.csParam.receive.markFlag) tcp->urgent = 0;
  310.                         for (rds = tcp->rrds; rds->length; ++rds) {
  311.                             if (!rds[1].length) result &= ~NATCP_more;
  312.                             (*tcp->callback)(tcp->context, i, result,
  313.                                 rds->length, rds->ptr);
  314.                         }
  315.                         tcp->rpb.csCode = TCPRcvBfrReturn;
  316.                         PBControl((ParmBlkPtr) &tcp->rpb, false);
  317.                     }
  318.                 }
  319.             }
  320.             result = tcp->pb.ioResult;
  321.             newstate = 0;
  322.             switch (tcp->state) {
  323.                 case TCP_GETHOST:
  324.                     if (tcp->dnrdone) {
  325.                         tcp->rclose = 3;
  326.                         newstate = TCP_CLOSED;
  327.                         if (tcp->host.rtnCode != noErr) {
  328.                             (*tcp->callback)(tcp->context, i, NATCP_nohost,
  329.                                 tcp->host.rtnCode, NULL);
  330.                         } else {
  331.                             (*tcp->callback)(tcp->context, i, NATCP_connect,
  332.                                 strlen(tcp->host.cname), tcp->host.cname);
  333.                             strcpy(tstate->localhost, tcp->host.cname);
  334.                         }
  335.                     }
  336.                     break;
  337.                 case TCP_RESOLVE:
  338.                     if (tcp->dnrdone) {
  339.                         if (tcp->host.rtnCode != noErr) {
  340.                             tcp->rclose = 3;
  341.                             newstate = TCP_CLOSED;
  342.                             (*tcp->callback)(tcp->context, i, NATCP_nohost,
  343.                                 tcp->host.rtnCode, NULL);
  344.                         } else if (!tcp->lclose) {
  345.                             memset((void *) &tcp->pb, 0, sizeof (tcp->pb));
  346.                             tcp->pb.ioCRefNum = tstate->tcp_driver;
  347.                             tcp->pb.tcpStream = tcp->stream;
  348.                             tcp->pb.csParam.open.ulpTimeoutValue = 30;
  349.                             tcp->pb.csParam.open.ulpTimeoutAction = 1; /* Abort on timeout */
  350.                             tcp->pb.csParam.open.tosFlags = tstate->TOS;
  351.                             tcp->pb.csParam.open.precedence = tstate->precedence;
  352.                             tcp->pb.csParam.open.validityFlags = 
  353.                                 timeoutValue|timeoutAction|typeOfService|precedence;
  354.                             tcp->pb.csParam.open.remoteHost = tcp->host.addr[0];
  355.                             if (tcp->server) {
  356.                                 tcp->pb.csCode = TCPPassiveOpen;
  357.                                 tcp->pb.csParam.open.commandTimeoutValue = 0;
  358.                                 tcp->pb.csParam.open.remotePort = 0;
  359.                                 tcp->pb.csParam.open.localPort = tcp->port;
  360.                             } else {
  361.                                 tcp->pb.csCode = TCPActiveOpen;
  362.                                 tcp->pb.csParam.open.remotePort = tcp->port;
  363.                                 tcp->pb.csParam.open.localPort = 0;
  364.                             }
  365.                             PBControl((ParmBlkPtr) &tcp->pb, true);
  366.                             newstate = TCP_CONNECT;
  367.                         }
  368.                     }
  369.                     break;
  370.                 case TCP_CONNECT:
  371.                     if (result == 1) {
  372.                         processed = NA_NOTPROCESSED;
  373.                         break;
  374.                     }
  375.                     if (result != noErr) {
  376.                         tcp->rclose = 3;
  377.                         newstate = TCP_CLOSED;
  378.                         (*tcp->callback)(tcp->context, i, NATCP_nocon, result, NULL);
  379.                     } else {
  380.                         newstate = TCP_READY;
  381.                         if (tcp->server) {
  382.                             tcp->port = tcp->pb.csParam.open.remotePort;
  383.                             if (!*tcp->host.cname) {
  384.                                 AddrToStr(tcp->pb.csParam.open.remoteHost, tcp->host.cname);
  385.                             }
  386.                         }
  387.                         (*tcp->callback)(tcp->context, i, NATCP_connect,
  388.                             tcp->port, tcp->host.cname);
  389.                     }
  390.                     break;
  391.                 case TCP_READY:
  392.                     /* Write data if we have it */
  393.                     wb = tcp->wb + tcp->wbnum;
  394.                     if (wb->rused && (newstate = beginwrite(tcp))) {
  395.                         break;
  396.                     }
  397.                     /* check if other side wants to close */
  398.                     if (tcp->rclose == 1) {
  399.                         tcp->rclose = 2;
  400.                         (*tcp->callback)(tcp->context, i, NATCP_closing, 0, NULL);
  401.                     }
  402.                     /* check if connection needs closing at this end */
  403.                      if (tcp->lclose == 1) {
  404.                         tcp->lclose = 2;
  405.                         tcp->pb.csCode = TCPClose;
  406.                         tcp->pb.csParam.close.validityFlags = 0xC0;
  407.                         tcp->pb.csParam.close.ulpTimeoutValue = 30; /* give 30 secs to close */
  408.                         tcp->pb.csParam.close.ulpTimeoutAction = 0;
  409.                         PBControl((ParmBlkPtr) &tcp->pb, true);
  410.                         newstate = TCP_CLOSING;
  411.                         break;
  412.                     }
  413.                     /* check if connection closed at both ends */
  414.                     if (tcp->rclose == 3) {
  415.                         (*tcp->callback)(tcp->context, i, NATCP_closed, tcp->reason, NULL);
  416.                         newstate = TCP_CLOSED;
  417.                     }
  418.                     break;
  419.                 case TCP_WRITING:
  420.                     if (result == 1) {
  421.                         processed = NA_NOTPROCESSED;
  422.                         break;
  423.                     }
  424.                     wb = tcp->wb;
  425.                     if (wb->rused != -1) ++wb;
  426.                     freewb(wb);
  427.                     if (result != noErr) {
  428.                         tcp->pushed = 0;
  429.                         (*tcp->callback)(tcp->context, i, NATCP_nowrite, result, NULL);
  430.                     }
  431.                     newstate = TCP_READY;
  432.                     break;
  433.                 case TCP_CLOSING:
  434.                     if (result == 1) {
  435.                         processed = NA_NOTPROCESSED;
  436.                         break;
  437.                     }
  438.                     newstate = TCP_READY;
  439.                     break;
  440.                 case TCP_CLOSED:
  441.                     if (!tcp->rclose) break;
  442.                     if (!tcp->gethost) {
  443.                         tcp->pb.csCode = TCPRelease;
  444.                         PBControl((ParmBlkPtr)&tcp->pb, false);
  445.                     }
  446.                     freewb(tcp->wb);
  447.                     freewb(tcp->wb + 1);
  448.                     DisposPtr((Ptr) tcp);
  449.                     tstate->tcpbufs[i] = NULL;
  450.                     break;
  451.             }
  452.             if (newstate) tcp->state = newstate;
  453.         } while (newstate);
  454.     }
  455.     
  456.     return (processed);
  457. }
  458.  
  459. /* Async notification routine
  460.  */
  461. static pascal void myTCPNotifyProc(StreamPtr stream, unsigned short eventCode,
  462.     Ptr userDataPtr, unsigned short terminReason, struct ICMPReport *icmpMsg)
  463. {
  464.     tcpinfo *tcp = (tcpinfo *) userDataPtr;
  465.     
  466.     switch (eventCode) {
  467.         case TCPTerminate:
  468.             tcp->rclose = 3;
  469.             tcp->reason = terminReason;
  470.             break;
  471.         case TCPClosing:
  472.             tcp->rclose = 1;
  473.             break;
  474.         case TCPDataArrival:
  475.             tcp->havedata = 1;
  476.             break;
  477.         case TCPUrgent:
  478.             tcp->urgent = 1;
  479.             break;
  480.     }
  481. }
  482.  
  483. /* DNR resultproc */
  484. static pascal void addrproc(struct hostInfo *hinfop, char *udata)
  485. {
  486.     ((tcpinfo *) udata)->dnrdone = 1;
  487. }
  488.  
  489. /* callback to pass TCP info to window
  490.  */
  491. static void winreadp(void *context, na_tcp i, short status, long len, char *buf)
  492. {
  493.     natcp_win *w;
  494.     
  495.     w = (natcp_win *) NAlockWindow((na_win **) context);
  496.     w->s = i;
  497.     (*w->readp)(&w->winp, status, len, buf);
  498.     NAunlockWindowh((na_win **) context, &w->winp);
  499. }
  500.  
  501. /* adjust TCP settings
  502.  */
  503. void NATCPsettings(long streamsize, short type_of_service, short precedence, unsigned short wbsize)
  504. {
  505.     if (!streamsize) streamsize = DEF_STREAM_SIZE;
  506.     (*tcpstate)->streamsize = streamsize ? streamsize : DEF_STREAM_SIZE;
  507.     (*tcpstate)->TOS = type_of_service;
  508.     (*tcpstate)->precedence = precedence;
  509.     if (!wbsize) wbsize = 1024;
  510.     (*tcpstate)->wbsize = wbsize;
  511. }
  512.  
  513. /* initialize TCP system
  514.  */
  515. void NATCPinit(na_tcpinitp *initp)
  516. {
  517.     IOParam *pb;
  518.     int i;
  519.     struct tcpstate *ts;
  520.  
  521.     pb = (IOParam *) NewPtrClear(sizeof (IOParam));
  522.     tcpstate = (struct tcpstate **) NAaddtask(NATCPtask, sizeof (struct tcpstate));
  523.     if (!tcpstate || !pb) {
  524.         (*initp)(NATCP_nomem);
  525.     } else {
  526.         pb->ioNamePtr = "\p.IPP";
  527.         PBOpen((ParmBlkPtr) pb, true);
  528.         ts = *tcpstate;
  529.         for (i = 0; i < MAX_TCPCON; ++i) ts->tcpbufs[i] = NULL;
  530.         ts->waiticks = 60; /* wait 1 sec for TCP close by default */
  531.         ts->win.type = NA_TCPTYPE;
  532.         ts->win.closep = tcp_closep;
  533.         ts->win.priority = -1;
  534.         ts->tcp_initp = initp;
  535.         ts->open_pb = pb;
  536.         NATCPsettings(0, 0, 0, 0);
  537.     }
  538. }
  539.  
  540. /* get a TCP buffer block
  541.  */
  542. static tcpinfo *getTCPbuf(na_tcpreadp *callback, void *context, int *id)
  543. {
  544.     int i;
  545.     tcpinfo *tcp;
  546.     
  547.     /* make sure driver is open */
  548.     if (!(*tcpstate)->tcp_driver
  549.         && (!tcp_wait(NULL, NULL) || !(*tcpstate)->tcp_driver)) {
  550.         (*callback)(context, -1, NATCP_nodriver, 0, NULL);
  551.         return (NULL);
  552.     }
  553.     
  554.     /* find pointer slot and create buffer */
  555.     for (i = 0; i < MAX_TCPCON && (*tcpstate)->tcpbufs[i]; ++i);
  556.     if (i == MAX_TCPCON) {
  557.         (*callback)(context, -1, NATCP_notcpbuf, 0, NULL);
  558.         return (NULL);
  559.     }
  560.     tcp = (tcpinfo *) NewPtr(sizeof (tcpinfo) - 1
  561.         + (*tcpstate)->streamsize + (unsigned long) (*tcpstate)->wbsize * 2);
  562.     if (!tcp) {
  563.         (*callback)(context, -1, NATCP_nomem, 0, NULL);
  564.         return (NULL);
  565.     };
  566.     *id = i;
  567.     (*tcpstate)->tcpbufs[i] = tcp;
  568.     memset((char *) tcp, 0, sizeof (tcpinfo));
  569.     
  570.     /* initialize fields from global state */
  571.     tcp->wbsize = (*tcpstate)->wbsize;
  572.     tcp->wb[0].buf = tcp->buf + (*tcpstate)->streamsize;
  573.     tcp->wb[1].buf = tcp->wb[0].buf + tcp->wbsize;
  574.     tcp->pb.ioCRefNum = (*tcpstate)->tcp_driver;
  575.     tcp->context = context;
  576.     tcp->callback = callback;
  577.     
  578.     return (tcp);
  579. }
  580.  
  581. /* get host name
  582.  */
  583. void NATCPgethost(na_tcpreadp *callback, void *context)
  584. {
  585.     int id;
  586.     tcpinfo *tcp;
  587.     struct IPParamBlock *ippb;
  588.     na_win *win;
  589.     
  590.     if ((*tcpstate)->localhost[0]) {
  591.         win = NAlockWindow((na_win **) tcpstate);
  592.         (*callback)(context, -1, NATCP_connect, strlen(tstate->localhost),
  593.             tstate->localhost);
  594.         NAunlockWindowh((na_win **) tcpstate, win);
  595.     } else if ((tcp = getTCPbuf(callback, context, &id)) != NULL) {
  596.         /* here we make the assumption that an IP param block is smaller than
  597.          * a TCP param block.  This seems like a safe assumption to me.
  598.          */
  599.         ippb = (struct IPParamBlock *) &tcp->pb;
  600.         /* get IP address */
  601.         ippb->ioCRefNum = (*tcpstate)->tcp_driver;
  602.         ippb->csCode = ipctlGetAddr;
  603.         PBControl((ParmBlkPtr)ippb, false);
  604.         if (ippb->ioResult != 0) {
  605.             (*callback)(context, -1, NATCP_notcpbuf, ippb->ioResult, NULL);
  606.             DisposPtr((Ptr) tcp);
  607.             (*tcpstate)->tcpbufs[id] = NULL;
  608.         } else {
  609.             /* begin IP address lookup */
  610.             tcp->dnrdone = 0;
  611.             AddrToName(ippb->ourAddress, &tcp->host, addrproc, (char *) tcp);
  612.             tcp->state = TCP_GETHOST;
  613.             tcp->gethost = 1;
  614.         }
  615.     }
  616. }
  617.  
  618. /* open a TCP connection
  619.  */
  620. na_tcp NATCPopen(na_tcpreadp *callback, void *context, char *host, long port, short flags)
  621. {
  622.     int i, err = NATCP_notcpbuf;
  623.     OSErr resolve = noErr;
  624.     tcpinfo *tcp;
  625.     
  626.     if ((tcp = getTCPbuf(callback, context, &i)) == NULL) return (-1);
  627.     if (flags & NATCP_server) tcp->server = 1;
  628.     tcp->port = port;
  629.     tcp->pb.csCode = TCPCreate;
  630.     tcp->pb.csParam.create.rcvBuff = (Ptr) tcp->buf;
  631.     tcp->pb.csParam.create.rcvBuffLen = (*tcpstate)->streamsize;
  632.     tcp->pb.csParam.create.notifyProc = myTCPNotifyProc;
  633.     tcp->pb.csParam.create.userDataPtr = (Ptr) tcp;
  634.     PBControl((ParmBlkPtr)&tcp->pb, false);
  635.     if (tcp->pb.ioResult == 0) {
  636.         tcp->state = TCP_RESOLVE;
  637.         tcp->stream = tcp->pb.tcpStream;
  638.         /* a server isn't required to have a hostname */
  639.         if (!host && tcp->server) return (i);
  640.         tcp->dnrdone = 0;
  641.         resolve = StrToAddr(host, &tcp->host, addrproc, (char *) tcp);
  642.         if (resolve == noErr) tcp->dnrdone = 1;
  643.         if (resolve == cacheFault || resolve == noErr) {
  644.             return (i);
  645.         }
  646.         err = NATCP_nohost;
  647.     }
  648.     DisposPtr((Ptr) tcp);
  649.     (*tcpstate)->tcpbufs[i] = NULL;
  650.     (*callback)(context, -1, err, resolve, NULL);
  651.     
  652.     return (-1);
  653. }
  654.  
  655. /* open a connection to a tcp window
  656.  */
  657. na_tcp NATCPwinopen(natcp_win *w, char *host, long port, short flags)
  658. {
  659.     return (NATCPopen(winreadp, (void *) RecoverHandle((Ptr) w), host, port, flags));
  660. }
  661.  
  662. /* pass a buffer to tcp connection for writing
  663.  *  dispose of -1 = copy data
  664.  */
  665. short NATCPwrite(na_tcp i, Ptr data, long len, short dispose)
  666. {
  667.     tcpinfo *tcp = (*tcpstate)->tcpbufs[i];
  668.     rdsEntry *rds = NULL;
  669.     tcpwb *wb;
  670.     long totallen = 0;
  671.     int j;
  672.     
  673.     if (tcp == NULL || tcp->lclose > 0 || tcp->rclose == 3) {
  674.         return (NATCP_nocon);
  675.     }
  676.     wb = tcp->wb + tcp->wbnum;
  677.     if (wb->rused == RDS) wb = tcp->wb + (1 - tcp->wbnum);
  678.     if (wb->rused == -1 || wb->rused == RDS) return (NATCP_notcpbuf);
  679.     for (j = 0; j < wb->rused; ++j) {
  680.         totallen += wb->rds[j].length;
  681.     }
  682.     if (totallen + len >= 65535) return (NATCP_notcpbuf);
  683.     rds = wb->rds + wb->rused;
  684.     rds->length = len;
  685.     rds->ptr = data;
  686.     rds[1].length = 0;
  687.     if (dispose < 0) {
  688.         if (len < tcp->wbsize - wb->wused) {
  689.             /* if data short enough, use small internal buffer */
  690.             rds->ptr = wb->buf + wb->wused;
  691.             wb->wused += len;
  692.             dispose = 0;
  693.             /* If adjacent to last rds, attach to it */
  694.             if (wb->rused && rds[-1].ptr + rds[-1].length == rds->ptr) {
  695.                 --wb->rused;
  696.                 rds[-1].length += len;
  697.                 rds->length = 0;
  698.             }
  699.         } else {
  700.             rds->ptr = NewPtr(len);
  701.             if (!rds->ptr) return (NATCP_nomem);
  702.             dispose = 1;
  703.         }
  704.         memcpy(rds->ptr, data, len);
  705.     }
  706.     wb->fflag[wb->rused++] = dispose;
  707.     if (tcp->push && tcp->state == TCP_READY) {
  708.         (void) beginwrite(tcp);
  709.     }
  710.     
  711.     return (NATCP_data);
  712. }
  713.  
  714. /* put a character on the TCP connection -- optimized for fast turnaround
  715.  */
  716. short NATCPputchar(na_tcp i, char c)
  717. {
  718.     (*tcpstate)->tcpbufs[i]->push = 1;
  719.     
  720.     return (NATCPwrite(i, (Ptr) &c, 1, -1));
  721. }
  722.  
  723. /* close a TCP connection
  724.  */
  725. void NATCPclose(na_tcp i)
  726. {
  727.     tcpinfo *tcp = (*tcpstate)->tcpbufs[i];
  728.     
  729.     if (tcp && tcp->lclose < 1) tcp->lclose = 1;
  730. }
  731.  
  732. /* dispose of all TCP system resources
  733.  */
  734. void NATCPdone(long waiticks)
  735. {
  736.     struct tcpstate *ts;
  737.     
  738.     if (tcpstate) {
  739.         ts = (struct tcpstate *) NAlockWindow((na_win **) tcpstate);
  740.         ts->waiticks = waiticks;
  741.         NAcloseWindow((na_win *) ts, NA_REQCLOSE);
  742.     }
  743. }
  744.