#include <common.h>
#include <slip.h>
#include <udp.h>
#include <ip.h>
#include <icmp.h>
#include <tcp.h>
#include <strings.h>
#include <delays.h>

extern WORD rxpos=0;
extern WORD txpos=0;
extern BYTE rxbuffer[];
extern BYTE txbuffer[];
tcphead tcph;
ipheader ip;
ipstats ipstat;
extern test;
extern ulong tcpcounter;
void tcp_header();
void tcp_checksum();
bit tcpoptions=0;
WORD tcpdatalen;
tcpt tcptx;
void www();
void show_stats();

/************************************************************************
 * void tcp_receive()
 * This function is called by the IP layer when a TCP packet is
 * received. It performs every essential task of the TCP layer.
 * Incoming flag bits are read and responses generated accordingly
 ***********************************************************************/
void tcp_receive()
{
	WORD tcpseq[2];
	BYTE applayer=0;
	BYTE n;
	tcpoptions=0;
	/* increment TCP statistics counter */
	ipstat.tcprx++;
	/* copy various fields of the TCP header */
	copy_rx_word(&tcph.src);
	copy_rx_word(&tcph.dest);
	copy_rx_word(&tcph.seq[0]);
	copy_rx_word(&tcph.seq[1]);
	copy_rx_word(&tcph.ack[0]);
	copy_rx_word(&tcph.ack[1]);
	copy_rx_byte(&tcph.headerlen);
	tcph.headerlen = tcph.headerlen>>4;
	copy_rx_byte(&tcph.flags);
	copy_rx_word(&tcph.windowsize);
	copy_rx_word(&tcph.chksum);
	copy_rx_word(&tcph.urgent);
	
	/* discard any TCP options */
	for (n=5;n<tcph.headerlen;n++)
	{
		SkipLong();
	}
	
	tcpdatalen = ip.totallen - IPSIZE - (WORD)(tcph.headerlen<<2);
	
	/* if a reset or a segment with only an ACK is received, do nothing */
	if (tcph.flags & RST || tcph.flags == ACK) {
		return;
	}
	
	/* segment contains a SYN flag so treat is as a connection request */
	if (tcph.flags & SYN) {
		/* take value of 32 bit counter as Initial Sequence Number */
		tcptx.seq[0] = (WORD)(tcpcounter>>16);
		tcptx.seq[1] = (WORD)tcpcounter;
		/* and ACK the client's ISN plus one, for the SYN */
		tcptx.ack[0] = tcph.seq[0];
		tcptx.ack[1] = tcph.seq[1]+1;
		
		/* a connection request for the web server port */
		if (tcph.dest == HTTP_PORT) {
			/* acknowledge it */
			tcptx.action = SYN|ACK;
			/* and set flag to send TCP options */
			tcpoptions=1;
		/* connection request for an unused port */
		} else {
			/* so reset it */
			tcptx.action = RST|ACK;
			/* and increment statistics counters */
			ipstat.dropped++;
			ipstat.tcpdrop++;
		}
	}
	/* client is closing the connection */
	else if (tcph.flags & FIN) {
		tcptx.seq[0] = tcph.ack[0];
		tcptx.seq[1] = tcph.ack[1];
		tcptx.ack[0] = tcph.seq[0];
		tcptx.ack[1] = tcph.seq[1]+tcpdatalen+1;
		tcptx.action = ACK;
	}
	else if (tcph.flags & ACK) {
		tcptx.seq[0] = tcph.ack[0];
		tcptx.seq[1] = tcph.ack[1];
		tcptx.ack[0] = tcph.seq[0];
		tcptx.ack[1] = tcph.seq[1]+tcpdatalen;
		
		if (!tcpdatalen) return;	/* if the segment contains no data
						 * no action is required */
	
		/* we shouldn't get to this stage  unless the destination port
		 * is the HTTP port, but check just in case */
		if (tcph.dest == HTTP_PORT) {
			www();		/* call the www function to read the request
					 * and add HTTP data to the transmit buffer */
		}
		return;
	}
	
	/* place tcp header starting at 20 */
	tcp_header();
	/* reset transmit buffer position */
	txpos=0;
	/* and place IP header at start */
	ip_header(PROTTCP);
	/* calculate the TCP checksum and insert into header */
	tcp_checksum();
	/* finally, call the SLIP layer to send out the packet */
	slip_send(txbuffer,ip.totallen);
}

/************************************************************************
 * void tcp_header()
 * adds a TCP header to the transmit buffer, this function is
 * called either by an application of the tcp_receive function
 * If requested, TCP options will be sent (only MSS)
 ***********************************************************************/
void tcp_header()
{
	txpos=IPSIZE;
	tx_word(tcph.dest);		// source port
	tx_word(tcph.src);		// destination port
	tx_word(tcptx.seq[0]);	// sequence number
	tx_word(tcptx.seq[1]);	//
	tx_word(tcptx.ack[0]);	// ack number
	tx_word(tcptx.ack[1]);	//
	if (tcpoptions) {
		tx_byte(0x60);		// data offset 0101 << 4
	} else {
		tx_byte(0x50);
	}
	tx_byte(tcptx.action);
	tx_word(WINDOWSIZE);	// window size
	tx_word(0);				// checksum
	tx_word(0);				// urgent pointer

	if (tcpoptions) {
		tx_byte(0x02);		// kind
		tx_byte(0x04);		// length
		tx_word(MSS);		// MSS
		ip.totallen=IPSIZE+TCPHSIZE+4;
	} else {
		ip.totallen=IPSIZE+TCPHSIZE;
	}
}

/************************************************************************
 * void www()
 * called by the TCP layer when a data segment is received
 * for the HTTP_PORT.
 * The HTTP request string is validated before the requested
 * filename is checked and responded to accordingly.
 * Requests for an unknown filename result in a 404
 ***********************************************************************/
void www()
{
	BYTE request[4];
	
	/* check the incoming packet is a GET request, this is the only type which will be dealt with
	 * so issue a Bad Request HTTP error on anything else */
	if (rx_byte() != 'G' || rx_byte() != 'E' || rx_byte() != 'T' || 
		rx_byte() != ' ' || rx_byte() != '/') {
		tcptx.action = FIN|PSH|ACK;
		tcp_header();
		/* add the response to the transmit buffer */
		q_string("HTTP/1.0 400 Bad Request\r\nContent-type: text/html\r\n\r\n",53);
		q_string("<html><head><title>Bad Request</title></head><body>",51);	
		q_string("<h2>Bad Request</h2>",20);
		q_string("The request could not be understood.",36);
		q_string("</body></html>",14);				
	} else {
		/* read the requested filename */
		copy_rx_byte(&request[0]);
		copy_rx_byte(&request[1]);
		copy_rx_byte(&request[2]);
		copy_rx_byte(&request[3]);
		
		tcptx.action = FIN|PSH|ACK;
		tcp_header();
		
		/* client requested /   */
		if (request[0] == ' ') {
			q_string("HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n",44);
			q_string("<html><head><title>TCP/IP Stack</title></head><body>",52);	
			q_string("<h2>Welcome!</h2>",17);
			q_string("<p>This is an httpd running on a minimal TCP/IP stack",53);
			q_string(" written by Stuart Haslam.</p>",30);
			q_string("<p>The implementation provides at least partial support ",56);
			q_string("for the following protocols: ",29);
			q_string("SLIP, IP, ICMP, UDP, TCP, TFTP, HTTP.",37);
			q_string("<p><a href=\"/1\">TCP/IP packet statistics</a></p>",48);
			q_string("</body></html>",14);
		}
		/* client requested /1   */
		else if (request[0] == '1' && request[1] == ' ') {
			show_stats();
		}
		/* client requested /1?c
		 * there are some repeated lines here to overcome a bug in the compiler */
		else if (request[0] == '1' && request[1] == '?' && request[2] == 'c') {
			ipstat.rxpackets=0;
			ipstat.rxpackets=0;
			ipstat.txpackets=0;
			ipstat.tcptx=0;
			ipstat.tcprx=0;
			ipstat.udprx=0;
			ipstat.udptx=0;
			ipstat.icmprx=0;
			ipstat.icmptx=0;
			ipstat.icmpdrop=0;
			ipstat.tcpdrop=0;
			ipstat.udpdrop=0;
			ipstat.dropped=0;
			show_stats();
		}
		/* client requested unknown filename, send a 404 error */
		else {
			q_string("HTTP/1.0 404 Not Found\r\n\r\n",26);
			q_string("<html><head><title>TCP/IP Stack</title></head><body>",52);	
			q_string("<h2>404 - File Not Found!</h2>",30);
			q_string("The requested file could not be found.",38);
			q_string("<p><a href=\"/\">index</a></p>",28);
			q_string("</body></html>",14);	
		}
	}
	
	ip.totallen=txpos;	/* set length of outgoing packet */

	txpos=0;		/* reset transmit buffer position */
	ip_header(PROTTCP);	/* and place IP header at start */
	txpos+=20;
	
	tcp_checksum();	
	slip_send(txbuffer,ip.totallen);
}

/************************************************************************
 * void show_stats()
 * simple function to add HTTP headers and HTML content to the transmit
 * buffer, to display some basic packet statistics.
 ***********************************************************************/
void show_stats()
{
	q_string("HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n",44);
	q_string("<html><head><title>TCP/IP Stack</title>",39);
	q_string("<style>td{text-align:center;background-color:white}",51);
	q_string("th{background-color:#cccccc}</style>",36);
	q_string("</head><body>",13);	
	q_string("<h2>Packet Statistics</h2>",26);
	q_string("<table bgcolor=\"#000000\" cellspacing=1 cellpadding=7><tr>",57);
	q_string("<th>&nbsp;</th><th>Received</th><th>Transmitted</th><th>Dropped</th></tr>",73);
	q_string("<tr><th>Total</th><td>",22);
	q_num(ipstat.rxpackets);
	q_string("</td><td>",9);
	q_num(ipstat.txpackets);
	q_string("</td><td>",9);
	q_num(ipstat.dropped);
	q_string("</td></tr>",10);
	
	q_string("<tr><th>ICMP</th><td>",21);
	q_num(ipstat.icmprx);
	q_string("</td><td>",9);
	q_num(ipstat.icmptx);
	q_string("</td><td>",9);
	q_num(ipstat.icmpdrop);
	q_string("</td></tr>",10);
	
	q_string("<tr><th>UDP</th><td>",20);
	q_num(ipstat.udprx);
	q_string("</td><td>",9);
	q_num(ipstat.udptx);
	q_string("</td><td>",9);
	q_num(ipstat.udpdrop);
	q_string("</td></tr>",10);
			
	q_string("<tr><th>TCP</th><td>",20);
	q_num(ipstat.tcprx);
	q_string("</td><td>",9);
	q_num(ipstat.tcptx);
	q_string("</td><td>",9);
	q_num(ipstat.tcpdrop);
	q_string("</td></tr>",10);
			
	q_string("</table>",8);
	q_string("<p><a href=\"/\">index</a> - ",27);
	q_string("<a href=\"/1?c\">clear stats</a></p>",34);
	q_string("</body></html>",14);	
}

/************************************************************************
 * void tcp_checksum()
 * calculates the internet checksum of the packet waiting in the
 * transmit buffer, and inserts it into the header field
 ***********************************************************************/
void tcp_checksum()
{
	WORD csum;
	bool odd=0;
	/* increment transmit counter */
	ipstat.tcptx++;
	txpos=ip.totallen;
	
	/* odd length, so add pad byte */
	if (ip.totallen % 2) {
		tx_byte(0x00);
		odd=1;
	}
	/* add parts of pseudo header */
	tx_byte(IP1);
	tx_byte(IP2);
	tx_byte(IP3);
	tx_byte(IP4);
	tx_byte(ip.destaddr[0]);
	tx_byte(ip.destaddr[1]);
	tx_byte(ip.destaddr[2]);
	tx_byte(ip.destaddr[3]);
	tx_word(PROTTCP);
	tx_word(ip.totallen-IPSIZE);
	
	/* length is total length minus length of IP header (20)
	 * plus length of TCP pseudo header (12) */
	csum = chksm(&txbuffer[20],ip.totallen-8+odd);
	put_checksum(csum,&txbuffer[IPSIZE+16]);
}




syntax highlighted by Code2HTML, v. 0.9