home *** CD-ROM | disk | FTP | other *** search
/ GRIPS 2: Government Rast…rocessing Software & Data / GRIPS_2.cdr / dos / ncsa_tel / contribu / byu_tel2.hqx / tcpip / tcp.c < prev    next >
Text File  |  1989-06-22  |  18KB  |  635 lines

  1. /*
  2. *   TCP routines
  3. *
  4. ****************************************************************************
  5. *                                                                          *
  6. *      part of:                                                            *
  7. *      TCP/UDP/ICMP/IP Network kernel for NCSA Telnet                      *
  8. *      by Tim Krauskopf                                                    *
  9. *                                                                          *
  10. *      National Center for Supercomputing Applications                     *
  11. *      152 Computing Applications Building                                 *
  12. *      605 E. Springfield Ave.                                             *
  13. *      Champaign, IL  61820                                                *
  14. *                                                                          *
  15. *    Copyright (c) 1987, Board of Trustees of the University of Illinois   *
  16. *                                                                          *
  17. ****************************************************************************
  18. *   Tim Krauskopf       Fall 1986
  19. */
  20. #include "stdio.h"
  21. #include "protocol.h"
  22. #include "data.h"
  23.  
  24. static int pnum;
  25. /************************************************************************
  26. *  tcpinterpret
  27. *  This is called when a packet comes in, passes IP checksumming and is
  28. *  of the TCP protocol type.  Check and see if we are expecting it and
  29. *  where the data should go.
  30. */
  31. tcpinterpret(p,tlen)
  32.     int tlen;
  33.     TCPKT *p;
  34.     {
  35.     uint i,myport,hlen,hisport;
  36.     struct port *prt;
  37.     
  38. /*
  39. *  checksum
  40. *    First, fill the pseudo header with its fields, then run our
  41. *  checksum to confirm it.
  42. *
  43. */
  44.     if (p->t.check) {
  45.         movebytes(tcps.source,p->i.ipsource,8);  /* move both addresses */
  46.         tcps.z = 0;
  47.         tcps.proto = p->i.protocol;
  48.  
  49.         tcps.tcplen = intswap(tlen);            /* byte-swapped length */
  50.  
  51.         if (tcpcheck(&tcps,&p->t,tlen)) {    /* compute checksum */
  52.             netposterr(400);
  53.             return(2);
  54.         }
  55.     }
  56.  
  57. /*
  58. *  find the port which is associated with the incoming packet
  59. *  First try open connections, then try listeners
  60. */
  61.     myport = intswap(p->t.dest);
  62.     hisport = intswap(p->t.source);
  63.     hlen = p->t.hlen >> 2;                /* bytes offset to data */
  64.  
  65.  
  66.     for (i=0; i<NPORTS; i++) {
  67.  
  68.         prt = portlist[i];
  69.  
  70.         if (prt != NULL && prt->in.port == myport && prt->out.port == hisport) {
  71.             pnum = i;
  72.             return(tcpdo(prt,p,tlen,hlen));
  73.         }
  74.  
  75.     }
  76.  
  77. /*
  78. *  check to see if the incoming packet should go to a listener
  79. */
  80.  
  81.     for (i=0; i < NPORTS; i++) {
  82.         prt = portlist[i];
  83.  
  84.         if (prt != NULL && !prt->out.port && 
  85.             prt->in.port == myport && (p->t.flags & TSYN)) {
  86.             pnum = i;
  87.             return(tcpdo(prt,p,tlen,hlen));
  88.             }
  89.  
  90.     }
  91.  
  92. /*
  93. *  no matching port was found to handle this packet, reject it
  94. */
  95.  
  96.         tcpreset(p);                /* tell them they are crazy */
  97.         if (!(p->t.flags & TSYN))    /* no error message if it is a SYN */
  98.             netposterr(407);            /* invalid port for incoming packet */
  99.  
  100.         return(1);                    /* no port matches */
  101.  
  102. }
  103.  
  104. /**********************************************************************/
  105. /*  tcpdo
  106. *  Looking at the port structure for the destination port, deliver
  107. *  the incoming packet.
  108. */
  109. tcpdo(prt,p,tlen,hlen)
  110.     int tlen,hlen;
  111.     struct port *prt;
  112.     TCPKT *p;
  113.     {
  114.  
  115.     switch (prt->state) {
  116.  
  117.         case SLISTEN:                    /* waiting for remote connection */
  118.             if (p->t.flags & TSYN) {    /* receive SYN */
  119. /*
  120. *   remember anything important from the incoming TCP header 
  121. */
  122.                 prt->out.size = intswap(p->t.window);    /* credit window */
  123.                 prt->out.port = intswap(p->t.source);
  124.                 prt->in.nxt = longswap(p->t.seq) + 1;
  125. /*
  126. *  set the necessary fields in the outgoing TCP packet
  127. */
  128.                 prt->tcpout.t.dest = p->t.source;
  129.                 prt->tcpout.t.ack = longswap(prt->in.nxt);
  130.                 prt->tcpout.t.flags = TSYN | TACK;
  131.                 prt->tcpout.t.hlen = 24 << 2;
  132. /*
  133. *  note that the maxmimum segment size is installed by 'netlisten()'
  134. *  hence the header length is 24, not 20
  135. */
  136.                                 
  137. /*
  138. *  initialize all of the low-level transmission stuff (IP and lower)
  139. */
  140.                 movebytes(prt->tcps.dest,p->i.ipsource,4);
  141.                 movebytes(prt->tcpout.i.ipdest,p->i.ipsource,4);
  142.                 movebytes(prt->tcpout.d.dest,p->d.me,DADDLEN);
  143. #ifdef MAC
  144. /*
  145. *   look up address in the arp cache if using Atalk encapsulation
  146. */
  147.     if (!nnemac) {
  148.         unsigned char *pc;
  149.             pc = getdlayer(p->i.ipsource);
  150.             if (pc != NULL)
  151.                 movebytes(prt->tcpout.d.dest,pc,DADDLEN);
  152.             else
  153.                 return(0);        /* no hope this time */
  154.     }
  155. #endif
  156.  
  157.                 tcpsend(prt,4);
  158.                 prt->state = SSYNR;        /* syn received */
  159.             }
  160.             break;
  161.         case SSYNR:
  162.             if (!(p->t.flags & TACK)) {
  163.                 tcpsend(prt,4);
  164.                 break;                    /* not the right one */
  165.             }
  166.             prt->tcpout.t.hlen = 20 << 2;
  167.             prt->out.lasttime = time(NULL);            /* don't need response */
  168.             prt->out.nxt++;                            /* count SYN as sent */
  169.             prt->out.ack = longswap(p->t.ack);         /* starting ACK value */
  170.             prt->out.size = intswap(p->t.window);    /* allowed window */
  171.             prt->tcpout.t.flags = TACK;        /* starting ACK flag */
  172.             prt->state = SEST;                /* drop through to established */
  173.             netputevent(CONCLASS,CONOPEN,pnum);
  174.             checkmss(prt,p,hlen);            /* see if MSS option is there */
  175.  
  176.                                             /* fall through */
  177.         case SEST:
  178.             /* normal data transmission */
  179.             /*
  180.             *  check and accept a possible piggybacked ack
  181.             */
  182.             ackcheck(prt,p,pnum);
  183.  
  184.             estab1986(prt,p,tlen,hlen);
  185.             return(0);
  186.         case SSYNS:                /* check to see if it ACKS correctly */
  187.                                 /* remember that tcpout is pre-set-up */
  188.             if (p->t.flags & TACK) {        /* It is ACKING us */
  189.                 if (longswap(p->t.ack) != prt->out.nxt) {
  190.                     netposterr(401);
  191.                     return(1);
  192.                 }
  193.             }
  194.             if (p->t.flags & TRESET) {
  195.                 netposterr(507);
  196.                 prt->state = SCLOSED;
  197.                 netputuev(CONCLASS,CONCLOSE,pnum);
  198.                 return(1);
  199.             }
  200.             if (p->t.flags & TSYN) {            /* need to send ACK */
  201.                 prt->tcpout.t.flags = TACK;
  202.                 prt->in.nxt = longswap(p->t.seq) + 1;
  203.                 prt->tcpout.t.ack = longswap(prt->in.nxt);
  204.                 prt->out.ack = longswap(p->t.ack);
  205.                 prt->out.size = intswap(p->t.window);    /* credit window */
  206.                 prt->out.lasttime = 0L;
  207.                 if (p->t.flags & TACK) {
  208.                     prt->state = SEST;
  209.                     netputevent(CONCLASS,CONOPEN,pnum);
  210.                     checkmss(prt,p,hlen);
  211.                 }
  212.                 else
  213.                     prt->state = SSYNR;        /* syn received */
  214.             }
  215.             break;
  216.         case SCWAIT:
  217.             ackcheck(prt,p,pnum);
  218.             if (!prt->in.contain) {
  219.                 prt->tcpout.t.flags = TFIN | TACK;
  220.                 prt->out.lasttime = 0L;
  221.                 prt->state = SLAST;
  222.             }
  223.             break;
  224.         case SLAST:
  225.             /* check ack of FIN, or reset to see if we are done */
  226.             if ((p->t.flags & TRESET) || (longswap(p->t.ack) == prt->out.nxt+1))
  227.                 prt->state = SCLOSED;
  228.             break;
  229.         case SFW1:                            /* waiting for ACK of FIN */
  230.                                             /* throw away data */
  231.             prt->in.nxt = longswap(p->t.seq)+tlen-hlen;
  232.             if (p->t.flags & TRESET)
  233.                 prt->state = SCLOSED;
  234.             else if (longswap(p->t.ack) != prt->out.nxt+1) {
  235.                 if (p->t.flags & TFIN) {    /* got FIN, no ACK for mine */
  236.                     prt->in.nxt++;                /* account for FIN byte */
  237.                     prt->tcpout.t.ack = longswap(prt->in.nxt);
  238.                     prt->tcpout.t.flags = TACK;    /* final byte has no FIN flag */
  239.                     prt->out.lasttime = 0L;        /* cause last ACK to be sent */
  240.                     prt->state = SCLOSING;
  241.                 }
  242.                 else {
  243.                     prt->tcpout.t.ack = longswap(prt->in.nxt);
  244.                     prt->tcpout.t.flags = TACK | TFIN;
  245.                     prt->out.lasttime = 0L;
  246.                 }
  247.             }
  248.             else if (p->t.flags & TFIN) {    /* ACK and FIN */
  249.                 prt->in.nxt++;                /* account for his FIN flag */
  250.                 prt->out.nxt++;                /* account for my FIN */
  251.                 prt->tcpout.t.ack = longswap(prt->in.nxt);
  252.                 prt->tcpout.t.flags = TACK;    /* final byte has no FIN flag */
  253.                 prt->out.lasttime = 0L;        /* cause last ACK to be sent */
  254.                 prt->state = STWAIT;        /* we are done */
  255.             }
  256.             else {                            /* got ACK, no FIN */
  257.                 prt->out.nxt++;                /* account for my FIN byte */
  258.                 prt->tcpout.t.flags = TACK;    /* final pkt has no FIN flag */
  259.                 prt->state = SFW2;
  260.             }
  261.             break;
  262.         case SFW2:                                /* want FIN */
  263.             prt->in.nxt = longswap(p->t.seq)+tlen-hlen;
  264.             if (p->t.flags & TRESET)
  265.                 prt->state = SCLOSED;
  266.             else if (p->t.flags & TFIN) {        /* we got FIN */
  267.                 prt->in.nxt++;                    /* count his FIN byte */
  268.                 prt->tcpout.t.ack = longswap(prt->in.nxt);
  269.                 prt->out.lasttime = 0L;        /* cause last ACK to be sent */
  270.                 prt->state = STWAIT;
  271.             }
  272.             break;
  273.         case SCLOSING:                        /* want ACK of FIN */
  274.             if (p->t.flags & TRESET)
  275.                 prt->state = SCLOSED;
  276.             else if (!ackcheck(prt,p,pnum)) {
  277.                 prt->out.nxt++;                /* account for my FIN byte */
  278.                 prt->state = STWAIT;        /* time-wait state next */
  279.             }
  280.             break;
  281.         case STWAIT:                        /* ack FIN again? */
  282.             if (p->t.flags & TRESET)
  283.                 prt->state = SCLOSED;
  284.             if (p->t.flags & TFIN)             /* only if he wants it */
  285.                 prt->out.lasttime = 0L;
  286.             if (prt->out.lasttime && 
  287.                 (prt->out.lasttime + WAITTIME < time(NULL))) 
  288.                 prt->state = SCLOSED;
  289.             break;            
  290.         case SCLOSED:
  291.             prt->in.port = prt->out.port = 0;
  292.             break;
  293.         default:
  294.             netposterr(403);            /* unknown tcp state */
  295.             break;
  296.     }
  297.  
  298.     return(0);
  299.  
  300. }
  301.  
  302. /**********************************************************************/
  303. /*  checkmss
  304. *  Look at incoming SYN,ACK packet and check for the options field
  305. *  containing a TCP Maximum segment size option.  If it has one,
  306. *  then set the port's internal value to make sure that it never
  307. *  exceeds that segment size.
  308. */
  309. checkmss(prt,p,hlen)
  310.     int hlen;
  311.     struct port *prt;
  312.     TCPKT *p;
  313.     {
  314.     unsigned int i;
  315. /*
  316. *  check header for maximum segment size option
  317. */
  318.     if (hlen > 20 && p->x.options[0] == 2 && p->x.options[1] == 4) {
  319.         movebytes(&i,&p->x.options[2],2);    /* swapped value of maxseg */
  320.         i = intswap(i);
  321.         if (i < prt->sendsize)                /* we have our own limits too */
  322.             prt->sendsize = i;
  323.     }
  324. }
  325.  
  326. /**********************************************************************/
  327. /* tcpreset
  328. *  Send a reset packet back to sender
  329. *  Use the packet which just came in as a template to return to
  330. *  sender.  Fill in all of the fields necessary and dlayersend it back.
  331. */
  332. tcpreset(t)
  333.     TCPKT *t;
  334.     {
  335.     uint tport;
  336.     struct pseudotcp xxx;
  337.  
  338.     if (t->t.flags & TRESET)        /* don't reset a reset */
  339.         return(1);
  340.  
  341. /*
  342. *  swap TCP layer portions for sending back
  343. */
  344.     if (t->t.flags & TACK) {
  345.         t->t.seq = t->t.ack;        /* ack becomes next seq # */
  346.         t->t.ack = 0L;                /* ack # is 0 */
  347.     }
  348.     else {
  349.         t->t.ack = longswap(longswap(t->t.seq)+t->i.tlen-sizeof(IPLAYER));
  350.         t->t.seq = 0L;
  351.     }
  352.  
  353.     t->t.flags = TRESET;
  354.     tport = t->t.source;                    /* swap port #'s */
  355.     t->t.source = t->t.dest;
  356.     t->t.dest = tport;
  357.     t->t.hlen = 20 << 2;                    /* header len */
  358.     t->t.window = 0;
  359.  
  360. /*
  361. *  create pseudo header for checksum
  362. */
  363.     xxx.z = 0;
  364.     xxx.proto = t->i.protocol;
  365.     xxx.tcplen = intswap(20);
  366.     movebytes(xxx.source,t->i.ipsource,4);
  367.     movebytes(xxx.dest,t->i.ipdest,4);
  368.  
  369.     t->t.check = 0;    
  370.     t->t.check = tcpcheck(&xxx,&t->t,sizeof(struct tcph));
  371. /*
  372. *  IP and data link layers
  373. */    
  374.     movebytes(t->i.ipdest,t->i.ipsource,4);  /* machine it came from */
  375.     movebytes(t->i.ipsource,nnipnum,4); 
  376.     t->i.tlen = intswap(sizeof(IPLAYER)+sizeof(TCPLAYER));
  377.     t->i.ident = nnipident++;
  378.     t->i.ttl = 30;
  379.     t->i.check = 0;
  380.     t->i.check = ipcheck(&t->i,10);
  381.  
  382.     movebytes(t->d.dest,t->d.me,DADDLEN);    /* data link address */
  383.     movebytes(t->d.me,blankd.me,DADDLEN);    /* my address */
  384.     
  385.     return(dlayersend(t,sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(TCPLAYER)));
  386.  
  387. }
  388.  
  389. /***************************************************************************/
  390. /*  tcpsend
  391. *     transmits a TCP packet.  
  392. *
  393. *   For IP:
  394. *      sets ident,check,totallen
  395. *   For TCP:
  396. *      sets seq and window from port information,
  397. *        fills in the pseudo header and computes the checksum.
  398. *      Assumes that all fields not filled in here are filled in by the
  399. *      calling proc or were filled in by makeport(). 
  400. *      (see all inits in protinit)
  401. *
  402. */
  403. tcpsend(pport,dlen)
  404.     int dlen;
  405.     struct port *pport;
  406.     {
  407.     struct port *p;
  408.  
  409.     p = pport;
  410.  
  411.     if (p == NULL) {
  412.         netposterr(404);
  413.         return(-1);
  414.     }
  415.  
  416. /*
  417. *  do IP header first
  418. */
  419.     p->tcpout.i.ident = intswap(nnipident++);
  420.     p->tcpout.i.tlen = intswap(sizeof(struct iph)+sizeof(struct tcph) + dlen);
  421.     p->tcpout.i.check = 0;                /* install checksum */
  422.     p->tcpout.i.check = ipcheck(&p->tcpout.i,10);
  423. /*
  424. *  do TCP header
  425. */
  426.     p->tcpout.t.seq = longswap(p->out.nxt);            /* bytes swapped */
  427.  
  428. /*
  429. *  if the port has some credit limit, use it instead of large
  430. *  window buffer.  Generally demanded by hardware limitations.
  431. */
  432.     if (p->credit < p->in.size)
  433.         p->tcpout.t.window = intswap(p->credit);
  434.     else
  435.         p->tcpout.t.window = intswap(p->in.size);    /* window size */
  436.  
  437. /*
  438. *  prepare pseudo-header for checksum
  439. */
  440.     p->tcps.tcplen = intswap(dlen+sizeof(TCPLAYER));
  441.     p->tcpout.t.check = 0;
  442.     p->tcpout.t.check = tcpcheck(&p->tcps,&p->tcpout.t,dlen+sizeof(struct tcph));
  443.  
  444.     p->out.lasttime = time(NULL);
  445.  
  446.     return(dlayersend(&p->tcpout,
  447.         sizeof(DLAYER)+sizeof(IPLAYER)+sizeof(TCPLAYER)+dlen));
  448.  
  449. }
  450.  
  451. /***************************************************************************/
  452. /*  ackcheck
  453. *   take an incoming packet and see if there is an ACK for the outgoing
  454. *   side.  Use that ACK to dequeue outgoing data.
  455. */
  456. ackcheck(p,t,pnum)
  457.     TCPKT *t;
  458.     struct port *p;
  459.     int pnum;
  460.     {
  461.     uint32 ak;
  462.     int32 rttl;
  463.     int i;
  464.  
  465.     if ((t->t.flags & TRESET) && (t->t.seq == p->tcpout.t.ack)) {
  466.         netposterr(405);
  467.         p->state = SCLOSED;
  468.         netputuev(CONCLASS,CONCLOSE,pnum);
  469.         return(1);
  470.     }
  471.  
  472.     if (!(t->t.flags & TACK))                /* check ACK flag */
  473.         return(1);                             /* if no ACK, no go */
  474.  
  475.     p->out.size = intswap(t->t.window);    /* allowable transmission size */
  476.  
  477. /*
  478. *  rmqueue any bytes which have been ACKed, update p->out.nxt to the
  479. *  new next seq number for outgoing.  Update send window.
  480. *
  481. */
  482.     ak = longswap(t->t.ack);            /* other side's ACK */
  483. /*
  484. *  Need to add code to check for wrap-around of sequence space
  485. *  for ak.  ak - p->out.ack may be affected by sequence wraparound.
  486. *  If you have good, efficient code for this, please send it to me.
  487. *
  488. *  If ak is not increasing (above p->out.nxt) then we should assume
  489. *  that it is a duplicate packet or one of those stupid keepalive
  490. *  packets that 4.2 sends out.
  491. */
  492.     if (ak > p->out.nxt) {
  493.         rmqueue(&p->out,(int)(ak - p->out.ack));    /* take off of queue */
  494.         p->out.nxt = ak;
  495.         p->out.ack = ak;
  496. /*
  497. *  Check to see if this acked our most recent transmission.  If so, adjust
  498. *  the RTO value to reflect the newly measured RTT.  This formula reduces
  499. *  the RTO value so that it gradually approaches the most recent round
  500. *  trip measurement.  When a packet is retransmitted, this value is
  501. *  doubled (exponential backoff).
  502. */
  503.         rttl = time(NULL) - p->out.lasttime;
  504.         if (!p->out.contain &&             /* just now emptied queue */
  505.             rttl < (long)(MAXRTO) && p->rto >= MINRTO) {
  506.             i = (int)(rttl);
  507.             i = ((p->rto-MINRTO)*3 + i + 1) >> 2;    /* smoothing function */
  508.             p->rto = i+MINRTO;
  509.         }
  510.  
  511.         if (p->out.size > 0)
  512.             p->out.lasttime = 0L;            /* forces xmit */
  513.         return(0);
  514.     }
  515.  
  516.     return(1);
  517.  
  518. }    
  519.  
  520. /***************************************************************************/
  521. /*  estab1986
  522. *   take a packet which has arrived for an established connection and
  523. *   put it where it belongs.
  524. */
  525. estab1986(prt,pkt,tlen,hlen)
  526.     struct port *prt;
  527.     TCPKT *pkt;
  528.     int tlen,hlen;
  529.     {
  530.     int dlen;
  531.     uint32 sq,want;
  532.  
  533.     dlen = tlen-hlen;
  534.     
  535. #ifdef OLDM
  536. /*  problem with old way is that dlen==0 is not checked to see if it is in
  537.   seq window */
  538.   
  539.     if (dlen <= 0) {                        /* only an ACK packet */
  540.         checkfin(prt,pkt);                    /* might still have FIN */
  541.         return(0);
  542.     }
  543. #endif
  544.  
  545. /*
  546. *  see if we want this packet, or is it a duplicate?
  547. */
  548.     sq = longswap(pkt->t.seq);
  549.     want = prt->in.nxt;
  550.  
  551.     if (sq != want) {                            /* we may want it, may not */
  552.  
  553.         if (sq < want && sq+dlen >= want) {      /* overlap */
  554.             hlen += want-sq;                    /* offset desired */
  555.             dlen -= want-sq;                    /* skip this much */
  556.         }
  557.         else {                                    /* tough it */
  558.             prt->out.lasttime = 0L;                /* make the ACK time out */
  559.             return(-1);
  560.         }
  561.     }
  562.     else if (dlen <= 0) {                        /* only an ACK packet */
  563.         checkfin(prt,pkt);                        /* might still have FIN */
  564.         return(0);
  565.     }
  566.  
  567. /*
  568. *  If we have room in the window, update the ACK field values
  569. */
  570.     if (prt->in.size >= dlen) {
  571.         prt->in.nxt += dlen;                /* new ack value */
  572.         prt->tcpout.t.ack = longswap(prt->in.nxt);
  573.         prt->in.size -= dlen;                /* new window size */
  574.  
  575.         prt->out.lasttime = 0L;                /* force timeout for ACK */
  576.  
  577.         enqueue(&prt->in,pkt->x.data+hlen-20,dlen);
  578.         netputuev(CONCLASS,CONDATA,pnum);    /* tell user about it */
  579.  
  580.         prt->in.lasttime = time(NULL);
  581.  
  582.     }
  583.  
  584.     else {                                    /* no room in input buffer */
  585.         prt->out.lasttime = 0L;                /* re-ack old sequence value */
  586.  
  587.     }
  588.  
  589. /* 
  590. *  Check the FIN bit to see if this connection is closing
  591. */
  592.     checkfin(prt,pkt);
  593.  
  594.     return(0);
  595. }
  596.  
  597. /***************************************************************************/
  598. /* checkfin
  599. *   Check the FIN bit of an incoming packet to see if the connection
  600. *   should be closing, ACK it if we need to.
  601. *   Half open connections immediately, automatically close.  We do
  602. *   not support them.  As soon as the incoming data is delivered, the
  603. *   connection will close.
  604. */
  605. checkfin(prt,pkt)
  606.     struct port *prt;
  607.     TCPKT *pkt;
  608.     {
  609.  
  610.     if (pkt->t.flags & TFIN) {        /* fin bit found */
  611.  
  612.         prt->in.nxt++;                /* count the FIN byte */
  613.         prt->state = SCWAIT;        /* close-wait */
  614.         prt->tcpout.t.ack = longswap(prt->in.nxt);    /* set ACK in packet */
  615.         prt->credit = 0;
  616.         prt->out.lasttime = 0L;        /* cause ACK to be sent */
  617.         netputuev(CONCLASS,CONCLOSE,pnum);
  618.  
  619. /*
  620. *   At this point, we know that we have received all data that the other
  621. *   side is allowed to send.  Some of that data may still be in the 
  622. *   incoming queue.  As soon as that queue empties, finish off the TCP
  623. *   close sequence.  We are not allowing the user to utilize a half-open
  624. *   connection, but we cannot close before the user has received all of
  625. *   the data from the incoming queue.
  626. */
  627.         if (!prt->in.contain) {                    /* data remaining? */
  628.             prt->tcpout.t.flags = TFIN | TACK;
  629.             tcpsend(prt,0);
  630.             prt->state = SLAST;
  631.         }
  632.     }
  633. }    
  634.  
  635.