home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Media Share 9
/
MEDIASHARE_09.ISO
/
hamradio
/
wattcp.zip
/
PCTCP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-08-05
|
60KB
|
2,150 lines
/* 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 <copyright.h>
#include <time.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <mem.h>
#include <dos.h>
#include <values.h>
#include "wattcp.h"
#include "elib.h"
#define TCP_LOCAL 0x4000
/* statics */
static tcp_ProcessData(tcp_Socket *s, tcp_Header *tp, int len);
static char far *mono = 0xb0000000L;
static char far *colour = 0xb8000000L;
static initialized = 0;
static void (*system_yield)() = NULL;
extern int multihomes;
void (*_dbugxmit)() = NULL;
void (*_dbugrecv)() = NULL;
void (*wattcpd)() = NULL;
char *_hostname = "012345678901234567890123456789012345678901234567890";
word _mss = ETH_MSS;
char *_wattcp = WATTCP_C;
/*
* sock_yield - enable user defined yield function
*/
sock_yield( tcp_Socket *s, void (*fn)())
{
if ( s )
s->usr_yield = fn;
else
system_yield = fn;
}
/*
* sock_mode - set binary or ascii - affects sock_gets, sock_dataready
* - set udp checksums
*/
sock_mode( sock_type *s, word mode )
{
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 = 0x000046cL;
#define MAXTICKS 0x1800b0L
void ip_timer_init( s , delayseconds )
udp_Socket *s;
int delayseconds;
{
if (delayseconds)
s->usertimer = set_timeout( delayseconds );
else
s->usertimer = 0;
}
int ip_timer_expired( s )
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 */
word debug_on = 0;
/*
* 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
*/
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
*/
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 in large model C compiler
*/
static 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
* - we need not be this picky, but it doesn't hurt
*/
static word findfreeport()
{
word temp;
tcp_Socket *s;
temp = (word) set_timeout( 0 ) + (word)(set_timeout(0) >> 16);
do {
if ( ++temp < 1024 ) temp += 1024;
if ( s = (tcp_Socket*)udp_allsocs ) {
while ( s->next && (s->myport != temp))
s = (tcp_Socket*)s->next;
if ( s->myport == temp ) continue;
}
if ( s = tcp_allsocs ) {
while ( s->next && (s->myport != temp ))
s = s->next;
if ( s->myport == temp ) continue;
}
break;
} while ( 1 );
return( temp );
}
/* socket, localport, destaddress */
int udp_open(s, lport, ina, port, datahandler)
udp_Socket *s;
longword ina;
word lport, 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;
if ( lport == 0 ) lport = findfreeport(); /* get a nonzero port val */
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, 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 */
if ( lport == 0 ) lport = findfreeport(); /* get a nonzero port val */
s->myaddr = my_ip_addr;
s->myport = lport;
if ( ! _arp_resolve(ina, &s->hisethaddr[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__ );
return( 1 );
}
/*
* Passive open: listen for a connection on a particular port
*/
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 );
if ( lport == 0 ) lport = findfreeport(); /* 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(ds)
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;
}
}
/*
* Send a FIN on a particular port -- only works if it is open
* Must still allow receives
*/
static 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 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( s );
}
/*
* tcp_sendsoon - schedule a transmission pretty soon
* - this one has an imperfection at midnight, but it
* is not significant to the connection performance
*/
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 tcp_Retransmitter()
{
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
if(debug_on >1) printf("regular retran TO set unacked back to 0 from %u\n", s->unacked);
#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( 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 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);
s->tcp.ip_type = 0;
}
}
/* plan our next retransmit */
if ( !timeout )
timeout = make_timeout( tcp_RETRANSMITTIME );
while ( ip = _eth_arrived( &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 {
if (debug_on)
outs("IP Received BAD Checksum \n\r");
}
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 );
}
tcp_set_debug_state( x )
int x;
{
debug_on = x;
}
/* returns 1 if connection is established */
int tcp_established(tcp_Socket *s)
{
return( s->state >= tcp_StateESTAB );
}
static udp_Write(s, datap, len)
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 = &pkt->data;
inp = &pkt->in;
udpp = &pkt->udp;
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(s, datap, maxlen)
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 );
}
_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 );
}
_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]);
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 tcp_Flush(tcp_Socket *s)
{
if ( s->datalen > 0 ) {
s->flags |= tcp_FlagPUSH;
tcp_send(s, __LINE__);
}
}
/*
* Handler for incoming packets.
*/
static 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 || /* broadcast */
((( temp - my_ip_addr) > multihomes ) && my_ip_addr))
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 ) {
if (debug_on) outs("chain error in udp\r\n");
}
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 && intel16( up->dstPort ) == s->myport ) {
if (_arp_resolve(intel(ip->source), &s->hisethaddr[0])) {
s->hisaddr = intel( ip->source );
s->hisport = intel16( up->srcPort );
}
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 ) {
if (debug_on) outs("discarding...");
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 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 );
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' : ' ';
}
/* demux to active sockets */
for ( s = tcp_allsocs; s; s = s->next ) {
if ( s->safetysig != SAFETYTCP ) {
if (debug_on) outs("chain error in tcp\r\n");
}
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 ) {
if (debug_on) outs("bad tcp checksum \n\r");
/* tester */
ph.checksum = checksum(tp, len);
checksum(&ph, sizeof(ph));
tcp_sendsoon( s );
return;
}
/* reset code */
if ( flags & tcp_FlagRST ) {
if (debug_on) outs("\7\7\7\7\7\7\7connection reset\n");
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
if (debug_on > 1 ) printf("finally got it safely zapped from %u to ????\n\r",s->unacked);
#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
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 /* 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 */
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( 1 );
break; /* unreachable */
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( 1 );
}
} 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( 1 );
}
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( 1 );
}
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
if(debug_on >1) printf("tcphandler confused so set unacked back to 0 from %u\n",s->unacked);
#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( 1 );
break; /* dummy for compiler */
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++;
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:
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( 1 );
}
}
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( 1 );
} 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( 1 );
}
}
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 )) {
options = (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
if(debug_on >1) printf("data process timeout so set unacked back to 0 from %u\n",s->unacked);
#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 = &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 */
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);
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' : ' ';
}
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
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 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;
byte *dp;
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( 0 );
#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);
dp = &pkt->maxsegopt;
inp = &pkt->in;
tcpp = &pkt->tcp;
sendtotlen = sizeof( tcp_Header ) + 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, dp, len );
else
templen = tcp_read( s, 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, dp, len );
else
len = tcp_read( s, 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, &dp[ offset ], len);
} else
offset += udp_Write( s, &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 );
}
sock_flush( sock_type *s )
{
if ( s->tcp.ip_type == TCP_PROTO )
tcp_Flush( s );
}
/*
* sock_flushnext - cause next transmission to have a flush
*/
sock_flushnext( sock_type *s)
{
if (s->tcp.ip_type == TCP_PROTO )
s->tcp.flags |= tcp_FlagPUSH;
}
/*
* 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;
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 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)
*/
word sock_gets( sock_type *s, byte *dp, int n )
{
int len, templen;
char *src_p, *temp, *temp2;
char *endn, *endr; /* used since we do not list strpbrk */
int *np;
char *src, *dest, *tempp;
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 ) 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;
/* src_p[ *np ] = 0; terminate string */
strncpy( dp, src_p, n ); /* copy everything */
dp[ n ] = 0; /* terminate */
temp = strchr( dp, '\n');
temp2= strchr( dp, '\r');
if (temp) *temp = 0;
if (temp2) *temp2= 0;
len = strlen( dp );
/* skip if there were no crs */
if ( !temp2 ) {
*dp = 0;
return( 0 );
}
#ifdef OLD
/* skip over \n and \r but stop on end */
if ( temp ) templen = FP_OFF( temp ) - FP_OFF( dp );
else if ( temp2 ) templen = FP_OFF( temp2 ) - FP_OFF( dp );
else templen = len + 1;
if (temp || temp2 ) {
++templen;
movmem( &src_p[ templen ], src_p, *np -= templen);
} else
* np = 0;
#else
/* 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 */
#ifdef OLD
if ( temp2 && temp[1] == 0 ) n++; /* account for \0, \n, etc */
#else
/* we check next char if it exists, and skip it if 0, \r, or \n */
if ((*np > n) && !src_p[n] ) n++;
#endif /* OLD */
movmem( &src_p[ n ], src_p, *np -= n );
if (*np < 0) *np = 0;
#endif /* OLD */
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( s )
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 ) 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 = strchr( s, '\n')) *temp = 0;
if (temp = strchr( s, '\r')) *temp = 0;
return( s );
}