#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 #include #include #include #include #include #include #include #include "wattcp.h" #include "elib.h" /* statics */ static tcp_ProcessData(tcp_Socket *s, tcp_Header *tp, int len); #ifndef WINDOWS static char far *mono = 0xb0000000L; static char far *colour = 0xb8000000L; #endif static initialized = 0; static void (*system_yield)() = NULL; void (*_dbugxmit)() = NULL; void (*_dbugrecv)() = 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 */ unsigned long far *realclock = (unsigned long far *)0x0000046cL; #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; /* * 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); } return 0; } /* * Checks for bugs in large model C compiler */ static largecheck( void *s, int size ) { if ( (word)(FP_OFF(s)) > (word)(-size)) { #ifndef WINDOWS outs("ERROR: C compiler sock size error\n"); #endif exit( 3 ); } } /* 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->ip_type = UDP_PROTO; if ( lport == 0 ) lport = ++next_udp_port; /* get a nonzero port val */ s->myport = lport; /* 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->ip_type = TCP_PROTO; s->mss = _mss; s->state = tcp_StateSYNSENT; s->timeout = set_timeout( tcp_LONGTIMEOUT ); s->rmaxdatalen = tcp_MaxBufSize; s->cwindow = 1; s->wwindow = 0; /* slow start VJ algorithm */ s->vj_sa = 4; /* about 250 ms */ if ( lport == 0 ) lport = ++next_tcp_port; /* get a nonzero port val */ 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->ip_type = TCP_PROTO; s->mss = _mss; s->rmaxdatalen = tcp_MaxBufSize; 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 ); 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->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)) { if ( s->karn_count == 0 ) { /* if really did timeout */ #ifdef DEBUG if(debug_on >1) printf("regular retran TO set unacked back to 0 from %u\n", s->unacked); #endif DEBUG 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; } 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; } } } } /* * 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 ) { if ( !my_ip_addr || (intel( ip->destination ) == my_ip_addr)){ switch ( ip->proto ) { case TCP_PROTO : tcp_handler(ip); break; case UDP_PROTO : udp_handler(ip); break; case ICMP_PROTO : icmp_handler(ip); break; } } #ifdef WINDOWS /* nothing */ #else } else { if (debug_on) outs("IP Received BAD Checksum \n\r"); #endif } break; case /*0x806*/ 0x608 : /* do arp */ _arp_handler(ip); break; } if (ip) _eth_free(ip); continue; } /* x = *realclock - start; timeout -= x; */ /* check for our outstanding packets */ tcp_Retransmitter(); return( (s) ? s->udp.ip_type : 0 ); } 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 || s->state == tcp_StateESTCL || s->state == tcp_StateCLOSWT ); } 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( my_ip_addr ); inp->destination = intel( s->hisaddr ); inp->checksum = ~checksum( inp, sizeof(in_Header))/* 0*/; /* Watstar */ /* 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); _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 (( x = s->rdatalen ) > 0) { if ( x > maxlen ) x = maxlen; if ( x > 0 ) { 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->myport && intel16( up->srcPort ) == s->hisport && intel( ip->source ) == 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; } } _tcp_cancel(in_Header *ip, int code) { 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->dstPort ) == s->myport && intel16( tp->srcPort ) == s->hisport && intel( ip->source ) == s->hisaddr ) { switch (code) { /* halt it */ case 1 : s->err_msg = "ICMP closed connection"; s->rdatalen = s->datalen = 0; s->unhappy = false; s->state = tcp_StateCLOSED; /* 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; } } } } static int tcp_read(tcp_Socket *s, byte *datap, int maxlen) { int x; if (( x = s->rdatalen) > 0) { if ( x > maxlen ) x = maxlen; if ( x > 0 ) { 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; /* no longer uses tcp_MaxData */ if ( s->state != tcp_StateESTAB ) len = 0; if ( len > (x = tcp_MaxBufSize - 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_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; udp_Socket *s; 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 ) { #ifdef WINDOWS /* nothing */ #else if (debug_on) outs("chain error in udp\r\n"); #endif } if ( s->hisport != 0 && intel16( up->dstPort ) == s->myport && intel16( up->srcPort ) == s->hisport && intel( ip->source ) == s->hisaddr ) break; } if (_dbugrecv) (*_dbugrecv)(s,ip,up); 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 ); } 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 ) { #ifdef WINDOWS /* nothing */ #else if (debug_on) outs("discarding..."); #endif 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); else { if (len > tcp_MaxBufSize ) len = tcp_MaxBufSize; 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; 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) { #ifndef WINDOWS mono[160]++; colour[160]++; mono[162] = colour[162] = (flags & tcp_FlagSYN) ? 'S' : ' '; mono[164] = colour[164] = (flags & tcp_FlagACK) ? 'A' : ' '; mono[166] = colour[166] = (flags & tcp_FlagFIN) ? 'F' : ' '; mono[168] = colour[168] = (flags & tcp_FlagRST) ? 'R' : ' '; #endif } /* demux to active sockets */ for ( s = tcp_allsocs; s; s = s->next ) { if ( s->safetysig != SAFETYTCP ) { #ifdef WINDOWS /* nothing */ #else if (debug_on) outs("chain error in tcp\r\n"); #endif } if ( s->hisport != 0 && intel16( tp->dstPort ) == s->myport && intel16( tp->srcPort ) == s->hisport && 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 )) break; } if (_dbugrecv) (*_dbugrecv)(s,ip,tp); if ( !s ) { if (!(flags & tcp_FlagRST)) tcp_rst( ip, tp ); return; } /* save his ethernet address */ movmem(&((((eth_Header *)ip) - 1)->source[0]), &s->hisethaddr[0], sizeof(eth_address)); 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 ) { #ifdef WINDOWS /* nothing */ #else if (debug_on) outs("bad tcp checksum \n\r"); #endif /* tester */ ph.checksum = checksum(tp, len); checksum(&ph, sizeof(ph)); tcp_sendsoon( s ); return; } /* reset code */ if ( flags & tcp_FlagRST ) { #ifdef WINDOWS /* nothing */ #else if (debug_on) outs("\7\7\7\7\7\7\7connection reset\n"); #endif 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 */ if ( flags & tcp_FlagSYN ) { 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 ) { 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; /* they must be told */ } 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))) { s->window = intel16( tp->window ); 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: 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 ++; 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 ) { 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; s->window = intel16( tp->window ); 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 = tcp_MaxBufSize - 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 ) { sendtotdata = min( s->datalen - s->unacked, s->window ); 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->rmaxdatalen - 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 | tcp_FlagACK)) == 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 = 0; inp->identification = intel16( ++ip_id ); /* was post inc */ inp->frag = 0; inp->ttl = 254; inp->proto = TCP_PROTO; inp->checksum = 0; inp->source = intel( my_ip_addr ); 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); if (debug_on > 1) { #ifndef WINDOWS mono[0]++; colour[0]++; mono[2] = colour[2] = (s->flags & tcp_FlagSYN) ? 'S' : ' '; mono[4] = colour[4] = (s->flags & tcp_FlagACK) ? 'A' : ' '; mono[6] = colour[6] = (s->flags & tcp_FlagFIN) ? 'F' : ' '; mono[8] = colour[8] = (s->flags & tcp_FlagRST) ? 'R' : ' '; #endif } 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 */ 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 ); 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; 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 ); tcpp->checksum = 0; tcpp->urgentPointer = 0; /* internet header */ inp->ver = 4; inp->hdrlen = 5; inp->tos = 0; 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*/; /* Watstar */ /* 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, (sendtotlen - sizeof(in_Header) +1) & 0xfffe); /* intel16(ph.length));*/ /* 0; *//* watstar */ tcpp->checksum = ~checksum(&ph, sizeof(ph)); if (_dbugxmit) (*_dbugxmit)(NULL,inp,tcpp); _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) ); } void sock_enqueue(sock_type *s, byte *dp, int len) { int written; 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 */ } } 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; sock_read( s, &ch, 1 ); return( ch ); } /* * sock_puts - does not append carriage return in binary mode * - returns length */ sock_puts( sock_type *s, byte *dp ) { int len; len = strlen( dp ); sock_flushnext( s ); sock_write( s, dp, len ); if (s->tcp.sock_mode & TCP_MODE_ASCII ) sock_write( s, "\r\n", 2 ); 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 < ( 3 * s->rmaxdatalen) / 4 ) 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 */ 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 */ word *np; char *src, *dest, *tempp; if ( n > tcp_MaxBufSize ) n = tcp_MaxBufSize; 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; } src_p[ *np ] = 0; /* terminate string */ strncpy( dp, src_p, n ); /* copy everything */ dp[ n-1 ] = 0; /* terminate */ if (temp = strchr( dp, '\n')) *temp = 0; if (temp2= strchr( dp, '\r')) *temp2= 0; len = strlen( dp ); /* skip if there were no crs or lfs ??? */ if (!temp2 && !temp && ( strlen( dp ) < n - 1) ) { if ( s->udp.ip_type == TCP_PROTO && s->tcp.state != tcp_StateESTAB && s->tcp.state != tcp_StateESTCL && s->tcp.state != tcp_StateCLOSWT ) /* take what we can get from this connection */; else { *dp = 0; return( 0 ); } } /* skip over \n and \r but stop on end */ #ifndef OLD if ( temp ) templen = FP_OFF( temp ) - FP_OFF( dp ); else if ( temp2 ) templen = FP_OFF( temp2 ) - FP_OFF( dp ); else templen = len + 1; if (templen) { ++templen; movmem( &src_p[ templen ], src_p, *np -= templen); } else * np = 0; #else temp = &src_p[ len + 1 ]; while ( *temp && ( ( *temp == '\n' ) || (*temp == '\r'))) temp++; if (*temp) movmem( temp, src_p, *np = strlen( temp )); else *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) == -1) return( -1 ); if (s->tcp.sock_mode & TCP_MODE_ASCII) { if ( len == tcp_MaxBufSize ) return( tcp_MaxBufSize ); /* check for terminating \n \r */ p = s->tcp.rdata; if ( strchr( p , '\n') || strchr( p, '\r')) 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 )) { s->tcp.err_msg = "Host refused connection"; status = -1; /* get an early reset */ break; } if ( ip_timer_expired( s )) { 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 )) { 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 { /* while ( sock_dataready( s )) sock_fastread( s, &status, 1 ); */ kbhit(); /* permit ^c */ if ( !tcp_tick( s )) { status = 1; break; } if ( ip_timer_expired( s )) { 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 ); }