www.pudn.com > tcpipstack.rar > TINYTC5.C


/* TINYTC5.C - Tiny-TCP source fragment 
	Part 5 of the TCP layer. 
 
 * Copyright (C) 1986, IMAGEN Corporation 
 * "This code may be duplicated in whole or in part provided that [1] there 
 * is no commercial gain involved in the duplication, and [2] that this 
 * copyright notice is preserved on all copies. Any other duplication 
 * requires written notice of the author." 
 
|===================================================================| 
|  The author of this code hereby licenses all duplication and/or   | 
|  modification of this code, in whole or in part, consistent with  | 
|  the terms of the GNU Library General Public License.             | 
|              - Geoffrey H. Cooper 10/29/97                        | 
|===================================================================| 
 
|===================================================================| 
|  My changes can be considered public domain.  Geof's statement    | 
|  will cover everything.                                           | 
|              - Rick Rodman 09/02/97                               | 
|===================================================================| 
 
	941012	rr	split into fragments 
*/ 
 
#include "tinytcp.h" 
 
#include  
 
#ifdef PC 
#include  
#endif 
 
/* ----- globals from tinytcp.c ------------------------------------- */ 
 
extern	IP_Address	local_IP_address;	/* local IP address */ 
 
extern	int		tcp_id;			/* TCP ID, gets incremented */ 
 
extern	struct tcp_Socket *	tcp_allsocs;	/* socket linklist */ 
 
/* ----- Process the data in an incoming packet. -------------------- */ 
 
/* Called from all states where incoming data can be received: established, 
	fin-wait-1, fin-wait-2 */ 
 
Void tcp_ProcessData( s, tp, len ) 
	struct tcp_Socket *s; 
	struct tcp_Header *tp; 
	int len; 
{ 
	S8	int		diff, x; 
	S8	Word		flags; 
	S8	Byte *		dp; 
 
	flags = ( Word ) rev_word( tp -> flags ); 
 
	/* Look at the difference between the value I'm acking and the 
		sequence number he's sending. The difference (if any) is the 
		length of data in the buffer which we have already seen. */ 
 
	diff = ( int )( s -> acknum - rev_longword( tp -> seqnum )); 
 
	/* I don't understand the following statement, unless it is supposed 
		to be compensating for options. There should not be data 
		on a SYN packet. */ 
 
	if( flags & TCPF_SYN ) diff--; 
 
	x = TCP_DATAOFFSET( tp ) << 2;	/* mult. times 4 */ 
	dp = (( Byte * ) tp ) + x;		/* point to data */ 
	len -= x;				/* subtract offset */ 
 
	if( diff >= 0 ) { 
		dp += diff;		/* Ignore stuff we've already seen */ 
		len -= diff;		/* subtract length of data we've 
						already seen */ 
		s -> acknum += len;	/* Add len to acknum to acknowledge 
						new data */ 
 
		if( s -> dataHandler != 0 )	/* cast removed */ 
			( s -> dataHandler )(( void * ) s, dp, len ); 
 
		else	printf( "got data, %d bytes\n", len ); 
 
		if( flags & TCPF_FIN ) { 
			s -> acknum++; 
#ifdef DEBUG_TCP 
			printf( "consumed fin.\n" ); 
#endif 
			switch(s -> state) { 
			case TS_ESTAB: 
				/* note: skip state CLOSEWT by automatically closing conn */ 
				x = TS_LASTACK; 
				s -> flags |= TCPF_FIN; 
				s -> unhappy = True; 
#ifdef DEBUG_TCP 
				printf( "sending fin.\n" ); 
#endif 
				break; 
			case TS_SFIN: 
				x = TS_RFIN; 
				break; 
			case TS_AFIN: 
				x = TS_TIMEWT; 
				break; 
			} 
			s -> state = x; 
		} 
	} else { 
#ifdef DEBUG_TCP 
		printf( "diff was negative, %d\n", diff ); 
#endif 
	} 
	s -> timeout = tcp_TIMEOUT; 
	tcp_Send( s ); 
} 
 
/* ----- Format and send an outgoing segment ------------------------ */ 
 
Void tcp_Send( s ) struct tcp_Socket *s; { 
 
	S8	struct tcp_Pseudoheader	ph; 
 
	S8	struct _pkt { 
			struct in_Header	in; 
			struct tcp_Header	tcp; 
			Longword		maxsegopt; 
		} * pkt; 
 
	S8	Byte *			dp; 
	S8	Longword		lw; 
 
	/* don't do it if the state is Closed or the socket is not 
		on the linklist */ 
 
	if(( s -> state == 0 ) || ( s -> state == TS_CLOSED )) 
		return; 
 
	pkt = ( struct _pkt * ) sed_FormatPacket( 
		( Byte * ) &( s -> hisethaddr.w[ 0 ] ), 0x800 ); 
	dp = ( Byte * ) &( pkt -> maxsegopt ); 
 
	if( s -> flags & TCPF_SYN ) { 
		/* Should not send data on SYN */ 
 
		pkt -> in.length = rev_word( sizeof( struct in_Header ) 
			+ sizeof( struct tcp_Header )); 
	} else { 
		pkt -> in.length = rev_word( sizeof( struct in_Header ) 
			+ sizeof( struct tcp_Header ) + s -> dataSize ); 
	} 
 
	/* tcp header */ 
 
	pkt -> tcp.srcPort = rev_word( s -> myport ); 
	pkt -> tcp.dstPort = rev_word( s -> hisport ); 
	pkt -> tcp.seqnum = rev_longword( s -> seqnum ); 
	pkt -> tcp.acknum = rev_longword( s -> acknum ); 
	pkt -> tcp.window = rev_word( 1024 ); 
 
		/* This 1024 is the size of data which I am willing to 
			receive */ 
 
	pkt -> tcp.flags = rev_word( s -> flags | 0x5000 ); 
	pkt -> tcp.checksum = 0; 
	pkt -> tcp.urgentPointer = 0; 
 
	if( s -> flags & TCPF_SYN ) { 
 
		/* If sending the options, add 1 DWORD to the data offset */ 
 
		pkt -> tcp.flags = rev_word( 
			rev_word( pkt -> tcp.flags ) + 0x1000 ); 
 
		/* Add 4 to the length */ 
 
		pkt -> in.length = rev_word( 
			rev_word( pkt -> in.length ) + 4 ); 
 
		/* Options. This is really: kind 02, length 04, value 1400. 
			See page 42. */ 
 
		pkt -> maxsegopt = rev_longword( 0x02040578 ); 
				/* 1400 bytes */ 
		dp += 4; 
	} else { 
		/* Only send the data if NOT a SYN. */ 
 
		Move( s -> data, dp, s -> dataSize ); 
	} 
 
	/* internet header */ 
 
	pkt -> in.vht = rev_word( 0x4500 ); 
				/* version 4, hdrlen 5, tos 0 */ 
	pkt -> in.identification = rev_word( tcp_id++ ); 
	pkt -> in.frag = 0; 
	pkt -> in.ttlProtocol = rev_word(( 250 << 8 ) + 6 ); 
	pkt -> in.checksum = 0; 
	pkt -> in.source = rev_longword( local_IP_address ); 
	pkt -> in.destination = rev_longword( s -> hisaddr ); 
	pkt -> in.checksum = rev_word( 
			( Word ) ~ checksum( ( Word * ) &pkt -> in, 
				sizeof( struct in_Header ))); 
 
	/* compute tcp checksum */ 
 
	ph.src = pkt -> in.source; 
	ph.dst = pkt -> in.destination; 
	ph.mbz = 0; 
	ph.protocol = 6; 
	ph.length = rev_word( 
		rev_word( pkt -> in.length ) - sizeof( struct in_Header )); 
 
	/* Actually, the idea is to compute the checksum of the pseudoheader 
		plus that of the tcp header and the data portion. By sticking 
		the *unreversed* checksum in the pseudoheader itself, we hope 
		to achieve the same result. I'm rather doubtful of this, 
		because the overflows will have been mangled. It seems to work 
		on receive, though. */ 
 
	lw = lchecksum( ( Word * ) &ph, sizeof( ph ) - 2 ); 
	while( lw & 0xFFFF0000L ) 
		lw = ( lw & 0xFFFFL ) + (( lw >> 16L ) & 0xFFFFL ); 
	lw += lchecksum( ( Word * ) &pkt -> tcp, 
		rev_word( ph.length )); 
	while( lw & 0xFFFF0000L ) 
		lw = ( lw & 0xFFFFL ) + (( lw >> 16L ) & 0xFFFFL ); 
 
	pkt -> tcp.checksum = rev_word( ~ ( Word )( lw & 0xFFFFL )); 
 
#ifdef DEBUG_TCP 
	tcp_DumpHeader( &pkt -> in, &pkt -> tcp, "Sending" ); 
#endif 
 
	sed_Send( rev_word( pkt -> in.length )); 
} 
 
/* ----- calculate Word checksum ------------------------------------ */ 
 
Word checksum( dp, length ) Word *dp; int length; { 
	S8	Longword	sum; 
 
	sum = lchecksum( dp, length ); 
 
	while( sum & 0xFFFF0000L ) 
		sum = ( sum & 0xFFFFL ) + (( sum >> 16L ) & 0xFFFFL ); 
 
	return ( Word )( sum & 0xFFFF ); 
} 
 
/* ----- compute a Longword sum of words in message ----------------- */ 
	/* (partial checksum) */ 
 
Longword lchecksum( dp, length ) Word *dp; int length; { 
	S8	int		len; 
	S8	Longword	sum; 
 
	len = length >> 1; 
	sum = 0L; 
 
	while ( len-- > 0 ) sum += rev_word( *dp++ ); 
	if( length & 1 ) sum += ( rev_word( *dp ) & 0xFF00 ); 
 
	return sum; 
} 
 
/* ----- Dump tcp protocol header of a packet ----------------------- */ 
 
Void tcp_DumpHeader( ip, tp, mesg ) 
	struct in_Header *ip; 
	struct tcp_Header *tp; 
	char *mesg; 
{ 
	static char *flags[] = { "FIN", "SYN", "RST", "PUSH", "ACK", "URG" }; 
	S8	int		len; 
	S8	Word		f; 
 
	len = rev_word( ip -> length ) 
		- (( TCP_DATAOFFSET( tp ) + IP_HLEN( ip )) << 2 ); 
	printf( "TCP: %s packet:\nSrcP: %x; DstP: %x; SeqN=%lx AckN=%lx Wind=%d DLen=%d\n", 
		mesg, 
		rev_word( tp -> srcPort ), 
		rev_word( tp -> dstPort ), 
		rev_longword( tp -> seqnum ), 
		rev_longword( tp -> acknum ), 
		rev_word( tp -> window ), 
		len ); 
	printf( "DO=%d, C=%x U=%d", 
		TCP_DATAOFFSET( tp ), 
		rev_word( tp -> checksum ), 
		rev_word( tp -> urgentPointer )); 
 
	/* output flags */ 
 
	f = rev_word( tp -> flags ); 
	for( len = 0; len < 6; len++ ) 
		if( f & ( 1 << len )) printf( " %s", flags[ len ] ); 
	printf( "\n" ); 
} 
 
#if 0 
/* ----- Move bytes from hither to yon ------------------------------ */ 
 
/* RR NOTE: This is a strange routine, presumably intended to allow strings 
	to be moved up or down. We will have to look very carefully at each 
	call to this routine to see why this is necessary. */ 
 
Void Move( src, dest, numbytes ) 
	Byte *src, *dest; 
	numbytes; 
{ 
	if( numbytes <= 0 ) return; 
	if( src < dest ) { 
		src += numbytes; 
		dest += numbytes; 
		do { 
			*--dest = *--src; 
		} while ( --numbytes > 0 ); 
	} else 
		do { 
			*dest++ = *src++; 
		} while ( --numbytes > 0 ); 
} 
#endif 
 
#ifndef BIG_ENDIAN 
/* ----- reverse Word ----------------------------------------------- */ 
 
Word rev_word( w ) Word w; { 
	return ( Word )((( w >> 8 ) & 0xFF ) 
		| (( w << 8 ) & 0xFF00 )); 
} 
 
/* ----- reverse Longword ------------------------------------------- */ 
 
Longword rev_longword( l ) Longword l; { 
	return ( Longword )((( l >> 24L ) & 0x000000FFL ) 
		| (( l >> 8L ) & 0x0000FF00L ) 
		| (( l << 8L ) & 0x00FF0000L ) 
		| (( l << 24L ) & 0xFF000000L )); 
} 
#endif	/* BIG_ENDIAN */ 
 
/* end of tinytc5.c */