home *** CD-ROM | disk | FTP | other *** search
- /* DEBUG flag may be set for my internal playing */
-
- /*
- #define DEBUG
- */
-
- /*
- * PCTCP - the true worker of Waterloo TCP
- * - contains all opens, closes, major read/write routines and
- * basic IP handler for incomming packets
- * - NOTE: much of the TCP/UDP/IP layering is done at the data structure
- * level, not in separate routines or tasks
- *
- */
-
-
- #include"capalloc.h"
- #include"capstdio.h"
- #include <copyright.h>
- #include <time.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <conio.h>
- #include <string.h>
- #include <mem.h>
- #include <dos.h>
- #include <values.h>
-
- #include "wattcp.h"
- #include "elib.h"
-
- static void udp_handler(in_Header *ip);
- static udp_write(udp_Socket *s, byte *datap, int len);
- static int udp_read(udp_Socket *s, byte *datap, int maxlen);
- static void tcp_Retransmitter(void);
-
-
- #define TCP_LOCAL 0x4000
-
- /* statics */
- static tcp_ProcessData(tcp_Socket *s, tcp_Header *tp, int len);
-
- static char far *mono = (char far *)0xb0000000L;
- static char far *colour = (char far *)0xb8000000L;
-
- static initialized = 0;
- static void (*system_yield)() = NULL;
- extern int multihomes;
- extern word _pktipofs;
- void (*_dbugxmit)() = NULL;
- void (*_dbugrecv)() = NULL;
- void (*wattcpd)() = NULL;
-
- char *_hostname = "012345678901234567890123456789012345678901234567890";
-
- word _mss = ETH_MSS;
-
- char *_wattcp = WATTCP_C;
-
- static void tcp_handler(in_Header *ip);
- static void udp_handler(in_Header *ip);
- extern void icmp_handler(in_Header *ip );
-
- static void tcp_unthread(tcp_Socket *ds);
- static void tcp_abort(tcp_Socket *s);
- void tcp_sendsoon(tcp_Socket *s );
- static tcp_send(tcp_Socket *s, int line);
- tcp_rst( in_Header *his_ip, tcp_Header *oldtcpp);
- static udp_close(udp_Socket *ds);
-
-
- /*
- * sock_yield - enable user defined yield function
- */
- sock_yield( tcp_Socket *s, void (*fn)())
- {
- if ( s )
- s->usr_yield = fn;
- else
- system_yield = fn;
- return( 0 );
- }
-
- /*
- * sock_mode - set binary or ascii - affects sock_gets, sock_dataready
- * - set udp checksums
- */
- word sock_mode( sock_type *s, word mode )
- {
- return( s->tcp.sock_mode = (s->tcp.sock_mode & 0xfffc) | mode);
- }
-
- /*
- * ip user level timer stuff
- * void ip_timer_init( void *s, int delayseconds )
- * int ip_timer_expired( void *s )
- * - 0 if not expired
- */
- static unsigned long far *realclock = (unsigned long far *)0x000046cL;
- #define MAXTICKS 0x1800b0L
-
- void ip_timer_init( udp_Socket *s , int delayseconds )
- {
- if (delayseconds)
- s->usertimer = set_timeout( delayseconds );
- else
- s->usertimer = 0;
- }
-
- int ip_timer_expired( udp_Socket *s )
- {
- if (! s->usertimer) /* cannot expire */
- return( 0 );
- return( chk_timeout( s->usertimer));
- }
- longword MsecClock()
- {
- return( (*realclock) * 055L);
- }
- static long make_timeout( word timeout )
- {
- if ( timeout ) return( set_timeout( timeout ));
- return( 0 );
- }
- /*
- * check_timeout - test agains timeout clock - account for overflow
- */
- static int check_timeout( unsigned long timeout )
- {
- if (timeout) return( chk_timeout( timeout ));
- return( 0 );
- }
-
- /*
- * Local IP address
- */
- longword my_ip_addr = 0L; /* for external references */
- longword sin_mask = 0xfffffe00L;
- longword sin_gate = 0x0;
-
-
- /*
- * IP identification numbers
- */
-
- static int ip_id = 0; /* packet number */
- static int next_tcp_port = 1024; /* auto incremented */
- static int next_udp_port = 1024;
- static tcp_Socket *tcp_allsocs = NULL;
- static udp_Socket *udp_allsocs = NULL;
-
- /* Timer definitions */
- #define RETRAN_STRAT_TIME 1 /* in ticks - how often do we check retransmitter tables*/
- #define tcp_RETRANSMITTIME 3 /* interval at which retransmitter is called */
- #define tcp_LONGTIMEOUT 31 /* timeout for opens */
- #define tcp_TIMEOUT 13 /* timeout during a connection */
-
- #ifndef RELEASE
- word debug_on = 0;
- #endif /* RELEASE */
-
- /*
- * look for bugs
- */
- tcp_checkfor( tcp_Socket *t )
- {
- tcp_Socket *p;
-
- for ( p = tcp_allsocs ; p ; p = p->next )
- if ( p == t ) return( 1 );
- return( 0 );
- }
-
- /*
- * Shut down the card and all services
- */
- void tcp_shutdown()
- {
- while (tcp_allsocs)
- tcp_abort( tcp_allsocs );
- _eth_release();
- initialized = 0;
- }
-
- /*
- * tcp_Init - Initialize the tcp implementation
- * - may be called more than once without hurting
- */
- void tcp_init()
- {
- extern int _arp_last_gateway;
- extern int _last_nameserver;
-
- if (!initialized) {
- /* initialize ethernet interface */
- initialized = 1;
- _eth_init();
-
- /* reset the various tables */
- _arp_last_gateway = 0; /* reset the gateway table */
- _last_nameserver = 0; /* reset the nameserver table */
- _last_cookie = 0; /* eat all remaining crumbs */
- *_hostname = 0; /* reset the host's name */
-
- if (!my_ip_addr) {
- /* using our local reverse ethernet address thingamajig */
- movmem( &_eth_addr[2], &my_ip_addr, 4 );
- }
- _eth_free( 0 );
- next_udp_port = next_tcp_port = 1024 + ((*realclock >> 7 )& 0x1ff);
- }
- }
-
- /*
- * Checks for bugs when compiling in large model C compiler
- *
- * Borland C uses a 4K stack by default. In all memory models the
- * stack grows down toward the heap.
- *
- * If you accidentally place tcp_Socket onto the stack (like by making
- * it an automatic variable), then you will have already used up that
- * whole 4K and then some!
- *
- * In large model, this will mess up the data space in a major way
- * because the stack starts at SS:_stklen, or SS:1000, so you will
- * wrap the SP pointer back around to FFFE and start writing over
- * the far heap. Yuck.
- *
- * In small model it usually doesn't kill your application because
- * you would have to be down to your last 4K of memory and this is
- * not as common.
- *
- * The solutions: declare your sockets as static, or put them on the
- * heap, or bump up your stack size by using the global special variable:
- *
- * unsigned _stklen = 16536; // set stack to 16 k
- */
- static void largecheck( void *s, int size )
- {
- #ifdef __TURBOC__
- if ( (word)(FP_OFF(s)) > (word)(-size)) {
- outs("ERROR: user stack size error\n");
- sleep(5);
- exit( 3 );
- }
- #endif
- }
-
- /*
- * findfreeport - return unused local port
- * - oldport = 0:normal port, 1:special port (513-1023)
- * - we need not be this picky, but it doesn't hurt
- */
- static word findfreeport( word oldport )
- {
- word temp;
- tcp_Socket *s;
-
- if (( oldport > 0 ) && (oldport < 0xffff))
- return( oldport );
-
- if ( oldport == 0 ) oldport = 1025;
- else oldport = 513;
-
- for ( temp = oldport ; temp < oldport + 510 ; ++temp ) {
- if (( s = (tcp_Socket*)udp_allsocs) != NULL ) {
- while ( s->next && (s->myport != temp))
- s = (tcp_Socket*)s->next;
- if ( s->myport == temp ) continue;
- }
- if ( (s = tcp_allsocs ) != NULL ) {
- while ( s->next && (s->myport != temp ))
- s = s->next;
- if ( s->myport == temp ) continue;
- }
- break;
- }
- return( temp );
- }
-
- /* socket, localport, destaddress */
- int udp_open(udp_Socket *s, word lport, longword ina, word port, procref datahandler)
- {
- udp_close( s );
- largecheck( s, sizeof( udp_Socket ));
- memset( s, 0, sizeof( udp_Socket ));
- s->rdata = s->rddata;
- s->maxrdatalen = tcp_MaxBufSize;
- s->ip_type = UDP_PROTO;
-
- lport = findfreeport(lport);
-
- s->myport = lport;
- s->myaddr = my_ip_addr;
-
- /* check for broadcast */
- if ( (long)(ina) == -1 || !ina )
- memset( s->hisethaddr, 0xff, sizeof( eth_address ));
- else if ( ! _arp_resolve(ina, (eth_address *) &s->hisethaddr[0], 0) )
- return( 0 );
-
- s->hisaddr = ina;
- s->hisport = port;
- s->dataHandler = datahandler;
- s->usr_yield = system_yield;
- s->safetysig = SAFETYUDP;
- s->next = udp_allsocs;
- udp_allsocs = s;
- return( 1 );
- }
-
- /*
- * Actively open a TCP connection to a particular destination.
- * - 0 on error
- */
- int tcp_open(tcp_Socket *s, word lport, longword ina, word port, procref datahandler)
- {
- largecheck( s, sizeof( tcp_Socket )); /* stack space warnings */
- tcp_unthread(s); /* just in case not totally closed */
-
- memset( s, 0, sizeof( tcp_Socket));
- s->rdata = s->rddata;
- s->maxrdatalen = tcp_MaxBufSize;
- s->ip_type = TCP_PROTO;
- s->mss = _mss;
- s->state = tcp_StateSYNSENT;
- s->timeout = set_timeout( tcp_LONGTIMEOUT );
- s->cwindow = 1;
- s->wwindow = 0; /* slow start VJ algorithm */
- s->vj_sa = 4; /* about 250 ms */
- lport = findfreeport( lport ); /* get a nonzero port val */
- s->myaddr = my_ip_addr;
- s->myport = lport;
-
- if ( ina - my_ip_addr <= multihomes ) return( 0 );
- if ( ! _arp_resolve(ina, (eth_address *) &s->hisethaddr[0], 0) )
- return( 0 );
-
- s->hisaddr = ina;
- s->hisport = port;
- s->seqnum = intel( set_timeout( 1 )) & 0xffff0000;
- s->datalen = 0;
- s->flags = tcp_FlagSYN;
- s->unhappy = true;
- s->dataHandler = datahandler;
- s->usr_yield = system_yield;
-
- s->safetysig = SAFETYTCP; /* insert into chain */
- s->next = tcp_allsocs;
- tcp_allsocs = s;
-
- s->rtt_delay = s->rtt_smooth = 18; /* one second startup */
- tcp_send(s, __LINE__ );
- s->rtt_time = set_timeout( 1 );
- return( 1 );
- }
-
- /*
- * Passive open: listen for a connection on a particular port
- */
- int tcp_listen(tcp_Socket *s, word lport, longword ina, word port, procref datahandler, word timeout)
- {
- largecheck( s, sizeof( tcp_Socket ));
- tcp_unthread(s); /* just in case not totally closed */
- memset( s, 0, sizeof( tcp_Socket));
- s->rdata = s->rddata;
- s->maxrdatalen = tcp_MaxBufSize;
- s->ip_type = TCP_PROTO;
- s->mss = _mss;
- s->cwindow = 1;
- s->wwindow = 0; /* slow start VJ algorithm */
- s->vj_sa = 36; /* about 250 ms */
-
- s->state = tcp_StateLISTEN;
- if ( !timeout ) s->timeout = 0; /* forever... */
- else s->timeout = set_timeout( timeout );
- lport = findfreeport( lport ); /* get a nonzero port val */
- s->myport = lport;
- s->hisport = port;
- s->hisaddr = ina;
- s->seqnum = intel( (word)(s));
- s->datalen = 0;
- s->flags = 0;
- s->unhappy = false;
- s->dataHandler = datahandler;
- s->usr_yield = system_yield;
-
- s->safetysig = SAFETYTCP; /* insert into chain */
- s->next = tcp_allsocs;
- tcp_allsocs = s;
-
- return( 1 );
- }
-
-
- static udp_close(udp_Socket *ds)
- {
- udp_Socket *s, **sp;
-
- sp = &udp_allsocs;
- for (;;) {
- s = *sp;
- if ( s == ds ) {
- *sp = s->next;
- break;
- }
- if ( !s ) break;
- if ( ! s->err_msg ) s->err_msg = "UDP Close called";
- sp = &s->next;
- }
- return( 0 );
- }
-
- /*
- * Send a FIN on a particular port -- only works if it is open
- * Must still allow receives
- */
- static void tcp_close(tcp_Socket *s)
- {
- if ( s->ip_type != TCP_PROTO )
- return;
- if ( s->state == tcp_StateESTAB ||
- s->state == tcp_StateESTCL ||
- s->state == tcp_StateSYNREC ) {
-
- if ( s->datalen ) { /* must first flush all data */
- s->flags |= tcp_FlagPUSH | tcp_FlagACK;
- if ( s->state < tcp_StateESTCL ) {
- s->state = tcp_StateESTCL;
- tcp_sendsoon( s );
- }
- } else { /* really closing */
- s->flags = tcp_FlagACK | tcp_FlagFIN;
- if (!s->err_msg)
- s->err_msg = "Connection closed normally";
- s->state = tcp_StateFINWT1;
- s->timeout = set_timeout( 4 ); /* should be a pretty lengthy time */
- tcp_send( s, __LINE__ );
- }
- s->unhappy = true;
- } else if (s->state == tcp_StateCLOSWT ) {
- /* need to ack the fin and get on with it */
- s->state = tcp_StateLASTACK;
- s->flags |= tcp_FlagFIN;
- tcp_send( s, __LINE__ );
- s->unhappy = true;
- }
- }
-
- /*
- * Abort a tcp connection
- */
- static void tcp_abort(tcp_Socket *s)
- {
- if (!s->err_msg) s->err_msg = "TCP_ABORT";
- if ( s->state != tcp_StateLISTEN && s->state != tcp_StateCLOSED ) {
- s->flags = tcp_FlagRST | tcp_FlagACK ;
- s->unhappy = true;
- tcp_send(s, __LINE__);
- }
- s->unhappy = false;
- s->datalen = 0;
- s->ip_type = 0;
- s->state = tcp_StateCLOSED;
- /* if (s->dataHandler) s->dataHandler(s, 0, -1); */
- tcp_unthread(s);
- }
-
- void sock_abort(tcp_Socket *s )
- {
- if ( s->ip_type == TCP_PROTO )
- tcp_abort( s );
- else
- udp_close( (udp_Socket *)s );
- }
- /*
- * tcp_sendsoon - schedule a transmission pretty soon
- * - this one has an imperfection at midnight, but it
- * is not significant to the connection performance
- */
- void tcp_sendsoon(tcp_Socket *s )
- {
- longword temp;
- if (s->ip_type == TCP_PROTO ) {
- temp = set_ttimeout( 1 );
- if ( temp == s->rtt_time && s->rto < 2 && s->recent == 0 ) {
- s->karn_count = 0;
- tcp_send( s, __LINE__ );
- s->recent = 1;
- return;
- }
- if ((s->unhappy || s->datalen > 0 || s->karn_count == 1)
- && (s->rtt_time < temp ))
- return;
-
- s->rtt_time = set_ttimeout( 1 + (s->rto >> 4) );
- s->karn_count = 1;
- }
- }
-
- /*
- * Retransmitter - called periodically to perform tcp retransmissions
- */
- static longword retran_strat = 0L; /* timeout retran strategy */
- static void tcp_Retransmitter(void)
- {
- tcp_Socket *s;
-
- /* only do this once per RETRAN_STRAT_TIME milliseconds */
- if ( !chk_timeout( retran_strat ))
- return;
- retran_strat = set_ttimeout( RETRAN_STRAT_TIME );
-
- for ( s = tcp_allsocs; s; s = s->next ) {
- if ( s->datalen > 0 || s->unhappy || s->karn_count == 1 ) {
- /* retransmission strategy */
- if ( chk_timeout( s->rtt_time)) {
-
- #ifdef DEBUG
- #ifndef RELEASE
- if(debug_on >1) printf("regular retran TO set unacked back to 0 from %u\n", s->unacked);
- #endif /* RELEASE */
- #endif DEBUG
- /* strategy handles closed windows J.D. + E.E. */
- if (s->window == 0 && s->karn_count == 2)
- s->window = 1;
-
- if ( s->karn_count == 0 ) {
- /* if really did timeout */
-
-
- s->karn_count = 2;
-
- s->unacked = 0;
- /* use the backed off rto - implied, no code necessary */
- /* reduce the transmit window */
- s->cwindow = ((s->cwindow + 1) * 3) >> 2;
- s->wwindow = 0;
- }
- if (s->datalen)
- s->flags |= tcp_FlagPUSH | tcp_FlagACK;
- tcp_send(s, __LINE__);
- }
- }
- /* handle inactive tcp timeouts */
- if ( sock_inactive && s->inactive_to ) {
- if ( chk_timeout( s->inactive_to)) {
- /* this baby has timed out */
- s->err_msg = "Connection timed out - no activity";
- sock_close( (sock_type *) s );
- }
- }
- if ( s->timeout && chk_timeout( s->timeout)) {
- if ( s->state == tcp_StateTIMEWT ) {
- s->state = tcp_StateCLOSED;
- tcp_unthread(s);
- break;
- } else if (s->state != tcp_StateESTAB && s->state != tcp_StateESTCL ) {
- s->err_msg = "Timeout, aborting";
- tcp_abort(s);
- break;
- }
- }
- }
- /* do our various daemons */
- if ( wattcpd ) (*wattcpd)();
- }
-
-
- /*
- * Unthread a socket from the tcp socket list, if it's there
- */
- static void tcp_unthread(tcp_Socket *ds)
- {
- tcp_Socket *s, **sp;
-
- if (!ds->rdatalen || (ds->state > tcp_StateESTCL))
- ds->ip_type = 0; /* fail io */
- ds->state = tcp_StateCLOSED; /* tcp_tick needs this */
- sp = &tcp_allsocs;
- for (;;) {
- s = *sp;
- if ( s == ds ) {
- *sp = s->next;
- continue; /* unthread multiple copies if necessary */
- }
- if ( !s ) break;
- sp = &s->next;
- }
- }
-
- /*
- * tcp_tick - called periodically by user application
- * - returns 1 when our socket closes
- * - called with socket parameter or NULL
- */
- tcp_tick( sock_type *s )
- {
- in_Header *ip;
- static longword timeout = 0;
- static longword start = 0;
-
- int x;
- int packettype;
-
- /* finish off dead sockets */
- if ( s ) {
- if (( s->tcp.ip_type == TCP_PROTO ) &&
- ( s->tcp.state == tcp_StateCLOSED ) &&
- ( s->tcp.rdatalen == 0 )) {
- tcp_unthread( & s->tcp );
- s->tcp.ip_type = 0;
- }
- }
-
-
- /* plan our next retransmit */
-
- if ( !timeout )
- timeout = make_timeout( tcp_RETRANSMITTIME );
-
- while ( ip = (in_Header *)_eth_arrived( (word *) &packettype ) ) {
- start = *realclock;
-
- switch ( packettype ) {
- case /*0x800*/ 0x008 :
- /* do IP */
- if ( checksum(ip, in_GetHdrlenBytes(ip)) == 0xffff ) {
- switch ( ip->proto ) {
- case TCP_PROTO :
- tcp_handler(ip);
- break;
- case UDP_PROTO :
- udp_handler(ip);
- break;
- case ICMP_PROTO :
- icmp_handler(ip);
- break;
- }
- } else {
- #ifndef RELEASE
- if (debug_on)
- outs("IP Received BAD Checksum \n");
- #endif /* RELEASE */
- }
- break;
- case /*0x806*/ 0x608 :
- /* do arp */
- _arp_handler(ip);
- break;
- }
- if (ip) _eth_free(ip);
-
- continue;
- }
- /* check for our outstanding packets */
- tcp_Retransmitter();
-
- return( s->udp.ip_type );
- }
-
- #ifndef RELEASE
- void tcp_set_debug_state( x )
- int x;
- {
- debug_on = x;
- }
- #endif /* RELEASE */
-
- /* returns 1 if connection is established */
- int tcp_established(tcp_Socket *s)
- {
- return( s->state >= tcp_StateESTAB );
- }
-
-
- static udp_write(udp_Socket *s, byte *datap, int len)
- {
- tcp_PseudoHeader ph;
- struct _pkt {
- in_Header in;
- udp_Header udp;
- int data;
- /* longword maxsegopt; */
- } *pkt;
- byte *dp;
- in_Header *inp;
- udp_Header *udpp;
-
- pkt = (struct _pkt *)_eth_formatpacket(&s->hisethaddr[0], /*0x800*/ 8);
- dp = (byte *) &pkt->data;
- inp = &pkt->in;
- udpp = &pkt->udp;
-
- memset( inp, 0, sizeof( in_Header ));
- inp->length = intel16( sizeof(in_Header) + UDP_LENGTH + len );
-
- /* udp header */
- udpp->srcPort = intel16( s->myport );
- udpp->dstPort = intel16( s->hisport );
- udpp->checksum = 0;
- udpp->length = intel16( UDP_LENGTH + len );
- movmem(datap, dp, len );
-
- /* internet header */
- inp->ver = 4;
- inp->hdrlen = 5;
- inp->tos = 0;
- /* inp->vht = 0x4500;*/ /* version 4, hdrlen 5, tos 0 */
- inp->identification = intel16( ++ip_id ); /* was post inc */
- // inp->frag = 0;
- inp->ttl = 254;
- inp->proto = UDP_PROTO; /* udp */
- /* inp->ttlProtocol = (250<<8) + 6; */
- inp->checksum = 0;
- inp->source = intel( s->myaddr );
- inp->destination = intel( s->hisaddr );
- inp->checksum = ~checksum( inp, sizeof(in_Header));
-
-
- /* compute udp checksum if desired */
- if ( s->sock_mode & UDP_MODE_NOCHK )
- udpp->checksum = 0;
- else {
- ph.src = inp->source; /* already INTELled */
- ph.dst = inp->destination;
- ph.mbz = 0;
- ph.protocol = UDP_PROTO; /* udp */
- ph.length = udpp->length; /* already INTELled */
-
- ph.checksum = checksum(&pkt->udp, intel16(ph.length));
- udpp->checksum = ~checksum(&ph, sizeof(ph));
- }
-
- if (_dbugxmit) (*_dbugxmit)(s,inp,udpp,0);
- _eth_send( intel16( inp->length ));
-
- return ( len );
- }
-
- /*
- * udp_read - read data from buffer, does large buffering
- */
- static int udp_read(udp_Socket *s, byte *datap, int maxlen)
- {
- int x;
-
- if (maxlen < 0) maxlen = MAXINT;
- if (( x = s->rdatalen ) > 0) {
- if ( x > maxlen ) x = maxlen;
- if ( x > 0 ) {
- if (datap) movmem( s->rdata, datap, x );
- if ( s->rdatalen -= x )
- movmem( s->rdata + x, s->rdata, s->rdatalen);
- }
- }
- return( x );
- }
-
- void _udp_cancel( in_Header *ip )
- {
- int len;
- udp_Header *up;
- udp_Socket *s;
-
- /* match to a udp socket */
- len = in_GetHdrlenBytes(ip);
- up = (udp_Header *)((byte *)ip + len); /* udp frame pointer */
-
- /* demux to active sockets */
- for ( s = udp_allsocs; s; s = s->next )
- if ( s->hisport != 0 &&
- intel16( up->dstPort ) == s->hisport &&
- intel16( up->srcPort ) == s->myport &&
- intel( ip->destination ) == s->hisaddr ) break;
- if ( !s ) {
- /* demux to passive sockets */
- for ( s = udp_allsocs; s; s = s->next )
- if ( s->hisport == 0 && intel16( up->dstPort ) == s->myport ) break;
- }
- if (s) {
- s->rdatalen = 0;
- s->ip_type = 0;
- }
- }
-
- void *_tcp_lookup( longword hisip, word hisport, word myport )
- {
- tcp_Socket *s;
- for ( s = tcp_allsocs; s; s = s->next ) {
- if ( ( myport == s->myport ) && /* always unique under WATTCP */
- ( hisport == s->hisport ) &&
- ( hisip == s->hisaddr ))
- return( s );
- }
- return( NULL );
- }
-
-
- void _tcp_cancel(in_Header *ip, int code, char *msg, longword dummyip )
- {
- int len;
- tcp_Socket *s;
- tcp_Header *tp;
-
- len = in_GetHdrlenBytes(ip); /* check work */
-
- tp = (tcp_Header *)((byte *)ip + len); /* tcp frame pointer */
-
- /* demux to active sockets */
- for ( s = tcp_allsocs; s; s = s->next ) {
- if ( intel16( tp->srcPort) == s->myport &&
- intel16( tp->dstPort ) == s->hisport &&
- intel( ip->destination ) == s->hisaddr ) {
- switch (code) {
- /* halt it */
- case 1 : s->err_msg = (msg) ?
- msg : "ICMP closed connection";
- s->rdatalen = s->datalen = 0;
- s->unhappy = false;
- tcp_abort( s );
- /* if (s->dataHandler) s->dataHandler(s, 0, -1); */
- break;
- /* slow it down */
- case 2 : s->cwindow = 1;
- s->wwindow = 1;
- s->rto <<= 2;
- s->vj_sa <<= 2;
- s->vj_sd <<= 2;
- break;
- /* icmp redirect for host */
- case 5 : /* save his NEW network address */
- _arp_resolve(dummyip, &s->hisethaddr[0], 0);
- break;
- }
-
- }
- }
- }
-
- static int tcp_read(tcp_Socket *s, byte *datap, int maxlen)
- {
- int x;
-
- if (maxlen < 0 ) maxlen = MAXINT;
- if (( x = s->rdatalen) > 0) {
- if ( x > maxlen ) x = maxlen;
- if ( x > 0 ) {
- if (datap) movmem( s->rdata, datap, x );
- if (( s->rdatalen -= x ) > 0 ) {
- movmem( s->rdata + x, s->rdata, s->rdatalen );
- tcp_sendsoon( s ); /* update the window */
- } else
- tcp_send( s, __LINE__ ); /* update window el-pronto */
- }
- } else if ( s->state == tcp_StateCLOSWT )
- tcp_close( s );
- return( x );
- }
-
- /*
- * Write data to a connection.
- * Returns number of bytes written, == 0 when connection is not in
- * established state.
- */
- static tcp_write(tcp_Socket *s, byte *dp, int len)
- {
- int x;
-
- if (len < 0 ) len = MAXINT;
- /* no longer uses tcp_MaxData */
- if ( s->state != tcp_StateESTAB ) len = 0;
- if ( len > (x = s->maxrdatalen - s->datalen) ) len = x;
- if ( len > 0 ) {
- movmem( dp, s->data + s->datalen, len );
-
- s->datalen += len;
- s->unhappy = true; /* redundant because we have outstanding data */
-
- if ( s->sock_mode & TCP_LOCAL )
- s->sock_mode &= ~TCP_LOCAL;
- else {
- if ( s->sock_mode & TCP_MODE_NONAGLE ) {
- tcp_send( s, __LINE__ );
- } else {
- /* transmit if first data or reached MTU */
- /* not true MTU, but better than nothing */
- if (( s->datalen == len ) || ( s->datalen > (s->mss)/2 ))
- tcp_send( s, __LINE__ );
- else
- tcp_sendsoon( s );
- }
- }
- }
-
- return ( len );
- }
-
- /*
- * Send pending data
- */
- static void tcp_Flush(tcp_Socket *s)
- {
- if ( s->datalen > 0 ) {
- s->flags |= tcp_FlagPUSH;
- tcp_send(s, __LINE__);
- }
- }
-
- /*
- * Handler for incoming packets.
- */
- static void udp_handler(in_Header *ip)
- {
- udp_Header *up;
- tcp_PseudoHeader ph;
- word len;
- byte *dp;
- longword temp;
- udp_Socket *s;
-
- temp = intel( ip->destination );
-
- if ( ((~temp & ~sin_mask) != 0) && /* not a broadcast packet*/
- ((( temp - my_ip_addr) > multihomes ) /* not my address */
- && my_ip_addr)) /* and I know my address */
- return;
-
-
- len = in_GetHdrlenBytes(ip);
- up = (udp_Header *)((byte *)ip + len); /* udp segment pointer */
- len = intel16( up->length );
-
- /* demux to active sockets */
- for ( s = udp_allsocs; s; s = s->next ) {
- if ( s->safetysig != SAFETYUDP ) {
- #ifndef RELEASE
- if (debug_on) outs("chain error in udp\n");
- #endif /* RELEASE */
- }
- if ( s->hisport != 0 &&
- intel16( up->dstPort ) == s->myport &&
- intel16( up->srcPort ) == s->hisport &&
- intel( ip->destination ) == s->myaddr &&
- intel( ip->source ) == s->hisaddr ) break;
- }
- if (_dbugrecv) (*_dbugrecv)(s,ip,up,0);
- if ( !s ) {
- /* demux to passive sockets */
- for ( s = udp_allsocs; s; s = s->next )
- if ( ((s->hisaddr == 0) || (s->hisaddr == 0xffffffff))
- && intel16( up->dstPort ) == s->myport ) {
- s->hisaddr = intel( ip->source );
- s->hisport = intel16( up->srcPort );
- _arp_resolve(intel(ip->source), &s->hisethaddr[0], 0);
- // take on value of expected destination unless it
- // is broadcast
- if ( ip->destination != 0xffffffffL )
- s->myaddr = intel( ip->destination );
- break;
- }
- }
- if ( !s ) {
- /* demux to broadcast sockets */
- for ( s = udp_allsocs; s; s = s->next )
- if ( (s->hisaddr == 0xffffffff) &&
- (intel16( up->dstPort ) == s->myport )) break;
- }
-
- if ( !s ) {
- #ifndef RELEASE
- if (debug_on) outs("discarding...\n");
- #endif /* RELEASE */
- return;
- }
-
- if ( up->checksum ) {
- ph.src = ip->source; /* already INTELled */
- ph.dst = ip->destination;
- ph.mbz = 0;
- ph.protocol = UDP_PROTO;
- ph.length = up->length;
- ph.checksum = checksum(up, len);
- if (checksum(&ph, sizeof( tcp_PseudoHeader)) != 0xffff)
- return;
- }
-
- /* process user data */
- if ( (len -= UDP_LENGTH ) > 0) {
- dp = (byte *)( up );
- if (s->dataHandler) s->dataHandler( s, &dp[ UDP_LENGTH ], len , &ph, up);
- else {
- if (len > s->maxrdatalen ) len = s->maxrdatalen;
- movmem( &dp[ UDP_LENGTH ], s->rdata, len );
- s->rdatalen = len;
- }
- }
- }
-
- static void tcp_handler(in_Header *ip)
- {
- tcp_Header *tp;
- tcp_PseudoHeader ph;
- int len;
- byte *dp;
- int diff;
- tcp_Socket *s;
- word flags;
- long diffticks, ldiff; /* must be signed */
- long scheduleto;
-
-
- if ( (longword)(intel( ip->destination ) - my_ip_addr) > multihomes )
- return;
-
- len = in_GetHdrlenBytes(ip);
- len = intel16( ip->length ) - len; /* len of tcp data */
-
- len = in_GetHdrlenBytes(ip);
- tp = (tcp_Header *)((byte *)ip + len); /* tcp frame pointer */
- len = intel16( ip->length ) - len; /* len of tcp data */
- flags = intel16( tp->flags );
-
- #ifndef RELEASE
- if (debug_on > 1) {
- mono[160]++;
- colour[160]++;
- mono[162] = colour[162] = (flags & tcp_FlagSYN) ? 'S' : ' ';
- mono[164] = colour[164] = (flags & tcp_FlagACK) ? 'A' : ' ';
- mono[166] = colour[166] = (flags & tcp_FlagFIN) ? 'F' : ' ';
- mono[168] = colour[168] = (flags & tcp_FlagRST) ? 'R' : ' ';
- }
- #endif /* RELEASE */
-
- /* demux to active sockets */
- for ( s = tcp_allsocs; s; s = s->next ) {
- if ( s->safetysig != SAFETYTCP ) {
- #ifndef RELEASE
- if (debug_on) outs("chain error in tcp\n");
- #endif /* RELEASE */
- }
- if ( s->hisport != 0 &&
- intel16( tp->dstPort ) == s->myport &&
- intel16( tp->srcPort ) == s->hisport &&
- intel( ip->destination ) == s->myaddr &&
- intel( ip->source ) == s->hisaddr ) break;
- }
- if ( !s && (flags & tcp_FlagSYN)) {
- /* demux to passive sockets, must be a new session */
- for ( s = tcp_allsocs; s; s = s->next )
- if ((s->hisport == 0) && (intel16( tp->dstPort ) == s->myport )) {
- s->myaddr = intel( ip->destination );
- break;
- }
- }
-
-
- if (_dbugrecv) (*_dbugrecv)(s,ip,tp,0);
- if ( !s ) {
- if (!(flags & tcp_FlagRST)) tcp_rst( ip, tp );
- return;
- }
-
- ph.src = ip->source; /* already INTELled */
- ph.dst = ip->destination;
- ph.mbz = 0;
- ph.protocol = TCP_PROTO;
- ph.length = intel16( len );
- ph.checksum = checksum(tp, len);
- if ( checksum(&ph, sizeof(ph)) != 0xffff ) {
- #ifndef RELEASE
- if (debug_on) outs("bad tcp checksum \n");
- #endif /* RELEASE */
-
- /* tester */
- ph.checksum = checksum(tp, len);
- checksum(&ph, sizeof(ph));
-
- tcp_sendsoon( s );
- return;
- }
-
- /* reset code */
- if ( flags & tcp_FlagRST ) {
- #ifndef RELEASE
- if (debug_on) outs("\7\7\7\7\7\7\7connection reset\n");
- #endif /* RELEASE */
- s->rdatalen = s->datalen = 0;
- s->err_msg = "Remote reset connection";
- s->state = tcp_StateCLOSED;
- /* if (s->dataHandler) s->dataHandler(s, 0, -1); */
- tcp_unthread(s);
- return;
- }
-
- if ( sock_inactive )
- s->inactive_to = set_timeout( sock_inactive );
-
-
- /* update our retransmission stuff */
- /* new algorithms */
- if (s->karn_count == 2) {
- s->karn_count = 0;
- #ifdef DEBUG
- #ifndef RELEASE
- if (debug_on > 1 ) printf("finally got it safely zapped from %u to ????\n",s->unacked);
- #endif /* RELEASE */
- #endif /* DEBUG */
- } else {
- if ( s->vj_last ) {
- /* unnecessary to use unhappy || s->datalen ) */
- if ((diffticks = set_ttimeout( 0 ) - s->vj_last) >= 0 ) {
- /* we ignore the overnight case */
- diffticks -= (longword)( s->vj_sa >> 3 );
- s->vj_sa += (int)diffticks;
- if (diffticks < 0)
- diffticks = - diffticks;
- diffticks -= (s->vj_sd >> 2);
- s->vj_sd += (int)diffticks;
- if (s->vj_sa > MAXVJSA) s->vj_sa = MAXVJSA;
- if (s->vj_sd > MAXVJSD) s->vj_sd = MAXVJSD;
- }
- /* only recompute rtt hence rto after success */
- s->rto = 1 + ((s->vj_sa >> 2) + (s->vj_sd)) >> 1 ;
- #ifdef DEBUG
- #ifndef RELEASE
- if (debug_on > 1 ) printf("rto %u sa %u sd %u cwindow %u wwindow %u unacked %u\n",
- s->rto, s->vj_sa, s->vj_sd, s->cwindow, s->wwindow, s->unacked );
- #endif /* RELEASE */
- #endif /* DEBUG */
- }
- s->karn_count = 0;
- if ( s->wwindow++ >= s->cwindow ) {
- s->cwindow++;
- s->wwindow = 0;
- }
- }
- /* all new */
- scheduleto = set_ttimeout( s->rto + 2 );
- if ( s->rtt_time < scheduleto ) s->rtt_time = scheduleto;
-
-
- switch ( s->state ) {
-
- case tcp_StateLISTEN: /* accepting SYNs */
- /* save his ethernet address */
- if ( _pktipofs )
- movmem(&((((eth_Header *)ip) - 1)->source[0]), &s->hisethaddr[0], sizeof(eth_address));
- if ( flags & tcp_FlagSYN ) {
-
- if ( ip->tos > s->tos )
- s->tos = ip->tos;
- else if ( ip->tos < s->tos ) {
- /* RFC 793 says we should close connection */
- /* we best not do that while SunOS ignores TOS */
- }
-
- s->acknum = intel( tp->seqnum ) + 1;
- s->hisport = intel16( tp->srcPort );
- s->hisaddr = intel( ip->source );
- s->flags = tcp_FlagSYN | tcp_FlagACK;
- s->state = tcp_StateSYNREC;
- s->unhappy = true;
- tcp_send(s, __LINE__); /* we must respond immediately */
-
- s->timeout = set_timeout( tcp_TIMEOUT );
- } else
- tcp_rst( ip , tp ); /* send a reset */
-
- return;
-
- case tcp_StateSYNSENT: /* added ACK Section */
- if ( flags & tcp_FlagSYN ) {
-
- if ( ip->tos > s->tos )
- s->tos = ip->tos;
- else if ( ip->tos < s->tos ) {
- /* RFC 793 says we should close connection */
- /* we best not do that while SunOS ignores TOS */
- }
-
- s->flags = tcp_FlagACK;
- s->timeout = set_timeout( tcp_TIMEOUT );
-
- /* FlagACK means connection established, else SYNREC */
- if ( flags & tcp_FlagACK) {
- /* but is it for the correct session ? */
- if (tp->acknum == intel(s->seqnum + 1)) {
- s->state = tcp_StateESTAB;
- s->seqnum++; /* good increment */
- s->acknum = intel( tp->seqnum ) + 1; /* 32 bits */
- tcp_ProcessData(s, tp, len); /* someone may try it */
- s->unhappy = true; /* rely on their attempts */
- tcp_send( s, __LINE__ );
- } else {
- /* wrong ack, force a RST and resend SYN soon*/
- s->flags = tcp_FlagRST;
- s->unhappy = true;
- tcp_send( s, __LINE__ );
- s->flags = tcp_FlagSYN;
- tcp_send( s, __LINE__ );
- }
- } else {
- s->acknum++;
- s->state = tcp_StateSYNREC;
- return;
- }
- } else
- tcp_rst( ip, tp );
- break;
-
- case tcp_StateSYNREC: /* recSYNSENT, sentACK, waiting EST */
- if ( flags & tcp_FlagSYN ) {
- s->flags = tcp_FlagSYN | tcp_FlagACK;
- s->unhappy = true;
- tcp_send(s, __LINE__);
- s->timeout = set_timeout( tcp_TIMEOUT );
- return;
- }
- if ( (flags & tcp_FlagACK) && (intel( tp->acknum ) == (s->seqnum + 1))) {
- if ( (s->window = intel16( tp->window )) > 0x7fff )
- s->window = 0x7fff;
- s->flags = tcp_FlagACK;
- s->state = tcp_StateESTAB;
- s->seqnum++;
- s->timeout = 0; /* never timeout */
- s->unhappy = false;
- return;
- }
-
- break;
- case tcp_StateESTAB:
- case tcp_StateESTCL:
- case tcp_StateCLOSWT:
-
- /* handle lost SYN */
- if ((flags & tcp_FlagSYN) && (flags & tcp_FlagACK)) {
- tcp_send( s, __LINE__ );
- return;
- }
-
- if ( !(flags & tcp_FlagACK)) return; /* must ack somthing */
- if ( flags & tcp_FlagSYN ) {
- tcp_rst( ip , tp );
- return;
- }
- s->timeout = 0l; /* we do not timeout at this point */
-
- /* process ack value in packet - but only if it falls
- * within current window */
-
- ldiff = intel( tp->acknum ) - s->seqnum;
- diff = (int) ldiff;
-
- if ( ldiff >= 0 && diff <= s->datalen ) {
- s->datalen -= diff;
- s->unacked -= diff;
- if (s->datalen < 0) s->datalen = 0; /* remote proto error */
- if ( s->queuelen ) {
- s->queue += diff;
- s->queuelen -= diff;
- } else
- movmem(s->data + diff, s->data, s->datalen );
- s->seqnum += ldiff;
- } else {
- #ifdef DEBUG
- #ifndef RELEASE
- if(debug_on >1) printf("tcphandler confused so set unacked back to 0 from %u\n",s->unacked);
- #endif /* RELEASE */
- #endif DEBUG
- s->unacked = 0;
- }
- if (s->unacked < 0) s->unacked = 0;
-
- s->flags = tcp_FlagACK;
- tcp_ProcessData(s, tp, len);
-
- if (( flags & tcp_FlagFIN ) && (s->state != tcp_StateCLOSWT )
- && ( s->acknum == intel( tp->seqnum ))) {
- s->acknum ++;
- if ( ! s->err_msg ) s->err_msg = "Connection closed";
- s->state = tcp_StateCLOSWT;
- tcp_send( s, __LINE__ );
- s->state = tcp_StateLASTACK;
- s->flags |= tcp_FlagFIN;
- s->unhappy = true;
- }
-
- if ( diff > 0 || len > 0 ) {
- /* need to update window, but how urgent ??? */
- if ( diff > 0 || (len > (s->mss >> 1))) {
- tcp_send( s, __LINE__ );
- } else
- tcp_sendsoon( s );
-
- }
- if ( s->state == tcp_StateESTCL )
- tcp_close( s );
- return;
-
- case tcp_StateFINWT1:
- /* They have not necessarily read all the data yet, we must
- still supply it as requested */
-
- ldiff = intel( tp->acknum ) - s->seqnum;
- diff = (int) ldiff;
- if ( ldiff >= 0 && diff <= s->datalen ) {
- s->datalen -= diff;
- s->unacked -= diff;
- if (s->datalen < 0) s->datalen = 0;
- if ( s->queuelen ) {
- s->queue += diff;
- s->queuelen -= diff;
- } else
- movmem(s->data + diff, s->data, s->datalen );
- s->seqnum += ldiff;
- if (ldiff == 0 || s->unacked < 0) s->unacked = 0;
-
- }
-
- /* they may still be transmitting data, we must read it */
-
- tcp_ProcessData(s, tp, len);
-
- /* check if other tcp has acked all sent data and is ready
- to change states */
-
- if ( (flags & (tcp_FlagFIN|tcp_FlagACK) ) == (tcp_FlagFIN|tcp_FlagACK)) {
- /* trying to do similtaneous close */
- if (( intel( tp->acknum ) >= s->seqnum + 1 ) &&
- ( intel( tp->seqnum) == s->acknum )) {
- s->seqnum++;
- // we shouldn't be inc'ing the ack
- // s->acknum++;
- s->flags = tcp_FlagACK;
- tcp_send( s, __LINE__ );
- s->unhappy = false;
- s->timeout = set_timeout( 2 );
- s->state = tcp_StateCLOSED;
- }
- } else if ( flags & tcp_FlagACK ) {
- /* other side is legitimately acking our fin */
- if (( intel( tp->acknum ) == s->seqnum + 1 ) &&
- ( intel( tp->seqnum ) == s->acknum ) &&
- ( s->datalen == 0 )) {
- s->seqnum++;
- s->acknum++;
- s->state = tcp_StateFINWT2;
- s->timeout = set_timeout( 3 );
- s->unhappy = false; /* we don't send anything */
- }
- }
- break;
-
- case tcp_StateFINWT2:
-
- /* they may still be transmitting data, we must read it */
- tcp_ProcessData(s, tp, len);
-
- if ((flags & (tcp_FlagACK | tcp_FlagFIN)) ==
- (tcp_FlagACK | tcp_FlagFIN)) {
- if (( intel( tp->acknum ) == s->seqnum ) &&
- ( intel( tp->seqnum ) == s->acknum )) {
- s->acknum++;
- s->flags = tcp_FlagACK;
- tcp_send( s, __LINE__ );
- s->unhappy = false; /* we don't send anything */
- s->timeout = set_timeout( 2 );
- s->state = tcp_StateCLOSED;
- return;
- }
- }
- break;
-
- case tcp_StateCLOSING:
- if ((flags & (tcp_FlagACK | tcp_FlagFIN)) == tcp_FlagACK ) {
- if (( tp->acknum == intel(s->seqnum) ) &&
- ( tp->seqnum == intel(s->acknum))) {
- s->state = tcp_StateTIMEWT;
- s->timeout = set_timeout( tcp_TIMEOUT );
- s->unhappy = false;
- }
- }
- break;
-
- case tcp_StateLASTACK:
- if ( flags & tcp_FlagFIN ) {
- /* they lost our two packets, back up */
- s->flags = tcp_FlagACK | tcp_FlagFIN;
- tcp_send( s, __LINE__ );
- s->unhappy = TRUE; /* FALSE; */
- return;
- } else {
- if (( intel( tp->acknum ) == (s->seqnum + 1 )) &&
- ( intel( tp->seqnum ) == s->acknum )) {
- s->state = tcp_StateCLOSED; /* no 2msl necessary */
- s->unhappy = false; /* we're done */
- return;
- }
- }
- break;
-
- case tcp_StateTIMEWT:
- if ( flags & (tcp_FlagACK | tcp_FlagFIN) == (tcp_FlagACK | tcp_FlagFIN)) {
- /* he needs an ack */
- s->flags = tcp_FlagACK;
- tcp_send( s, __LINE__ );
- s->unhappy = false;
- s->state = tcp_StateCLOSED; /* support 2 msl in rst code */
- }
- break;
- }
- if (s->unhappy) tcp_sendsoon(s);
- }
-
- /*
- * Process the data in an incoming packet.
- * Called from all states where incoming data can be received: established,
- * fin-wait-1, fin-wait-2
- */
- static tcp_ProcessData(tcp_Socket *s, tcp_Header *tp, int len)
- {
- long ldiff;
- int diff, x;
- word flags;
- byte *dp;
-
- word *options, numoptions, opt_temp;
-
- if ( (s->window = intel16( tp->window )) > 0x7fff )
- s->window = 0x7fff;
-
- flags = intel16( tp->flags );
- ldiff = s->acknum - intel( tp->seqnum );
-
- if ( flags & tcp_FlagSYN ) ldiff--; /* back up to 0 */
- diff = (int) ldiff;
-
- /* find the data portion */
- x = tcp_GetDataOffset(tp) << 2; /* quadword to byte format */
- dp = (byte *)tp + x;
-
- /* process those options */
- if ( (numoptions = x - sizeof( tcp_Header )) != 0 ) {
- options = (word *)((byte *)(tp) + sizeof( tcp_Header));
- while ( numoptions-- > 0 ) {
- switch ( *options++ ) {
- case 0 : numoptions = 0; /* end of options */
- break;
- case 1 : break; /* nop */
-
- /* we are very liberal on MSS stuff */
- case 2 : if (*options == 2) {
- opt_temp = intel16( *(word*)(&options[1]));
- if (opt_temp < s->mss )
- s->mss = opt_temp;
- }
- numoptions -= 2 + *options;
- options += *options;
- break;
- }
- }
- }
- /* done option processing */
-
- len -= x; /* remove the header length */
- if ( ldiff >= 0 ) { /* skip already received bytes */
- dp += diff;
- len -= diff;
-
- if (s->dataHandler) {
- s->acknum += s->dataHandler(s, dp, len);
- } else {
- /* no handler, just dump to buffer, should be indexed, handles goofs */
- /* limit receive size to our window */
- if ( s->rdatalen >= 0 ) {
- if ( len > ( x = s->maxrdatalen - s->rdatalen )) {
- len = x;
- }
- if ( len > 0 ) {
- s->acknum += len; /* our new ack begins at end of data */
- movmem(dp, s->rdata + s->rdatalen, len );
- s->rdatalen += len;
- /*
- s->karn_count = 3;
- */
- }
- }
- }
- s->unhappy = (s->datalen) ? true : false;
- if (ldiff == 0 && s->unacked && chk_timeout( s->rtt_lasttran )) {
- #ifdef DEBUG
- #ifndef RELEASE
- if(debug_on >1) printf("data process timeout so set unacked back to 0 from %u\n",s->unacked);
- #endif /* RELEASE */
- #endif DEBUG
- s->unacked = 0;
- }
- } else {
- tcp_sendsoon( s );
- }
-
- s->timeout = set_timeout( tcp_TIMEOUT );
- return;
- }
-
- /*
- * Format and send an outgoing segment
- */
-
- static tcp_send(tcp_Socket *s, int line)
- {
- tcp_PseudoHeader ph;
- struct _pkt {
- in_Header in;
- tcp_Header tcp;
- word maxsegopt[2];
- } *pkt;
- byte *dp;
- in_Header *inp;
- tcp_Header *tcpp;
- int senddatalen, sendtotlen, sendpktlen, startdata, sendtotdata;
-
- int ippkt; /* 1..s->cwindow */
-
- s->recent = 0;
- pkt = (struct _pkt *)_eth_formatpacket(&s->hisethaddr[0], /*0x800*/ 8);
- dp = (byte *) &pkt->maxsegopt; /* dp constant for multi-packet sends */
- inp = &pkt->in;
- tcpp = &pkt->tcp;
-
-
- /* this is our total possible send size */
- if ( s->karn_count != 2 ) {
- /* BUG FIX : jason dent found this */
- /* sendtotdata = min( s->datalen - s->unacked, s->window ); */
- sendtotdata = max (min ( s->datalen, s->window ) - s->unacked, 0);
- startdata = s->unacked;
- } else {
- sendtotdata = (s->datalen >= s->window)? s->window : s->datalen;
- startdata = 0;
- }
- /*
- if (sendtotdata < 0) sendtotdata = 0;
- */
- sendtotlen = 0; /* running count of what we've sent */
-
- /* step through our packets */
- for ( ippkt = 1; ippkt <= s->cwindow; ++ippkt ) {
- /* adjust size for each packet */
- senddatalen = min( sendtotdata, s->mss );
-
- /*
- sendpktlen = senddatalen + sizeof( tcp_Header ) + sizeof( in_Header );
- inp->length = intel16( sendpktlen );
- */
- /* tcp header */
- tcpp->srcPort = intel16( s->myport );
- tcpp->dstPort = intel16( s->hisport );
- tcpp->seqnum = intel( s->seqnum + startdata ); /* unacked - no longer sendtotlen */
- tcpp->acknum = intel( s->acknum );
-
- tcpp->window = intel16( s->maxrdatalen - s->rdatalen );
- tcpp->flags = intel16( s->flags | 0x5000 );
- tcpp->checksum = 0;
- tcpp->urgentPointer = 0;
-
- /* do options if this is our first packet */
- if ( s->flags & tcp_FlagSYN ) {
- sendpktlen = sizeof( tcp_Header ) + sizeof( in_Header ) + 4;
- tcpp->flags = intel16( intel16( tcpp->flags) + 0x1000 );
- pkt->maxsegopt[0] = 0x0402;
- pkt->maxsegopt[1] = intel16( s->mss );
- dp += 4;
- } else {
- /* handle packets with data */
- if (senddatalen > 0) {
- sendpktlen = senddatalen + sizeof( tcp_Header ) + sizeof( in_Header );
-
- /* get data from appropriate place */
- if (s->queuelen) movmem(s->queue + startdata, dp, senddatalen );
- else movmem(s->data + startdata, dp, senddatalen);
- /* dp[ senddatalen ] = 0; */
- } else {
- /* handle no-data, not-first-SYN packets */
- sendpktlen = sizeof( tcp_Header ) + sizeof( in_Header );
- }
- }
-
- /* internet header */
- memset( inp, 0, sizeof( in_Header ));
- inp->ver = 4;
- inp->hdrlen = 5;
- inp->tos = s->tos;
- inp->identification = intel16( ++ip_id ); /* was post inc */
- // inp->frag = 0;
- inp->ttl = 254;
- inp->proto = TCP_PROTO;
- inp->checksum = 0;
- inp->source = intel( s->myaddr );
- inp->destination = intel( s->hisaddr );
- inp->length = intel16( sendpktlen );
-
- inp->checksum = ~checksum( inp, sizeof(in_Header));
-
- /* compute tcp checksum */
- ph.src = inp->source; /* already INTELled */
- ph.dst = inp->destination;
- ph.mbz = 0;
- ph.protocol = 6;
- ph.length = intel16( sendpktlen - sizeof(in_Header));
- /*
- ph.checksum = checksum(&pkt->tcp, (sendpktlen - sizeof(in_Header) +1) & 0xfffe);
- */
- ph.checksum = checksum(&pkt->tcp, sendpktlen - sizeof(in_Header));
-
- tcpp->checksum = ~checksum(&ph, sizeof(ph));
-
- if (_dbugxmit) (*_dbugxmit)(s,inp,tcpp, line);
- #ifndef RELEASE
- if (debug_on > 1) {
- mono[0]++;
- colour[0]++;
- mono[2] = colour[2] = (s->flags & tcp_FlagSYN) ? 'S' : ' ';
- mono[4] = colour[4] = (s->flags & tcp_FlagACK) ? 'A' : ' ';
- mono[6] = colour[6] = (s->flags & tcp_FlagFIN) ? 'F' : ' ';
- mono[8] = colour[8] = (s->flags & tcp_FlagRST) ? 'R' : ' ';
- }
- #endif /* RELEASE */
- if ( _eth_send( intel16( inp->length ))) { /* encounterred error */
- tcp_sendsoon( s );
- return;
- }
-
- /* do next ip pkt */
- sendtotlen += senddatalen;
- startdata += senddatalen;
- sendtotdata -= senddatalen;
- if (sendtotdata <= 0 ) break;
- }
- s->unacked = startdata;
- #ifdef DEBUG
- #ifndef RELEASE
- if (debug_on) printf(" Sent %u/%u bytes in %u/%u packets with (%u) unacked SYN %lu line %u\n",
- sendtotlen, s->window, ippkt, s->cwindow, s->unacked, s->seqnum, line);
- #endif /* RELEASE */
- #endif DEBUG
- s->vj_last = 0;
- if ( s->karn_count == 2 ) {
- if (s->rto) s->rto = (s->rto * 3) / 2;
- else s->rto = 4;
- } else {
- /* vj_last nonzero if we expect an immediate response */
- if (s->unhappy || s->datalen)
- s->vj_last = set_ttimeout( 0 );
- s->karn_count = 0;
- }
- s->rtt_time = set_ttimeout( s->rto + 2 );
- if (sendtotlen > 0 ) s->rtt_lasttran = s->rtt_time + s->rto;
- }
- /*
- * Format and send a reset tcp packet
- */
- tcp_rst( in_Header *his_ip, tcp_Header *oldtcpp)
- {
- tcp_PseudoHeader ph;
- struct _pkt {
- in_Header in;
- tcp_Header tcp;
- word maxsegopt[2];
- } *pkt, *his_pkt;
-
- static longword nextrst = 0L;
- word oldflags;
- in_Header *inp;
- tcp_Header *tcpp;
- eth_Header *eth;
- int sendtotlen; /* length of packet */
- int temp;
- longword templong;
-
- /* see RFC 793 page 65 for details */
-
- if ( !chk_timeout( nextrst )) return;
- nextrst = set_ttimeout( 1 );
-
- oldflags = intel16( oldtcpp->flags );
- if (oldflags & tcp_FlagRST ) return;
- #ifdef NEVER
- if ( (oldflags & (tcp_FlagACK | tcp_FlagFIN)) == (tcp_FlagACK | tcp_FlagFIN) ){
- templong = oldtcpp->seqnum;
- oldtcpp->seqnum = oldtcpp->acknum;
- oldtcpp->acknum = templong;
- oldflags = tcp_FlagACK;
- } else if ((oldflags & (tcp_FlagSYN | tcp_FlagACK)) == tcp_FlagSYN ) {
- oldtcpp->acknum = intel( intel( oldtcpp->seqnum ) + 1 );
- oldtcpp->seqnum = 0;
- oldflags = tcp_FlagACK | tcp_FlagRST;
- } else if ( oldflags & tcp_FlagACK ) {
- oldtcpp->seqnum = oldtcpp->acknum;
- oldtcpp->acknum = 0;
- } else {
- oldtcpp->acknum = intel( intel(oldtcpp->seqnum) + 1);
- oldtcpp->seqnum = 0;
- }
- if ( oldflags & ( tcp_FlagFIN | tcp_FlagSYN ) == 0 )
- oldflags ^= tcp_FlagACK | tcp_FlagRST;
-
- if ( oldflags & tcp_FlagACK ) {
- oldtcpp->seqnum = oldtcpp->acknum;
-
- #else
- /* better strategy - Dean Roth */
- if ( oldflags & tcp_FlagACK ) {
- oldtcpp->seqnum = oldtcpp->acknum;
- oldtcpp->acknum = 0;
- } else {
- temp = intel16( his_ip->length) - in_GetHdrlenBytes( his_ip );
- oldtcpp->acknum = intel( intel( oldtcpp->seqnum ) + temp );
- oldtcpp->seqnum = 0;
- }
- oldflags = tcp_FlagRST;
- #endif
-
- his_pkt = (struct _pkt*)( his_ip );
-
- /* convoluted mechanism - reads his ethernet address or garbage */
- eth = _eth_hardware( his_ip );
-
- pkt = (struct _pkt *)_eth_formatpacket( eth, 8);
- inp = &pkt->in;
- tcpp = &pkt->tcp;
-
- sendtotlen = sizeof( tcp_Header ) + sizeof( in_Header );
- memset( inp, 0, sizeof( in_Header ));
- inp->length = intel16( sendtotlen );
-
- /* tcp header */
- tcpp->srcPort = oldtcpp->dstPort;
- tcpp->dstPort = oldtcpp->srcPort;
- tcpp->seqnum = oldtcpp->seqnum;
- tcpp->acknum = oldtcpp->acknum;
- tcpp->window = 0;
- /* tcpp->flags = intel16( oldflags ); */
- /* BUG FIX : jason dent found this thanks to SCO */
- tcpp->flags = intel16( (oldflags & 0x0fff ) | 0x5000 );
- tcpp->checksum = 0;
- tcpp->urgentPointer = 0;
-
- /* internet header */
- inp->ver = 4;
- inp->hdrlen = 5;
- inp->tos = his_ip->tos;
- inp->identification = intel16( ++ip_id );
- // inp->frag = 0;
- inp->ttl = 254;
- inp->proto = TCP_PROTO;
- inp->checksum = 0;
- inp->source = his_ip->destination;
- inp->destination = his_ip->source;
-
- inp->checksum = ~checksum( inp, sizeof(in_Header))/* 0*/;
-
- /* compute tcp checksum */
- ph.src = inp->source; /* already INTELled */
- ph.dst = inp->destination;
- ph.mbz = 0;
- ph.protocol = 6;
- ph.length = intel16( sendtotlen - sizeof(in_Header));
-
- ph.checksum = checksum(&pkt->tcp, sizeof(tcp_Header));
- tcpp->checksum = ~checksum(&ph, sizeof(ph));
-
- if (_dbugxmit) (*_dbugxmit)(NULL,inp,tcpp,__LINE__);
- _eth_send( intel16( inp->length ));
- }
-
-
-
-
- /**********************************************************************
- * socket functions
- **********************************************************************/
-
- /* socket based stuff */
-
- /*
- * sock_read - read a socket with maximum n bytes
- * - busywaits until buffer is full but calls s->usr_yield
- * - returns count also when connection gets closed
- */
- sock_read(sock_type *s, byte *dp, int len )
- {
- int templen, count;
- count = 0;
- do {
- if ( s->udp.ip_type == UDP_PROTO )
- templen = udp_read( &(s->udp), dp, len );
- else
- templen = tcp_read( &(s->tcp), dp, len);
- if (s->tcp.usr_yield) (s->tcp.usr_yield)();
- if (templen < 1 ) {
- if (!tcp_tick( s )) return( count );
- } else {
- count += templen;
- dp += templen;
- len -= templen;
- }
- } while ( len );
- return( count );
- }
- /*
- * sock_fead - read a socket with maximum n bytes
- * - does not busywait until buffer is full
- */
- sock_fastread(sock_type *s, byte *dp, int len )
- {
- if ( s->udp.ip_type == UDP_PROTO )
- len = udp_read( &(s->udp), dp, len );
- else
- len = tcp_read( &(s->tcp), dp, len);
- return( len );
- }
-
-
- /*
- * sock_write - writes data and returns length written
- * - does not perform flush
- * - repeatedly calls s->usr_yield
- */
-
- sock_write(sock_type *s, byte *dp, int len)
- {
- int offset, oldlen, oldmode, proto;
-
- oldlen = len;
- offset = 0;
-
- proto = (s->udp.ip_type == TCP_PROTO);
- if ( proto ) oldmode = s->tcp.flags & tcp_FlagPUSH;
- while ( len > 0) {
- if (proto) {
- s->tcp.flags |= oldmode;
- offset += tcp_write( &(s->udp), &dp[ offset ], len);
- } else
- offset += udp_write( &(s->tcp), &dp[ offset ], len );
- len = oldlen - offset;
- if (s->udp.usr_yield)(s->udp.usr_yield)();
- if (!tcp_tick(s)) return( 0 );
- }
- return( oldlen );
- }
-
-
-
- sock_fastwrite(sock_type *s, byte *dp, int len)
- {
- return( ( s->udp.ip_type == UDP_PROTO ) ?
- udp_write( s, dp, len ) :
- tcp_write( s, dp, len) );
- }
-
- int sock_setbuf( sock_type *s, byte *dp, int len )
- {
- if ( len < 0 ) return( 0 );
- if (len == 0 || dp == NULL ) {
- s->tcp.rdata = s->tcp.rddata;
- s->tcp.maxrdatalen = tcp_MaxBufSize;
- } else {
- s->tcp.rdata = dp;
- s->tcp.maxrdatalen = len;
- }
- return( s->tcp.maxrdatalen);
- }
-
- int sock_enqueue(sock_type *s, byte *dp, int len )
- {
- int written;
-
- if ( len < 0 ) return( 0 );
- if ( s->udp.ip_type == UDP_PROTO ) {
- do {
- written = udp_write( s, dp, len );
- dp += written;
- } while (len -= written > 0);
- } else {
- s->tcp.queue = dp;
- s->tcp.queuelen = len;
- s->tcp.datalen = len;
- tcp_send( s, __LINE__ ); /* start sending it */
- }
- return( len );
- }
-
- void sock_noflush( sock_type *s )
- {
- if ( s->tcp.ip_type == TCP_PROTO ) {
- s->tcp.flags &= ~tcp_FlagPUSH;
- s->tcp.sock_mode |= TCP_LOCAL ;
- }
- }
- void sock_flush( sock_type *s )
- {
- if ( s->tcp.ip_type == TCP_PROTO ) {
- s->tcp.sock_mode &= ~TCP_LOCAL;
- tcp_Flush( s );
- }
- }
-
- /*
- * sock_flushnext - cause next transmission to have a flush
- */
- void sock_flushnext( sock_type *s)
- {
- if (s->tcp.ip_type == TCP_PROTO ) {
- s->tcp.flags |= tcp_FlagPUSH;
- s->tcp.sock_mode &= ~TCP_LOCAL;
- }
- }
- /*
- * sock_putc - put a character
- * - no expansion but flushes on '\n'
- * - returns character
- */
- byte sock_putc( sock_type *s, byte c )
- {
- if (( c == '\n') || ( c == '\r'))
- sock_flushnext( s );
- sock_write( s, &c, 1 );
- return( c );
- }
-
- word sock_getc( sock_type *s )
- {
- char ch;
- return( sock_read( s, &ch, 1 ) < 1 ? EOF : ch );
- }
-
- /*
- * sock_puts - does not append carriage return in binary mode
- * - returns length
- */
- sock_puts( sock_type *s, byte *dp )
- {
- int len, oldmode;
-
- len = strlen( dp );
-
- if (s->tcp.sock_mode & TCP_MODE_ASCII ) {
- if (s->tcp.ip_type == TCP_PROTO )
- s->tcp.sock_mode |= TCP_LOCAL;
- sock_noflush( s );
- if (len) sock_write( s, dp, len );
- sock_flushnext( s );
- sock_write( s, "\r\n", 2 );
- } else {
- sock_flushnext( s );
- sock_write( s, dp, len );
- }
- return( len );
- }
-
- /*
- * sock_update - update the socket window size to the other guy
- */
- static void sock_update( tcp_Socket *s )
- {
- if (s->ip_type == TCP_PROTO) {
- if ( !s->rdatalen )
- tcp_send( s, __LINE__ ); /* update the window */
- else
- tcp_sendsoon( s );
- }
- }
- /*
- * sock_gets - read a string from any socket
- * - return length of returned string
- * - removes end of line terminator(s)
- *
- * - Quentin Smart fixed some problems
- */
- int sock_gets( sock_type *s, byte *dp, int n )
- {
- int len, templen, *np;
- char *src_p, *temp, *temp2;
-
- if ( s->udp.ip_type == UDP_PROTO ) {
- src_p = s->udp.rdata;
- np = &s->udp.rdatalen;
- } else {
- src_p = s->tcp.rdata;
- np = &s->tcp.rdatalen;
- }
- if ( *np == 0 ) return( 0 );
-
- // eat trailing \n or \0 from previous line
- if ( *src_p == 0 || *src_p == '\n' ) {
- movmem( src_p + 1, src_p, *np -= 1 );
- if ( !*np ) return( 0 );
- }
-
- if ( --n > *np ) n = *np;
-
- // Q.Smart found and fixed a bug here
- memcpy( dp, src_p, n ); // copy everything
- dp[ n ] = 0; // terminate new string
- temp = memchr( dp, '\n', n);
- temp2= memchr( dp, '\r', n);
-
- if (temp) *temp = 0;
- if (temp2) *temp2= 0;
-
- // skip if there were no crs
- if ( !temp2 ) {
- *dp = 0;
- return( 0 );
- }
-
- // not: len = strlen( dp );
- len = (int)(( temp ? min( FP_OFF(temp), FP_OFF(temp2)) :
- FP_OFF(temp2)) - FP_OFF(dp));
-
- // expect \r\n or \r\0 or \n or \r
- // so get first of the two
-
- if ( temp == NULL ) { /* handles \r only */
- temp = temp2;
- temp2 = NULL;
- } else if ( FP_OFF( temp ) > FP_OFF( temp2 ))
- temp = temp2; // handles trailing \n or \0
-
- n = len + 1; // account for first \r
-
- // we check next char if it exists, and skip it if 0, \r, or \n
- if ((*np > n) && !src_p[n] ) n++;
- movmem( &src_p[ n ], src_p, *np -= n );
- if (*np < 0) *np = 0;
-
- sock_update( s ); /* new window */
- return( len );
- }
-
-
- /*
- * sock_dataready - returns number of bytes waiting to be ready
- * - if in ASCII mode, return 0 until a line is present
- * or the buffer is full
- */
- word sock_dataready( sock_type *s )
- {
- int len;
- char *p;
-
- if (!(len = s->tcp.rdatalen)) return( 0 );
-
- if ( s->tcp.sock_mode & TCP_MODE_ASCII ) {
- p = s->tcp.rdata;
- if ( *p == '\n' ) {
- movmem( p + 1, p, s->tcp.rdatalen = --len);
- if ( ! len ) return( 0 );
- }
- /* check for terminating \r */
- if ( memchr( p, '\r', len))
- return( len );
- return( 0 );
- } else
- return( len );
- }
-
- sock_established( sock_type *s )
- {
- switch ( s->tcp.ip_type ) {
- case UDP_PROTO :
- return( 1 );
- case TCP_PROTO :
- return( s->tcp.state == tcp_StateESTAB ||
- s->tcp.state == tcp_StateESTCL ||
- s->tcp.state == tcp_StateCLOSWT );
- default :
- return( 0 );
- }
- }
-
- sock_close( sock_type *s )
- {
- switch (s->udp.ip_type) {
- case UDP_PROTO :
- udp_close( s );
- break;
- case TCP_PROTO :
- tcp_close( s );
- tcp_tick( s );
- break;
- }
- }
-
-
- /*
- * _ip_delay0 called by macro sock_wait_established()
- * _ip_delay1 called by macro sock_wait_intput()
- * _ip_delay2 called by macro sock_wait_closed();
- *
- */
-
- _ip_delay0( s, timeoutseconds, fn, statusptr )
- sock_type *s;
- int timeoutseconds;
- procref fn;
- int *statusptr;
- {
- int status;
- ip_timer_init( s , timeoutseconds );
- do {
- if ( s->tcp.ip_type == TCP_PROTO ) {
- if ( tcp_established( s )) {
- status = 0;
- break;
- }
- }
- kbhit(); /* permit ^c */
- if ( !tcp_tick( s )) {
- if (!s->tcp.err_msg) s->tcp.err_msg = "Host refused connection";
- status = -1; /* get an early reset */
- break;
- }
- if ( ip_timer_expired( s )) {
- s->tcp.err_msg = "Open timed out";
- sock_close( s );
- status = -1;
- break;
- }
- if ( fn ) if (status = fn(s)) break;
- if ( s->tcp.usr_yield ) (*s->tcp.usr_yield)();
- if ( s->tcp.ip_type == UDP_PROTO ) {
- status = 0;
- break;
- }
- } while ( 1 );
- if (statusptr) *statusptr = status;
- return( status );
- }
-
- _ip_delay1( s, timeoutseconds, fn, statusptr)
- sock_type *s;
- int timeoutseconds;
- procref fn;
- int *statusptr;
- {
- int status;
- ip_timer_init( s , timeoutseconds );
-
- sock_flush( s ); /* new enhancement */
-
- do {
- if ( sock_dataready( s )) {
- status = 0;
- break;
- }
- kbhit(); /* permit ^c */
-
- if ( !tcp_tick( s )) {
- status = 1;
- break;
- }
- if ( ip_timer_expired( s )) {
- s->tcp.err_msg = "Connection timed out";
- sock_close( s );
- status = -1;
- break;
- }
- if (fn) {
- if (status = fn(s))
- break;
- }
- if ( s->tcp.usr_yield ) (*s->tcp.usr_yield)();
- } while ( 1 );
- if (statusptr) *statusptr = status;
- return( status );
- }
-
- _ip_delay2( s, timeoutseconds, fn, statusptr)
- sock_type *s;
- int timeoutseconds;
- procref fn;
- int *statusptr;
- {
- int status;
- ip_timer_init( s , timeoutseconds );
-
- if (s->tcp.ip_type != TCP_PROTO ) {
- if ( statusptr ) * statusptr = 1;
- return( 1 );
- }
-
- do {
- /* in this situation we KNOW user not planning to read rdata */
- s->tcp.rdatalen = 0;
- kbhit(); /* permit ^c */
- if ( !tcp_tick( s )) {
- status = 1;
- break;
- }
- if ( ip_timer_expired( s )) {
- s->tcp.err_msg = "Connection timed out";
- sock_abort( s );
- status = -1;
- break;
- }
- if (fn) {
- if (status = fn(s))
- break;
- }
- if ( s->tcp.usr_yield ) (*s->tcp.usr_yield)();
-
- } while ( 1 );
- if (statusptr) *statusptr = status;
- return( status );
- }
-
-
- char *rip( char *s )
- {
- char *temp;
-
- if (temp = (char *)strchr( s, '\n')) *temp = 0;
- if (temp = (char *)strchr( s, '\r')) *temp = 0;
- return( s );
- }
-
-
-