home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / MISC / NETWORK / TEL23SRC.ZIP / ENGINE / TCP.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-06-29  |  18.1 KB  |  609 lines

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