home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.wwiv.com
/
ftp.wwiv.com.zip
/
ftp.wwiv.com
/
pub
/
PPPBCKP
/
SRC
/
SRC15B93.ZIP
/
WATTSRC.ZIP
/
PCTCP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1998-10-28
|
72KB
|
2,408 lines
/*
file: PCTCP.C (updated version)
This file is an "improved" version of the original version (Wat9609).
*/
/* To undo a fix simply undefine the define that goes with the fix. */
/* 28/10/1998 */
#define __UDP_PASSIVE_SOCKET_BUG__ /* Dashui Zhou <dszhou@cs.sdu.edu.cn> */
#define __UDP_HANDLER_OPTIMIZATION__ /* Dashui Zhou <dszhou@cs.sdu.edu.cn> */
#define __SPURIOUS_ACK_BUG__ /* Joe <jdhagen@itis.com> */
#define __SIGNED_BUG__ /* Morten Terstrup <MorTer@dk-online.dk> */
#define __UDP_BROADCAST_BUG__ /* CpV <crudesoft@yahoo.com> */
/*
This file is available from (28/10/1998)
www.geocities.com/SiliconValley/Lab/9247
the Web page of Ken Yap <kenyap@research.canon.com.au>
Please do NOT send bug reports to Ken Yap, but instead send them to
Clemens Valens at <crudesoft@yahoo.com>
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! NOTE: !!
!! !!
!! It is very well possible that the bugs mentioned and fixed here were !!
!! found and fixed before by others as well. The names listed are the !!
!! names of the people I know of. If there is someone not credited, !!
!! please let me know and I will straighten things out. !!
!! !!
!! Send WATTCP bugs and fixes to: crudesoft@yahoo.com !!
!! Please be clear and please explain. !!
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
End of non-WATTCP comments.
*/
/* 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 <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 char far *mono = (char far *)0xb0000000L;
static char far *colour = (char far *)0xb8000000L;
static initialized = 0;
static void (*system_yield)() = NULL;
extern int multihomes;
extern word _pktipofs;
void (*_dbugxmit)( sock_type *s, in_Header *inp, void *phdr, unsigned line ) = NULL;
void (*_dbugrecv)( sock_type *s, in_Header *inp, void *phdr, unsigned line ) = NULL;
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 */
word debug_on = 0;
/*
* 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)) {
#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;
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;
}
} else {
if (debug_on)
outs("IP Received BAD Checksum \n\r");
}
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 );
}
void tcp_set_debug_state( int x )
{
debug_on = x;
}
/* 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));
}
}
if (_dbugxmit) (*_dbugxmit)( (sock_type*)s, inp, udpp, 0 );
_eth_send( intel16( inp->length ));
return ( len );
}
/*
* udp_read - read data from buffer, does large buffering
*/
static int udp_read( udp_Socket *s, byte *datap, int maxlen )
{
int x;
if (maxlen < 0) maxlen = MAXINT;
if (( x = s->rdatalen ) > 0) {
if ( x > maxlen ) x = maxlen;
if ( x > 0 ) {
if (datap) movmem( s->rdata, datap, x );
if ( s->rdatalen -= x )
movmem( s->rdata + x, s->rdata, s->rdatalen);
}
}
return( x );
}
void _udp_cancel( in_Header *ip )
{
int len;
udp_Header *up;
udp_Socket *s;
/* match to a udp socket */
len = in_GetHdrlenBytes(ip);
up = (udp_Header *)((byte *)ip + len); /* udp frame pointer */
/* demux to active sockets */
for ( s = udp_allsocs; s; s = s->next )
if ( s->hisport != 0 &&
intel16( up->dstPort ) == s->hisport &&
intel16( up->srcPort ) == s->myport &&
intel( ip->destination ) == s->hisaddr ) break;
if ( !s ) {
/* demux to passive sockets */
for ( s = udp_allsocs; s; s = s->next )
if ( s->hisport == 0 && intel16( up->dstPort ) == s->myport ) break;
}
if (s) {
s->rdatalen = 0;
s->ip_type = 0;
}
}
void *_tcp_lookup( longword hisip, word hisport, word myport )
{
tcp_Socket *s;
for ( s = tcp_allsocs; s; s = s->next ) {
if ( ( myport == s->myport ) && /* always unique under WATTCP */
( hisport == s->hisport ) &&
( hisip == s->hisaddr ))
return( s );
}
return( NULL );
}
void _tcp_cancel( in_Header *ip, int code, char *msg, longword dummyip )
{
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;
#ifdef __UDP_PASSIVE_SOCKET_BUG__
int need_arp = 0; /* must be auto, 98.04.03 */
#endif
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->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) &&
#ifdef __UDP_HANDLER_OPTIMIZATION__
((temp & sin_mask) == (s->myaddr & sin_mask)) &&
#else
((intel( ip->destination ) & sin_mask) == (s->myaddr & sin_mask)) &&
#endif
(intel( ip->source ) == s->hisaddr )) break;
}
if (_dbugrecv) (*_dbugrecv)( (sock_type*)s, ip, up, 0);
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 );
/* Dashui Zhou 13/10/1998 bug fix applied, original line is moved to the end of the
* function and replaced here by the setting of the new flag.
* The problem was that an ARP answer packet overwrites the first datagram
* that arrives at a newly opend passive UDP socket. */
#ifdef __UDP_PASSIVE_SOCKET_BUG__
need_arp = 1; /* remember we need arp and postpone arp action. */
#else
_arp_resolve(intel(ip->source), &s->hisethaddr, 0);
#endif
// take on value of expected destination unless it
// is broadcast
#ifdef __UDP_HANDLER_OPTIMIZATION__
if ( (~temp & ~sin_mask) != 0 )
#else
if ( (~ip->destination & ~sin_mask) != 0 )
#endif
s->myaddr = intel( ip->destination );
}
break;
}
}
if ( !s ) {
/* demux to broadcast sockets */
for ( s = udp_allsocs; s; s = s->next )
#ifdef __UDP_BROADCAST_BUG__
/*
* Explanation
* ===========
* 's->hisaddr' contains the address of the host we communicate with
* through this port (thanks to udp_open()). So, if the socket was not
* opened in broadcast mode (s->hisaddr == -1uL) it will ignore broadcasts
* to this port. (not really my idea of a broadcast).
* This can be changed by replacing 's->hisaddr' by 'temp', which contains
* the destination address of the packet we are currently trying to match
* with a port.
*/
if ( (temp == 0xffffffffuL) &&
#else
if ( (s->hisaddr == 0xffffffffuL) &&
#endif
(intel16( up->dstPort ) == s->myport )) break;
}
if ( !s ) {
if (debug_on) outs("discarding...");
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;
}
}
#ifdef __UDP_PASSIVE_SOCKET_BUG__
/* Get his MAC address before return, 98.04.03 */
if (need_arp) _arp_resolve(intel(ip->source),&s->hisethaddr,0);
#endif
}
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 );
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)( (sock_type*)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 != 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 {
#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, &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;
}
#ifdef __SPURIOUS_ACK_BUG__
/*
* Eliminate the spurious ACK messages bug.
* For the window update, the length should be the
* data length only, so exclude the TCP header size
* -- Joe <jdhagen@itis.com> (this helped alot -gv)
*/
len -= tcp_GetDataOffset(tp) << 2;
#endif
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 )) {
#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 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) {
#ifdef __SIGNED_BUG__
/* Morten Terstrup <MorTer@dk-online.dk> found this signed bug. */
int size = min (s->datalen, s->window);
sendtotdata = size - s->unacked;
if (sendtotdata < 0) sendtotdata = 0;
startdata = s->unacked;
} else {
sendtotdata = min (s->datalen,s->window);
startdata = 0;
}
#else
/* 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;
}
#endif
/*
if (sendtotdata < 0) sendtotdata = 0;
*/
sendtotlen = 0; /* running count of what we've sent */
/* step through our packets */
for ( ippkt = 1; ippkt <= s->cwindow; ++ippkt ) {
/* adjust size for each packet */
senddatalen = min( sendtotdata, s->mss );
/*
sendpktlen = senddatalen + sizeof( tcp_Header ) + sizeof( in_Header );
inp->length = intel16( sendpktlen );
*/
/* tcp header */
tcpp->srcPort = intel16( s->myport );
tcpp->dstPort = intel16( s->hisport );
tcpp->seqnum = intel( s->seqnum + startdata ); /* unacked - no longer sendtotlen */
tcpp->acknum = intel( s->acknum );
tcpp->window = intel16( s->maxrdatalen - s->rdatalen );
tcpp->flags = intel16( s->flags | 0x5000 );
tcpp->checksum = 0;
tcpp->urgentPointer = 0;
/* do options if this is our first packet */
if ( s->flags & tcp_FlagSYN ) {
sendpktlen = sizeof( tcp_Header ) + sizeof( in_Header ) + 4;
tcpp->flags = intel16( intel16( tcpp->flags) + 0x1000 );
pkt->maxsegopt[0] = 0x0402;
pkt->maxsegopt[1] = intel16( s->mss );
dp += 4;
} else {
/* handle packets with data */
if (senddatalen > 0) {
sendpktlen = senddatalen + sizeof( tcp_Header ) + sizeof( in_Header );
/* get data from appropriate place */
if (s->queuelen) movmem(s->queue + startdata, dp, senddatalen );
else movmem(s->data + startdata, dp, senddatalen);
/* dp[ senddatalen ] = 0; */
} else {
/* handle no-data, not-first-SYN packets */
sendpktlen = sizeof( tcp_Header ) + sizeof( in_Header );
}
}
/* internet header */
memset( inp, 0, sizeof( in_Header ));
inp->ver = 4;
inp->hdrlen = 5;
inp->tos = s->tos;
inp->identification = intel16( ++ip_id ); /* was post inc */
// inp->frag = 0;
inp->ttl = 254;
inp->proto = TCP_PROTO;
inp->checksum = 0;
inp->source = intel( s->myaddr );
inp->destination = intel( s->hisaddr );
inp->length = intel16( sendpktlen );
inp->checksum = ~checksum( inp, sizeof(in_Header));
/* compute tcp checksum */
ph.src = inp->source; /* already INTELled */
ph.dst = inp->destination;
ph.mbz = 0;
ph.protocol = 6;
ph.length = intel16( sendpktlen - sizeof(in_Header));
/*
ph.checksum = checksum(&pkt->tcp, (sendpktlen - sizeof(in_Header) +1) & 0xfffe);
*/
ph.checksum = checksum(&pkt->tcp, sendpktlen - sizeof(in_Header));
tcpp->checksum = ~checksum(&ph, sizeof(ph));
if (_dbugxmit) (*_dbugxmit)( (sock_type *)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-1), 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
*/
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));
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
*/
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 );
}