home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.wwiv.com
/
ftp.wwiv.com.zip
/
ftp.wwiv.com
/
pub
/
PPPBCKP
/
SRC15B24.ZIP
/
WATTSRC.ZIP
/
SRC
/
PCTCP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1997-03-08
|
70KB
|
2,224 lines
/*
* 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 <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, word offset);
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 void tcp_ProcessData(tcp_Socket *s, tcp_Header *tp, int len, // 94.11.19
tcp_PseudoHeader *ph);
static initialized = 0;
static void (*system_yield)() = NULL;
extern int multihomes;
extern word _pktipofs;
void (*wattcpd)(void) = 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);
static void tcp_unthread(tcp_Socket *ds);
static void tcp_abort(tcp_Socket *s);
void tcp_sendsoon(tcp_Socket *s );
static void tcp_send(tcp_Socket *s, int line);
static void tcp_rst( in_Header *his_ip, tcp_Header *oldtcpp);
static udp_close(udp_Socket *ds);
/*
* sock_yield - enable user defined yield function
*/
int sock_yield( tcp_Socket *s, void (*fn)( void ) )
{
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( sock_type *s , int delayseconds )
{
if (delayseconds)
s->tcp.usertimer = set_timeout( delayseconds );
else
s->tcp.usertimer = 0;
}
int ip_timer_expired( sock_type *s )
{
if (! s->tcp.usertimer) /* cannot expire */
return( 0 );
return( chk_timeout( s->tcp.usertimer));
}
longword MsecClock( void )
{
return( (*realclock) * 055L);
}
static long make_timeout( word timeout )
{
if ( timeout ) return( set_timeout( timeout ));
return( 0 );
}
#if 0 /* 94.11.27 -- not used? */
/*
* check_timeout - test agains timeout clock - account for overflow
*/
static int check_timeout( unsigned long timeout )
{
if (timeout) return( chk_timeout( timeout ));
return( 0 );
}
#endif
/*
* 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 */
/*
* look for bugs
*/
int tcp_checkfor( sock_type *t )
{
tcp_Socket *p;
for ( p = tcp_allsocs ; p ; p = p->next )
if ( p == (tcp_Socket *)t ) return( 1 );
return( 0 );
}
/*
* Shut down the card and all services
*/
void tcp_shutdown( void )
{
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( void )
{
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 */
_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");
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, dataHandler_t 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, &s->hisethaddr, 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, dataHandler_t 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, &s->hisethaddr, 0) )
return( 0 );
s->hisaddr = ina;
s->hisport = port;
s->seqnum = intel( set_timeout( 1 )) & 0xffff0000uL;
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, dataHandler_t 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 */
/* tcpwinfix -- mdurkin */
s->vj_sa = 4; /* about 250 ms */ /* was wrong val 95.05.02 */
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( sock_type *s )
{
if ( s->tcp.ip_type == TCP_PROTO )
tcp_abort( (tcp_Socket *)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)) {
/* 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;
if ( s->cwindow == 0 ) s->cwindow = 1;
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
*/
int 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 )) != NULL ) {
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;
}
}
break;
case /*0x806*/ 0x608 :
/* do arp */
_arp_handler( (arp_Header *)ip );
break;
}
if (ip) _eth_free(ip);
continue;
}
/* check for our outstanding packets */
tcp_Retransmitter();
return( s->udp.ip_type );
}
/* returns 1 if connection is established */
int tcp_established( tcp_Socket *s )
{
return( s->state >= tcp_StateESTAB );
}
/*
* udp_write() handles fragmented UDP by assuming it'll be called
* once for all fragments with no intervening calls. This is
* the case in sock_write().
* Handles upto a hair-under 32K datagrams. Could be made to handle
* upto a hair-under 64K easily... wanna Erick?
* Might be possible to test 'offset' for non/zero fewer times to be
* more efficient. Might also be more efficient to use the old
* UDP checksum() call when more_frags is false in the first frag
* (i.e., not a fragmented dgram).
* Uses _mss to decide splits which defaults to 1400. Could pack
* more into an Ethernet packet.
*/
#define IP_MF 0x0020 // more fragments, net byte order
static udp_write( udp_Socket *s, byte *datap, int len, word offset )
{
struct { // special pseudo header because need to
tcp_PseudoHeader ph; // compute checksum in two parts (may not
word checksum2; // have all of datagram built at once).
} ph;
struct _pkt {
in_Header in;
udp_Header udp;
int data;
/* longword maxsegopt; */
} *pkt;
byte *dp;
in_Header *inp;
udp_Header *udpp;
word maxlen;
int more_frags;
word origlen = len;
pkt = (struct _pkt *)_eth_formatpacket(&s->hisethaddr, /*0x800*/ 8);
if( offset ) { // this is not the first fragment
dp = (byte *) &pkt->udp; // data goes right after IP header
} else {
dp = (byte *) &pkt->data;
udpp = &pkt->udp;
/* udp header */
udpp->srcPort = intel16( s->myport );
udpp->dstPort = intel16( s->hisport );
udpp->checksum = 0;
udpp->length = intel16( UDP_LENGTH + len );
}
inp = &pkt->in;
memset( inp, 0, sizeof( in_Header ));
maxlen = _mss & 0xFFF8; // make a multiple of 8
if( !offset ) maxlen -= UDP_LENGTH; // note UDP_LENGTH is 8, so ok
if( len > maxlen ) {
len = maxlen;
more_frags = 1;
} else more_frags = 0;
inp->length = intel16( sizeof(in_Header) +
(offset ? 0 : 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 */
/* if offset non-zero, then is part of a prev datagram so don't incr ID */
inp->identification = intel16( offset ? ip_id : ++ip_id ); /* was post inc */
// inp->frag = 0;
inp->frags = (offset ? intel16((offset + UDP_LENGTH) >> 3) : 0);
if(more_frags) inp->frags |= IP_MF;
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(!offset) { // only first of frags has UDP header for entire UDP dgram
if ( s->sock_mode & UDP_MODE_NOCHK )
udpp->checksum = 0;
else {
ph.ph.src = inp->source; /* already INTELled */
ph.ph.dst = inp->destination;
ph.ph.mbz = 0;
ph.ph.protocol = UDP_PROTO; /* udp */
ph.ph.length = udpp->length; /* already INTELled */
/* can't use since may not have the whole dgram built at once */
// ph.checksum = checksum(&pkt->udp, intel16(ph.length));
/* this way handles it */
ph.ph.checksum = checksum(&pkt->udp, UDP_LENGTH);
ph.checksum2 = checksum(datap, origlen);
udpp->checksum = ~checksum(&ph, sizeof(ph));
}
}
_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 )
{
static int in_icmp_redirect = 0; // smart@actrix.gen.nz
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 : if (( s->stress ++ > s->rigid ) &&
( s->rigid < 100 )) {
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;
}
// follow through to next case
/* 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 */
/* Dummy is passed in NW form need to intel! */
/* This was a bug fixed QVS - smart@actrix.gen.nz */
if (!in_icmp_redirect)
{
in_icmp_redirect = 1;
_arp_resolve(intel(dummyip), &s->hisethaddr, 0);
in_icmp_redirect = 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 int 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 );
// temp = ip number
// or 255.255.255.255
// or sin_mask.255.255
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->hisport != 0) &&
(intel16( up->dstPort ) == s->myport) &&
(intel16( up->srcPort ) == s->hisport) &&
((intel( ip->destination ) & sin_mask) == (s->myaddr & sin_mask)) &&
(intel( ip->source ) == s->hisaddr )) break;
}
if ( !s ) {
/* demux to passive sockets */
for ( s = udp_allsocs; s; s = s->next )
if ( ((s->hisaddr == 0) || (s->hisaddr == 0xffffffffuL))
&& intel16( up->dstPort ) == s->myport ) {
// do we record this information ???
if ( s->hisaddr == 0 ) {
s->hisaddr = intel( ip->source );
s->hisport = intel16( up->srcPort );
_arp_resolve(intel(ip->source), &s->hisethaddr, 0);
// take on value of expected destination unless it
// is broadcast
if ( (~ip->destination & ~sin_mask) != 0 )
s->myaddr = intel( ip->destination );
}
break;
}
}
if ( !s ) {
/* demux to broadcast sockets */
for ( s = udp_allsocs; s; s = s->next )
if ( (s->hisaddr == 0xffffffffuL) &&
(intel16( up->dstPort ) == s->myport )) break;
}
if ( !s )
return;
// these parameters are used for things other than just checksums
ph.src = ip->source; /* already INTELled */
ph.dst = ip->destination;
ph.mbz = 0;
ph.protocol = UDP_PROTO;
ph.length = up->length;
if ( up->checksum ) {
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 );
/* demux to active sockets */
for ( s = tcp_allsocs; s; s = s->next ) {
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 ( !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 ) {
/* tester */
ph.checksum = checksum(tp, len);
checksum(&ph, sizeof(ph));
tcp_sendsoon( s );
return;
}
/* reset code */
if ( flags & tcp_FlagRST ) {
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;
} 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 ;
}
s->karn_count = 0;
if ( s->wwindow != 255 ) {
if ( s->wwindow++ >= s->cwindow ) {
if ( s->cwindow != 255 )
s->cwindow ++;
/* tcpwinfix
* Movement of the next line *seems* to fix the failure of the TCP
* send window to open up (which makes TCP writes very slow, as
* seen with some previous releases of WatFTP), though it's been
* a long time since I made this change (today is 96.09.24) and
* I'm not sure how confident I was even then that it was correct.
* Also I don't have any description of the VJ algorithm and don't
* really understand this code all that well, but some time ago
* this seemed to be the right thing to do and seems to work.
* That said, if I'm wrong, I hope I haven't broken things worse :-)
* There is one other place, also marked 'tcpwinfix' above in this
* file, and those are the only two changes I made for this bug
* which may need undoing if I'm wrong. -- mdurkin
*/
s->wwindow = 0; /* mdurkin -- added 95.05.02 */
}
/* s->wwindow = 0; /* mdurkin -- removed 95.05.02 */
}
}
/* 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), &s->hisethaddr, 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, &ph); /* 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 {
s->unacked = 0;
}
if (s->unacked < 0) s->unacked = 0;
s->flags = tcp_FlagACK;
tcp_ProcessData(s, tp, len, &ph);
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, &ph);
/* 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++;
// they are just acking our seq num, not sending more data for us to ack
// 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, &ph);
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 void tcp_ProcessData(tcp_Socket *s, tcp_Header *tp, int len,
tcp_PseudoHeader *ph)
{
long ldiff;
int diff, x;
word flags;
byte *dp;
word *options, numoptions, opt_temp;
if ( s->stress > 0 ) s->stress--;
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, ph, tp); // 94.11.19
// 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 )) {
s->unacked = 0;
}
} else {
tcp_sendsoon( s );
}
s->timeout = set_timeout( tcp_TIMEOUT );
return;
}
/*
* Format and send an outgoing segment
*/
static void 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, /*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 ( _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;
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
*/
static void 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;
#ifdef NEVER
longword templong;
#endif
/* 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;
oldflags = tcp_FlagRST;
} 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 {
temp = intel16( his_ip->length) - in_GetHdrlenBytes( his_ip );
oldtcpp->acknum = intel( intel( oldtcpp->seqnum ) + temp );
oldtcpp->seqnum = 0;
oldflags = tcp_FlagRST;
}
#endif
/* 94.11.19 -- removed, not used? */
/* his_pkt = (struct _pkt*)( his_ip ); */
/* convoluted mechanism - reads his ethernet address or garbage */
eth = _eth_hardware( (byte *)his_ip );
pkt = (struct _pkt *)_eth_formatpacket( (eth_address *)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));
_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
*/
int 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
*/
int 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
*/
int 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->tcp), &dp[ offset ], len);
} else
offset += udp_write( &(s->udp), &dp[ offset ], len, offset );
len = oldlen - offset;
if (s->udp.usr_yield)(s->udp.usr_yield)();
if (!tcp_tick(s)) return( 0 );
}
return( oldlen );
}
/* NOTE: for UDP, assumes data fits in one datagram, else only the first
fragment will be sent!!!!! Because _mss is used for splits,
by default the max data size is 1400 - UDP_LENGTH for a non-fragged
datagram.
*/
int sock_fastwrite( sock_type *s, byte *dp, int len )
{
return( ( s->udp.ip_type == UDP_PROTO ) ?
udp_write( &(s->udp), dp, len, 0 ) :
tcp_write( &(s->tcp), 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;
word offset = 0;
if ( len < 0 ) return( 0 );
if ( s->udp.ip_type == UDP_PROTO ) {
do {
written = udp_write( &(s->udp), dp, len, offset );
dp += written;
offset += written;
} while (len -= written > 0);
} else {
s->tcp.queue = dp;
s->tcp.queuelen = len;
s->tcp.datalen = len;
tcp_send( &(s->tcp), __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->tcp) );
}
}
/*
* 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 );
}
int 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
*/
int 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, *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->tcp) ); /* 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
*/
int 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 );
}
int 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 );
}
}
void sock_close( sock_type *s )
{
switch (s->udp.ip_type) {
case UDP_PROTO :
udp_close( &(s->udp) );
break;
case TCP_PROTO :
tcp_close( &(s->tcp) );
tcp_tick( s );
break;
}
}
void sock_sturdy( sock_type *s, int level )
{
s->tcp.rigid = level;
if ( s->tcp.rigid < s->tcp.stress ) sock_abort( s );
}
/*
* _ip_delay0 called by macro sock_wait_established()
* _ip_delay1 called by macro sock_wait_intput()
* _ip_delay2 called by macro sock_wait_closed();
*
*/
int _ip_delay0( sock_type *s, int timeoutseconds, sockfunct_t fn, int *statusptr )
{
int status;
ip_timer_init( s , timeoutseconds );
do {
if ( s->tcp.ip_type == TCP_PROTO ) {
if ( tcp_established( &(s->tcp) )) {
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 != NULL) && ((status = fn(s)) != 0) ) 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 );
}
int _ip_delay1( sock_type *s, int timeoutseconds, sockfunct_t 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)) != 0)
break;
}
if ( s->tcp.usr_yield ) (*s->tcp.usr_yield)();
} while ( 1 );
if (statusptr) *statusptr = status;
return( status );
}
int _ip_delay2( sock_type *s, int timeoutseconds, sockfunct_t 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)) != 0)
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 = strchr( s, '\n')) != NULL) *temp = '\0';
if ((temp = strchr( s, '\r')) != NULL) *temp = '\0';
return( s );
}