summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c')
-rw-r--r--c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c915
1 files changed, 0 insertions, 915 deletions
diff --git a/c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c b/c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c
deleted file mode 100644
index 92e63ec6ba..0000000000
--- a/c/src/lib/libbsp/arm/nds/dswifi/arm9/source/sgIP_TCP.c
+++ /dev/null
@@ -1,915 +0,0 @@
-// DSWifi Project - sgIP Internet Protocol Stack Implementation
-// Copyright (C) 2005-2006 Stephen Stair - sgstair@akkit.org - http://www.akkit.org
-/******************************************************************************
-DSWifi Lib and test materials are licenced under the MIT open source licence:
-Copyright (c) 2005-2006 Stephen Stair
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-******************************************************************************/
-
-#include "sgIP_TCP.h"
-#include "sgIP_IP.h"
-#include "sgIP_Hub.h"
-#include "sys/socket.h"
-
-sgIP_Record_TCP * tcprecords;
-int port_counter;
-unsigned long lasttime;
-extern unsigned long volatile sgIP_timems;
-sgIP_TCP_SYNCookie synlist[SGIP_TCP_MAXSYNS];
-
-int numsynlist; // number of active entries in synlist (earliest first)
-
-void sgIP_TCP_Init() {
- tcprecords=0;
- numsynlist=0;
- port_counter=SGIP_TCP_FIRSTOUTGOINGPORT;
- lasttime=sgIP_timems;
-}
-
-void sgIP_TCP_Timer() { // scan through tcp records and resend anything necessary
- sgIP_Record_TCP * rec;
- int time,i,j;
- time=sgIP_timems-lasttime;
- lasttime=sgIP_timems;
- for(i=0;i<numsynlist;i++) {
- if(synlist[i].timenext<=time) {
- j=time-synlist[i].timenext;
- synlist[i].timebackoff*=2;
- if(synlist[i].timebackoff>SGIP_TCP_BACKOFFMAX) synlist[i].timebackoff=SGIP_TCP_BACKOFFMAX;
- if(j>synlist[i].timebackoff) synlist[i].timenext=0; else synlist[i].timenext=synlist[i].timebackoff-j;
- // resend SYN
- sgIP_TCP_SendSynReply(SGIP_TCP_FLAG_SYN|SGIP_TCP_FLAG_ACK,synlist[i].localseq,synlist[i].remoteseq,synlist[i].localip,synlist[i].remoteip,synlist[i].localport,synlist[i].remoteport,-1);
- } else {
- synlist[i].timenext-=time;
- }
- }
- rec=tcprecords;
- while(rec) {
- time=sgIP_timems-rec->time_last_action;
- switch(rec->tcpstate) {
- case SGIP_TCP_STATE_NODATA: // newly allocated [do nothing]
- case SGIP_TCP_STATE_UNUSED: // allocated & BINDed [do nothing]
- case SGIP_TCP_STATE_CLOSED: // Block is unused. [do nothing]
- case SGIP_TCP_STATE_LISTEN: // listening [do nothing]
- case SGIP_TCP_STATE_FIN_WAIT_2: // got ACK for our FIN, haven't got FIN yet. [do nothing]
- break;
- case SGIP_TCP_STATE_SYN_SENT: // connect initiated [resend syn]
- if(time>rec->time_backoff) {
- rec->retrycount++;
- if(rec->retrycount>=SGIP_TCP_MAXRETRY) {
- //error
- rec->errorcode=ECONNABORTED;
- rec->tcpstate=SGIP_TCP_STATE_CLOSED;
- break;
- }
- j=rec->time_backoff;
- j*=2;
- if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_SYN,0);
- rec->time_backoff=j; // preserve backoff
- }
- break;
- case SGIP_TCP_STATE_CLOSE_WAIT: // got FIN, wait for user code to close socket & send FIN [do nothing]
- case SGIP_TCP_STATE_ESTABLISHED: // syns have been exchanged [check for data in buffer, send]
- if(rec->want_shutdown==1 && rec->buf_tx_out==rec->buf_tx_in) { // oblige & shutdown
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_FIN | SGIP_TCP_FLAG_ACK,0);
- if(rec->tcpstate==SGIP_TCP_STATE_CLOSE_WAIT) {
- rec->tcpstate=SGIP_TCP_STATE_CLOSING;
- } else {
- rec->tcpstate=SGIP_TCP_STATE_FIN_WAIT_1;
- }
- rec->want_shutdown=2;
- break;
- }
- j=rec->buf_tx_out-rec->buf_tx_in;
- if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH;
- j+=(int)(rec->sequence-rec->sequence_next);
- if(j>0) {// never-sent bytes
- if(time>SGIP_TCP_TRANSMIT_DELAY) { // 1000 is an arbitrary constant.
- j=rec->buf_tx_out-rec->buf_tx_in;
- if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH;
- i=(int)(rec->txwindow-rec->sequence);
- if(j>i) j=i;
- i=sgIP_IP_MaxContentsSize(rec->destip)-20; // max tcp data size
- if(j>i) j=i;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,i);
- break;
- }
- }
- if(time>rec->time_backoff && rec->buf_tx_out!=rec->buf_tx_in) { // resend last packet
- j=rec->buf_tx_out-rec->buf_tx_in;
- if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH;
- i=(int)(rec->txwindow-rec->sequence);
- if(j>i) j=i;
- i=sgIP_IP_MaxContentsSize(rec->destip)-20; // max tcp data size
- if(j>i) j=i;
- j=rec->time_backoff;
- j*=2;
- if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,i);
- rec->time_backoff=j; // preserve backoff
- break;
- }
- break;
- case SGIP_TCP_STATE_FIN_WAIT_1: // sent a FIN, haven't got FIN or ACK yet. [resend fin]
- if(time>rec->time_backoff) {
- rec->retrycount++;
- if(rec->retrycount>=SGIP_TCP_MAXRETRY) {
- //error
- rec->errorcode=ETIMEDOUT;
- rec->tcpstate=SGIP_TCP_STATE_CLOSED;
- break;
- }
- j=rec->time_backoff;
- j*=2;
- if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_FIN,0);
- rec->time_backoff=j; // preserve backoff
- }
- break;
- case SGIP_TCP_STATE_CLOSING: // got FIN, waiting for ACK of our FIN [resend FINACK]
- if(time>rec->time_backoff) {
- rec->retrycount++;
- if(rec->retrycount>=SGIP_TCP_MAXRETRY) {
- //error
- rec->errorcode=ETIMEDOUT;
- rec->tcpstate=SGIP_TCP_STATE_CLOSED;
- break;
- }
- j=rec->time_backoff;
- j*=2;
- if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_FIN | SGIP_TCP_FLAG_ACK,0);
- rec->time_backoff=j; // preserve backoff
- }
- break;
- case SGIP_TCP_STATE_LAST_ACK: // wait for ACK of our last FIN [resend FIN]
- if(time>rec->time_backoff) {
- rec->retrycount++;
- if(rec->retrycount>=SGIP_TCP_MAXRETRY) {
- //error
- rec->errorcode=ETIMEDOUT;
- rec->tcpstate=SGIP_TCP_STATE_CLOSED;
- break;
- }
- j=rec->time_backoff;
- j*=2;
- if(j>SGIP_TCP_BACKOFFMAX) j=SGIP_TCP_BACKOFFMAX;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_FIN,0);
- rec->time_backoff=j; // preserve backoff
- }
- break;
- case SGIP_TCP_STATE_TIME_WAIT: // wait to ensure remote tcp knows it's been terminated. [reset in 2MSL]
- if(time>SGIP_TCP_TIMEMS_2MSL) {
- rec->errorcode=ESHUTDOWN;
- rec->tcpstate=SGIP_TCP_STATE_CLOSED;
- }
- break;
- }
-
-
- rec=rec->next;
- }
-}
-
-
-unsigned long sgIP_TCP_support_seqhash(unsigned long srcip, unsigned long destip, unsigned short srcport, unsigned short destport) {
- unsigned long hash;
- hash=destip;
- hash ^= destport * (0x02041089+sgIP_timems);
- hash ^= srcport * (0x080810422+(sgIP_timems<<1));
- hash ^= srcip * (0x48841221+(sgIP_timems<<3));
- hash ^= destip * (0x04020108+(sgIP_timems<<5));
- return hash;
-}
-int sgIP_TCP_GetUnusedOutgoingPort() {
- int myport,clear;
- sgIP_Record_TCP * rec;
- port_counter+=(sgIP_timems&1023); // semi-random
- if(port_counter>SGIP_TCP_LASTOUTGOINGPORT) port_counter=SGIP_TCP_FIRSTOUTGOINGPORT;
- while(1) {
- rec = tcprecords;
- myport=port_counter++;
- if(port_counter>SGIP_TCP_LASTOUTGOINGPORT) port_counter=SGIP_TCP_FIRSTOUTGOINGPORT;
- clear=1;
- while(rec) {
- if(rec->srcport==myport && rec->tcpstate!=SGIP_TCP_STATE_CLOSED && rec->tcpstate!=SGIP_TCP_STATE_NODATA) { clear=0; break; }
- rec=rec->next;
- }
- if(clear) return myport;
- }
-}
-
-int sgIP_TCP_CalcChecksum(sgIP_memblock * mb, unsigned long srcip, unsigned long destip, int totallength) {
- int checksum;
- if(!mb) return 0;
- if(mb->totallength&1) mb->datastart[mb->totallength]=0;
- checksum=sgIP_memblock_IPChecksum(mb,0,mb->totallength);
- // add in checksum of "faux header"
- checksum+=(destip&0xFFFF);
- checksum+=(destip>>16);
- checksum+=(srcip&0xFFFF);
- checksum+=(srcip>>16);
- checksum+=htons(totallength);
- checksum+=(6)<<8;
- checksum= (checksum&0xFFFF) +(checksum>>16);
- checksum= (checksum&0xFFFF) +(checksum>>16);
-
- return checksum;
-}
-
-
-int sgIP_TCP_ReceivePacket(sgIP_memblock * mb, unsigned long srcip, unsigned long destip) {
- if(!mb) return 0;
- sgIP_Header_TCP * tcp;
- int delta1,delta2, delta3,datalen, shouldReply;
- unsigned long tcpack,tcpseq;
- tcp = (sgIP_Header_TCP *) mb->datastart;
- // 01234567890123456789012345678901
- // TCPxxxxxxxx-xxxxxxxx,xxxx-xxxx
- //SGIP_DEBUG_MESSAGE(("TCP%08X-%08X,%04X-%04X",srcip,destip,tcp->srcport,tcp->destport));
- // -Lxxxx,Cxxxx,Fxx,hx,Axxxxxxxx
- //SGIP_DEBUG_MESSAGE(("-L%04X,C%04X,F%02X,h%X,A%08X",mb->totallength,tcp->checksum,tcp->tcpflags,tcp->dataofs_>>4,tcp->acknum));
- if(tcp->checksum!=0x0000 && sgIP_TCP_CalcChecksum(mb,srcip,destip,mb->totallength)!=0xFFFF) { // checksum is invalid!
- SGIP_DEBUG_MESSAGE(("TCP receive checksum incorrect"));
- sgIP_memblock_free(mb);
- return 0;
- }
- sgIP_Record_TCP * rec;
- rec=tcprecords;
- // find associated block.
- while(rec) {
- if(rec->srcport==tcp->destport && (rec->srcip==destip || rec->srcip==0)) {
- if((rec->tcpstate==SGIP_TCP_STATE_LISTEN && (tcp->tcpflags&SGIP_TCP_FLAG_SYN)) || rec->destport==tcp->srcport) break;
- }
- rec=rec->next;
- }
- if(!rec) { // could be completion of an incoming connection?
- tcpack=htonl(tcp->acknum);
- if(tcp->tcpflags&SGIP_TCP_FLAG_ACK) {
- int i,j;
- for(i=0;i<numsynlist;i++) {
- if(synlist[i].localseq+1==tcpack) { // oki! this is probably legit ;)
- rec=synlist[i].linked; // we have the data we need.
- // remove entry from synlist
- numsynlist--;
- i*=3;
- for(;i<numsynlist;i++) {
- synlist[i]=synlist[i+1]; // assume struct copy
- }
- for(j=0;j<rec->maxlisten;j++) if(!rec->listendata[j]) break; // find last entry in listen queue
- if(j==rec->maxlisten) { rec=0; break; } // discard this connection! we have no space in the listen queue.
-
- rec->listendata[j]=sgIP_TCP_AllocRecord();
- j++;
- if(j!=rec->maxlisten) rec->listendata[j]=0;
-
- rec=rec->listendata[j-1];
-
- // fill in data about the connection.
- rec->tcpstate=SGIP_TCP_STATE_ESTABLISHED;
- rec->time_last_action=sgIP_timems;
- rec->time_backoff=SGIP_TCP_GENRETRYMS; // backoff timer
- rec->srcip=destip;
- rec->destip=srcip;
- rec->srcport=tcp->destport;
- rec->destport=tcp->srcport;
- rec->sequence=htonl(tcp->acknum);
- rec->ack=htonl(tcp->seqnum);
- rec->sequence_next=rec->sequence;
- rec->rxwindow=rec->ack+1400; // last byte in receive window
- rec->txwindow=rec->sequence+htons(tcp->window);
-
- sgIP_memblock_free(mb);
- return 0;
- }
- }
- }
- }
- if(!rec) { // we don't have a clue what this one is.
-#ifndef SGIP_TCP_STEALTH
- // send a RST
- sgIP_TCP_SendSynReply(SGIP_TCP_FLAG_RST,ntohl(tcp->acknum),0,destip,srcip,tcp->destport,tcp->srcport,0);
-#endif
- sgIP_memblock_free(mb);
- return 0;
- }
- // check sequence and ACK numbers, to ensure they're in range.
- tcpack=htonl(tcp->acknum);
- tcpseq=htonl(tcp->seqnum);
- datalen=mb->totallength-(tcp->dataofs_>>4)*4;
- shouldReply=0;
- if(tcp->tcpflags&SGIP_TCP_FLAG_RST) { // verify if rst is legit, and act on it.
- // check seq against receive window
- delta1=(int)(tcpseq-rec->ack);
- delta2=(int)(rec->rxwindow-tcpseq);
- if(delta1<0 || delta2<0 || rec->tcpstate==SGIP_TCP_STATE_LISTEN) {
- // out of range, ignore
- } else {
- // in range! reset connection.
- rec->errorcode=ECONNRESET;
- rec->tcpstate=SGIP_TCP_STATE_CLOSED;
- }
- sgIP_memblock_free(mb);
- return 0;
- }
-
- if((tcp->tcpflags&SGIP_TCP_FLAG_ACK) && !(tcp->tcpflags&SGIP_TCP_FLAG_SYN)) { // doesn't work very well with SYN.
- // verify ack value (checking ack sequence vs transmit window)
- delta1=(int)(tcpack-rec->sequence);
- delta2=(int)(rec->txwindow-tcpack);
- if(delta1<0 || delta2<0) { // invalid ack range, discard packet
- sgIP_memblock_free(mb);
- return 0;
- }
- delta2=tcpack-rec->sequence;
- rec->sequence=tcpack;
- delta2+=rec->buf_tx_in;
- if(delta2>=SGIP_TCP_TRANSMITBUFFERLENGTH) delta2-=SGIP_TCP_TRANSMITBUFFERLENGTH;
- rec->buf_tx_in=delta2;
- if(delta1>0) shouldReply=1;
- }
- rec->txwindow=rec->sequence+htons(tcp->window);
-
- // now, decide what to do with our nice new shiny memblock...
-
- // for most states, receive data
- switch(rec->tcpstate) {
- case SGIP_TCP_STATE_NODATA: // newly allocated
- case SGIP_TCP_STATE_UNUSED: // allocated & BINDed
- case SGIP_TCP_STATE_CLOSED: // Block is unused.
- case SGIP_TCP_STATE_LISTEN: // listening
- case SGIP_TCP_STATE_TIME_WAIT: // wait to ensure remote tcp knows it's been terminated.
- case SGIP_TCP_STATE_SYN_SENT: // connect initiated
- case SGIP_TCP_STATE_CLOSE_WAIT: // got FIN, wait for user code to close socket & send FIN
- case SGIP_TCP_STATE_CLOSING: // got FIN, waiting for ACK of our FIN
- case SGIP_TCP_STATE_LAST_ACK: // wait for ACK of our last FIN
- break;
- case SGIP_TCP_STATE_SYN_RECEIVED: // spawned from listen socket; or from syn sent.
- case SGIP_TCP_STATE_ESTABLISHED: // syns have been exchanged
- case SGIP_TCP_STATE_FIN_WAIT_1: // sent a FIN, haven't got FIN or ACK yet.
- case SGIP_TCP_STATE_FIN_WAIT_2: // got ACK for our FIN, haven't got FIN yet.
- if(tcp->tcpflags&SGIP_TCP_FLAG_ACK) {
- // check end of incoming data against receive window
- delta1=(int)(tcpseq+datalen-rec->ack); // check end of data vs start of window (>=0, end of data is equal to or after start of unreceived data)
- delta2=(int)(rec->rxwindow-tcpseq-datalen); // check end of data vs end of window (>=0, end of data is equal to or before end of rx window)
- delta3=(int)(rec->ack-tcpseq); // check start of data vs start of window (>=0, start of data is equal or before the next expected byte)
- if(delta1<0 || delta2<0 || delta3<0) {
- if(delta1>-SGIP_TCP_RECEIVEBUFFERLENGTH) { // ack it anyway, they got lost on the retard bus.
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0);
- }
- break; // out of range, they should know better.
- }
- {
- int datastart=(tcp->dataofs_>>4)*4;
- delta1=(int)(tcpseq-rec->ack);
- if(delta1<0) { // data is partly ack'd...just copy what we need.
- datastart-=delta1;
- datalen+=delta1;
- }
- // copy data into the fifo
- rec->ack+=datalen;
- delta1=datalen;
- while(datalen>0) { // don't actually need to check the rx buffer length, if the ack check approved it, it will be in range (not overflow) by default
- delta2=SGIP_TCP_RECEIVEBUFFERLENGTH-rec->buf_rx_out; // number of bytes til the end of the buffer
- if(datalen<delta2) delta2=datalen;
- sgIP_memblock_CopyToLinear(mb,rec->buf_rx+rec->buf_rx_out,datastart,delta2);
- datalen-=delta2;
- datastart+=delta2;
- rec->buf_rx_out += delta2;
- if(rec->buf_rx_out>=SGIP_TCP_RECEIVEBUFFERLENGTH) rec->buf_rx_out-=SGIP_TCP_RECEIVEBUFFERLENGTH;
- }
- if(rec->tcpstate==SGIP_TCP_STATE_FIN_WAIT_1 || rec->tcpstate==SGIP_TCP_STATE_FIN_WAIT_2) break;
- if(shouldReply || delta1>=0) { // send a packet in reply, ha!
- delta1=rec->buf_tx_out-rec->buf_tx_in;
- if(delta1<0) delta1+=SGIP_TCP_TRANSMITBUFFERLENGTH;
- delta2=(int)(rec->txwindow-rec->sequence);
- if(delta1>delta2) delta1=delta2;
- delta2=sgIP_IP_MaxContentsSize(rec->destip)-20; // max tcp data size
- if(delta1>delta2) delta1=delta2;
- if(delta1>=0) { // could be less than 0, but very odd.
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,delta1);
- }
- }
- }
- }
- }
-
- // decide what to do with the others
- switch(rec->tcpstate) {
- case SGIP_TCP_STATE_NODATA: // newly allocated
- case SGIP_TCP_STATE_UNUSED: // allocated & BINDed
- case SGIP_TCP_STATE_CLOSED: // Block is unused.
- break; // can't do anything in these states.
- case SGIP_TCP_STATE_LISTEN: // listening
- if(tcp->tcpflags&SGIP_TCP_FLAG_SYN) { // other end requesting a connection
- if(numsynlist==SGIP_TCP_MAXSYNS) {
- numsynlist--;
- for(delta1=0;delta1<numsynlist;delta1++) {
- synlist[delta1]=synlist[delta1+1]; // assume struct copy
- }
- }
- {
- unsigned long myseq,myport;
- myport=tcp->destport;
- myseq=sgIP_TCP_support_seqhash(srcip,destip,tcp->srcport,myport);
- // send relevant synack
- sgIP_TCP_SendSynReply(SGIP_TCP_FLAG_SYN|SGIP_TCP_FLAG_ACK,myseq,tcpseq+1,destip,srcip,myport,tcp->srcport,-1);
- synlist[numsynlist].localseq=myseq;
- synlist[numsynlist].timebackoff=SGIP_TCP_SYNRETRYMS;
- synlist[numsynlist].timenext=SGIP_TCP_SYNRETRYMS;
- synlist[numsynlist].linked=rec;
- synlist[numsynlist].remoteseq=tcpseq+1;
- synlist[numsynlist].remoteip=srcip;
- synlist[numsynlist].localip=destip;
- synlist[numsynlist].localport=myport;
- synlist[numsynlist].remoteport=tcp->srcport;
- numsynlist++;
- }
- }
- break;
- case SGIP_TCP_STATE_SYN_SENT: // connect initiated
- switch(tcp->tcpflags&(SGIP_TCP_FLAG_SYN|SGIP_TCP_FLAG_ACK)) {
- case SGIP_TCP_FLAG_SYN | SGIP_TCP_FLAG_ACK: // both flags set
- rec->ack=tcpseq+1;
- rec->sequence=tcpack;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0);
- rec->tcpstate=SGIP_TCP_STATE_ESTABLISHED;
- rec->retrycount=0;
- break;
- case SGIP_TCP_FLAG_SYN: // just got a syn...
- rec->ack=tcpseq+1;
- rec->sequence=tcpack;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0);
- rec->tcpstate=SGIP_TCP_STATE_SYN_RECEIVED;
- rec->retrycount=0;
- break;
- }
- break;
- case SGIP_TCP_STATE_SYN_RECEIVED: // spawned from listen socket; or from syn sent.
- if(tcp->tcpflags&SGIP_TCP_FLAG_ACK) {
- rec->tcpstate=SGIP_TCP_STATE_ESTABLISHED;
- rec->retrycount=0;
- }
- break;
- case SGIP_TCP_STATE_ESTABLISHED: // syns have been exchanged
- if(tcp->tcpflags&SGIP_TCP_FLAG_FIN) {
- // check sequence against next ack number
- delta1=(int)(tcpseq-rec->ack);
- //delta2=(int)(rec->rxwindow-tcpseq);
- if(delta1<0 || delta1>0) break; // out of range, they should know better.
- // this is the end...
- rec->tcpstate=SGIP_TCP_STATE_CLOSE_WAIT;
- rec->ack=tcpseq+datalen+1;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0);
-
- }
- break;
- case SGIP_TCP_STATE_FIN_WAIT_1: // sent a FIN, haven't got FIN or ACK yet.
- switch(tcp->tcpflags&(SGIP_TCP_FLAG_FIN|SGIP_TCP_FLAG_ACK)) {
- case SGIP_TCP_FLAG_FIN:
- // check sequence against next ack number
- delta1=(int)(tcpseq-rec->ack);
- //delta2=(int)(rec->rxwindow-tcpseq);
- if(delta1<0 || delta1>0) break; // out of range, they should know better.
-
- rec->tcpstate=SGIP_TCP_STATE_CLOSING;
- rec->ack=tcpseq+datalen+1;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0);
- break;
- case SGIP_TCP_FLAG_ACK: // already checked ack against appropriate window
- rec->tcpstate=SGIP_TCP_STATE_FIN_WAIT_2;
- break;
- case (SGIP_TCP_FLAG_FIN | SGIP_TCP_FLAG_ACK): // already checked ack, check sequence though
- // check sequence against next ack number
- delta1=(int)(tcpseq-rec->ack);
- //delta2=(int)(rec->rxwindow-tcpseq);
- if(delta1<0 || delta1>0) break; // out of range, they should know better.
- rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT;
- rec->ack=tcpseq+datalen+1;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0);
- break;
- }
- break;
- case SGIP_TCP_STATE_FIN_WAIT_2: // got ACK for our FIN, haven't got FIN yet.
- if(tcp->tcpflags&SGIP_TCP_FLAG_FIN) {
- // check sequence against next ack number
- delta1=(int)(tcpseq-rec->ack);
- //delta2=(int)(rec->rxwindow-tcpseq);
- if(delta1<0 || delta1>0) break; // out of range, they should know better.
-
- rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT;
- rec->ack=tcpseq+datalen+1;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0);
- }
- break;
- case SGIP_TCP_STATE_CLOSE_WAIT: // got FIN, wait for user code to close socket & send FIN
- if(tcp->tcpflags&SGIP_TCP_FLAG_FIN) {
- // check sequence against next ack number
- delta1=(int)(tcpseq-rec->ack);
- //delta2=(int)(rec->rxwindow-tcpseq);
- if(delta1<0 || delta1>0) break; // out of range, they should know better.
-
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); // they still don't seem to have got our ack, we'll send it again.
- }
- break;
- case SGIP_TCP_STATE_CLOSING: // got FIN, waiting for ACK of our FIN
- switch(tcp->tcpflags&(SGIP_TCP_FLAG_FIN|SGIP_TCP_FLAG_ACK)) {
- case SGIP_TCP_FLAG_FIN:
- // check sequence against receive window
- delta1=(int)(tcpseq-rec->ack);
- delta2=(int)(rec->rxwindow-tcpseq);
- if(delta1<1 || delta2<0) break; // out of range, they should know better.
-
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); // resend their ack.
- break;
- case SGIP_TCP_FLAG_ACK: // already checked ack against appropriate window
- rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT;
- break;
- case (SGIP_TCP_FLAG_FIN | SGIP_TCP_FLAG_ACK): // already checked ack, check sequence though
- // check sequence against receive window
- delta1=(int)(tcpseq-rec->ack);
- delta2=(int)(rec->rxwindow-tcpseq);
- if(delta1<1 || delta2<0) break; // out of range, they should know better.
- rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0);
- break;
- }
- break;
-
- case SGIP_TCP_STATE_LAST_ACK: // wait for ACK of our last FIN
- if(tcp->tcpflags&SGIP_TCP_FLAG_ACK) {
- rec->tcpstate=SGIP_TCP_STATE_TIME_WAIT;
- }
- break;
- case SGIP_TCP_STATE_TIME_WAIT: // wait to ensure remote tcp knows it's been terminated.
- if(tcp->tcpflags&SGIP_TCP_FLAG_FIN) {
- // check sequence against receive window
- delta1=(int)(tcpseq-rec->ack);
- delta2=(int)(rec->rxwindow-tcpseq);
- if(delta1<1 || delta2<0) break; // out of range, they should know better.
-
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0); // send 'em a grat ACK
- }
- break;
- }
- sgIP_memblock_free(mb);
- return 0;
-}
-
-sgIP_memblock * sgIP_TCP_GenHeader(sgIP_Record_TCP * rec, int flags, int datalength) {
- sgIP_memblock * mb = sgIP_memblock_alloc(datalength+20+sgIP_IP_RequiredHeaderSize());
- int windowlen;
- if(!mb) return 0;
- sgIP_memblock_exposeheader(mb,-sgIP_IP_RequiredHeaderSize()); // hide IP header space for later
- sgIP_Header_TCP * tcp = (sgIP_Header_TCP *) mb->datastart;
- tcp->srcport=rec->srcport;
- tcp->destport=rec->destport;
- tcp->seqnum=htonl(rec->sequence);
- tcp->acknum=htonl(rec->ack);
- tcp->tcpflags=flags;
- tcp->urg_ptr=0; // no support for URG data atm.
- tcp->checksum=0;
- tcp->dataofs_=5<<4; // header length == 20 (5*32bit)
- windowlen=rec->buf_rx_out-rec->buf_rx_in;
- if(windowlen<0) windowlen+=SGIP_TCP_RECEIVEBUFFERLENGTH; // we now have the amount in the buffer
- windowlen = SGIP_TCP_RECEIVEBUFFERLENGTH-windowlen-1;
- if(windowlen<0) windowlen=0;
- if(flags&SGIP_TCP_FLAG_ACK) rec->want_reack = windowlen<SGIP_TCP_REACK_THRESH; // indicate an additional ack should be sent when we have more space in the buffer.
- if(windowlen>65535) windowlen=65535;
- if(windowlen>1400) windowlen=1400; // don't want to deal with IP fragmentation.
- rec->rxwindow=rec->ack+windowlen; // last byte in receive window
- tcp->window=htons(windowlen);
- return mb;
-}
-void sgIP_TCP_FixChecksum(unsigned long srcip, unsigned long destip, sgIP_memblock * mb) {
- int checksum;
- if(!mb) return;
- sgIP_Header_TCP * tcp;
- tcp = (sgIP_Header_TCP *) mb->datastart;
- tcp->checksum=0;
- checksum=sgIP_memblock_IPChecksum(mb,0,mb->totallength);
-
- // add in checksum of "faux header"
- checksum+=(destip&0xFFFF);
- checksum+=(destip>>16);
- checksum+=(srcip&0xFFFF);
- checksum+=(srcip>>16);
- checksum+=htons(mb->totallength);
- checksum+=(6)<<8;
- checksum=(checksum&0xFFFF) + (checksum>>16);
- checksum=(checksum&0xFFFF) + (checksum>>16);
-
- checksum = ~checksum;
- if(checksum==0) checksum=0xFFFF;
- tcp->checksum=checksum;
-}
-
-int sgIP_TCP_SendPacket(sgIP_Record_TCP * rec, int flags, int datalength) { // data sent is taken directly from the TX fifo.
- int i,j,k;
- if(!rec) return 0;
- SGIP_INTR_PROTECT();
-
- j=rec->buf_tx_out-rec->buf_tx_in;
- if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH;
- if(datalength>j) datalength=j;
- sgIP_memblock * mb =sgIP_TCP_GenHeader(rec,flags,datalength);
- if(!mb) {
- SGIP_INTR_UNPROTECT();
- return 0;
- }
- j=20; // destination offset in memblock for data
- rec->sequence_next=rec->sequence+datalength;
- k=rec->buf_tx_in;
- while(datalength>0) {
- i=SGIP_TCP_TRANSMITBUFFERLENGTH-rec->buf_tx_in;
- if(i>datalength)i=datalength;
- sgIP_memblock_CopyFromLinear(mb,rec->buf_tx+k,j,i);
- k+=i;
- if(k>=SGIP_TCP_TRANSMITBUFFERLENGTH) k-=SGIP_TCP_TRANSMITBUFFERLENGTH;
- j+=i;
- datalength-=i;
- }
-
- sgIP_TCP_FixChecksum(rec->srcip,rec->destip,mb);
- sgIP_IP_SendViaIP(mb,6,rec->srcip,rec->destip);
-
- rec->time_last_action=sgIP_timems; // semi-generic timer.
- rec->time_backoff=SGIP_TCP_GENRETRYMS; // backoff timer
- SGIP_INTR_UNPROTECT();
- return 0;
-}
-
-int sgIP_TCP_SendSynReply(int flags,unsigned long seq, unsigned long ack, unsigned long srcip, unsigned long destip, int srcport, int destport, int windowlen) {
- SGIP_INTR_PROTECT();
-
- sgIP_memblock * mb = sgIP_memblock_alloc(20+sgIP_IP_RequiredHeaderSize());
- if(!mb) {
- SGIP_INTR_UNPROTECT();
- return 0;
- }
- sgIP_memblock_exposeheader(mb,-sgIP_IP_RequiredHeaderSize()); // hide IP header space for later
- sgIP_Header_TCP * tcp = (sgIP_Header_TCP *) mb->datastart;
- tcp->srcport=srcport;
- tcp->destport=destport;
- tcp->seqnum=htonl(seq);
- tcp->acknum=htonl(ack);
- tcp->tcpflags=flags;
- tcp->urg_ptr=0; // no support for URG data atm.
- tcp->checksum=0;
- tcp->dataofs_=5<<4; // header length == 20 (5*32bit)
-
- if(windowlen<0 || windowlen>1400) windowlen=1400; // don't want to deal with IP fragmentation.
- tcp->window=htons(windowlen);
-
- sgIP_TCP_FixChecksum(srcip,destip,mb);
- sgIP_IP_SendViaIP(mb,6,srcip,destip);
-
- SGIP_INTR_UNPROTECT();
- return 0;
-}
-
-sgIP_Record_TCP * sgIP_TCP_AllocRecord() {
- SGIP_INTR_PROTECT();
- sgIP_Record_TCP * rec;
- rec = sgIP_malloc(sizeof(sgIP_Record_TCP));
- if(rec) {
- rec->buf_oob_in=0;
- rec->buf_oob_out=0;
- rec->buf_rx_in=0;
- rec->buf_rx_out=0;
- rec->buf_tx_in=0;
- rec->buf_tx_out=0;
- rec->tcpstate=0;
- rec->next=tcprecords;
- tcprecords=rec;
- rec->maxlisten=0;
- rec->srcip=0;
- rec->retrycount=0;
- rec->errorcode=0;
- rec->listendata=0;
- rec->want_shutdown=0;
- rec->want_reack=0;
- }
- SGIP_INTR_UNPROTECT();
- return rec;
-}
-void sgIP_TCP_FreeRecord(sgIP_Record_TCP * rec) {
- if(!rec) return;
- SGIP_INTR_PROTECT();
- sgIP_Record_TCP * t;
- int i,j;
- rec->tcpstate=0;
- if(tcprecords==rec) {
- tcprecords=rec->next;
- } else {
- t=tcprecords;
- while(t) {
- if(t->next==rec) {
- t->next=rec->next;
- break;
- }
- t=t->next;
- }
- }
- if(rec->listendata) {
- for(i=0;i<rec->maxlisten;i++) {
- if(!rec->listendata[i]) break;
- sgIP_TCP_FreeRecord(rec->listendata[i]);
- }
- // kill any possible waiting elements in the SYN chain.
- j=0;
- for(i=0;i<numsynlist;i++) {
- if(j!=i) {
- synlist[j]=synlist[i];
- }
- if(synlist[i].linked==rec) j--;
- j++;
- }
- numsynlist=j;
- sgIP_free(rec->listendata);
- }
- sgIP_free(rec);
-
- SGIP_INTR_UNPROTECT();
-}
-
-int sgIP_TCP_Bind(sgIP_Record_TCP * rec, int srcport, unsigned long srcip) {
- if(!rec) return 0;
- SGIP_INTR_PROTECT();
- if(rec->tcpstate==SGIP_TCP_STATE_NODATA) {
- rec->srcip=srcip;
- rec->srcport=srcport;
- rec->tcpstate=SGIP_TCP_STATE_UNUSED;
- }
- SGIP_INTR_UNPROTECT();
- return 0;
-}
-int sgIP_TCP_Listen(sgIP_Record_TCP * rec, int maxlisten) {
- if(!rec) return SGIP_ERROR(EINVAL);
- if(rec->tcpstate!=SGIP_TCP_STATE_UNUSED) return SGIP_ERROR(EINVAL);
- int err;
- SGIP_INTR_PROTECT();
- if(rec->maxlisten!=0) { // we're *already* listening.
- err=0;
- } else {
- err=0;
- if(maxlisten<=0) maxlisten=1;
- rec->maxlisten=maxlisten;
- rec->listendata = (sgIP_Record_TCP **) sgIP_malloc(maxlisten*4); // pointers to TCP records, 0-terminated list.
- if(!rec->listendata) { rec->maxlisten=0; err=0; } else {rec->tcpstate=SGIP_TCP_STATE_LISTEN; rec->listendata[0]=0;}
- }
- SGIP_INTR_UNPROTECT();
- return err;
-}
-
-sgIP_Record_TCP * sgIP_TCP_Accept(sgIP_Record_TCP * rec) {
- if(!rec) return (sgIP_Record_TCP *)SGIP_ERROR0(EINVAL);
- if(rec->tcpstate!=SGIP_TCP_STATE_LISTEN) return (sgIP_Record_TCP *)SGIP_ERROR0(EINVAL);
- int err,i;
- sgIP_Record_TCP * t;
- SGIP_INTR_PROTECT();
- if(!rec->listendata) err=SGIP_ERROR0(EINVAL);
- else {
- if(!rec->listendata[0]) {
- err=SGIP_ERROR0(EWOULDBLOCK);
- } else {
- t=rec->listendata[0];
- for(i=1;i<rec->maxlisten;i++) {
- rec->listendata[i-1]=rec->listendata[i];
- }
- rec->listendata[i-1]=0;
- SGIP_INTR_UNPROTECT();
- return t;
- }
- }
-
- SGIP_INTR_UNPROTECT();
- return 0;
-}
-
-int sgIP_TCP_Close(sgIP_Record_TCP * rec) {
- if(!rec) return SGIP_ERROR(EINVAL);
- SGIP_INTR_PROTECT();
- if(rec->want_shutdown==0) rec->want_shutdown=1;
- SGIP_INTR_UNPROTECT();
- return 0;
-}
-int sgIP_TCP_Connect(sgIP_Record_TCP * rec, unsigned long destip, int destport) {
- if(!rec) return SGIP_ERROR(EINVAL);
- SGIP_INTR_PROTECT();
- if(rec->tcpstate==SGIP_TCP_STATE_NODATA) { // need to bind a local address
- rec->srcip=sgIP_IP_GetLocalBindAddr(0,destip);
- rec->srcport=htons(sgIP_TCP_GetUnusedOutgoingPort());
- rec->destip=destip;
- rec->destport=destport;
- } else if(rec->tcpstate==SGIP_TCP_STATE_UNUSED) { // already bound to a local address.
- rec->srcip=sgIP_IP_GetLocalBindAddr(rec->srcip,destip);
- rec->destip=destip;
- rec->destport=destport;
- } else {
- SGIP_INTR_UNPROTECT();
- return SGIP_ERROR(EINVAL);
- }
-
- // send a SYN packet, and advance the state of the connection
- rec->sequence=sgIP_TCP_support_seqhash(rec->srcip,rec->destip,rec->srcport,rec->destport);
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_SYN,0);
- rec->retrycount=0;
- rec->tcpstate=SGIP_TCP_STATE_SYN_SENT;
-
- SGIP_INTR_UNPROTECT();
- return 0;
-}
-int sgIP_TCP_Send(sgIP_Record_TCP * rec, const char * datatosend, int datalength, int flags) {
- if(!rec || !datatosend) return SGIP_ERROR(EINVAL);
- if(rec->want_shutdown) return SGIP_ERROR(ESHUTDOWN);
- SGIP_INTR_PROTECT();
- int bufsize;
- bufsize=rec->buf_tx_out-rec->buf_tx_in;
- if(bufsize<0) bufsize+=SGIP_TCP_TRANSMITBUFFERLENGTH;
- if(bufsize==0) { rec->time_last_action=sgIP_timems; rec->time_backoff=SGIP_TCP_GENRETRYMS; } // first byte sent, set up delay before sending
- bufsize=SGIP_TCP_TRANSMITBUFFERLENGTH-bufsize-1; // space left in buffer
- if(datalength>bufsize) datalength=bufsize;
- int i,j;
- j=rec->buf_tx_out;
- for(i=0;i<datalength;i++) {
- rec->buf_tx[j++]=datatosend[i];
- if(j==SGIP_TCP_TRANSMITBUFFERLENGTH) j=0;
- }
- rec->buf_tx_out = j;
- // check for immediate transmit
- j=rec->buf_tx_out-rec->buf_tx_in;
- if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH;
- j+=(int)(rec->sequence-rec->sequence_next);
- if(j>SGIP_TCP_TRANSMIT_IMMTHRESH && rec->tcpstate==SGIP_TCP_STATE_ESTABLISHED) {
- j=(int)(rec->sequence_next-rec->sequence);
- if(j<1000) { // arbitrary constant.
- j=rec->buf_tx_out-rec->buf_tx_in;
- if(j<0) j+=SGIP_TCP_TRANSMITBUFFERLENGTH;
- i=(int)(rec->txwindow-rec->sequence);
- if(j>i) j=i;
- i=sgIP_IP_MaxContentsSize(rec->destip)-20; // max tcp data size
- if(j>i) j=i;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,i);
- rec->retrycount=0;
- }
- }
- SGIP_INTR_UNPROTECT();
- if(datalength==0) return SGIP_ERROR(EWOULDBLOCK);
- return datalength;
-}
-int sgIP_TCP_Recv(sgIP_Record_TCP * rec, char * databuf, int buflength, int flags) {
- if(!rec || !databuf) return SGIP_ERROR(EINVAL); //error
- if(rec->buf_rx_in==rec->buf_rx_out) {
- if(rec->tcpstate==SGIP_TCP_STATE_CLOSED || rec->tcpstate==SGIP_TCP_STATE_CLOSE_WAIT) {
- if(rec->errorcode) return SGIP_ERROR(rec->errorcode);
- return SGIP_ERROR0(ESHUTDOWN);
- }
- return SGIP_ERROR(EWOULDBLOCK); //error no data
- }
- SGIP_INTR_PROTECT();
- int rxlen = rec->buf_rx_out - rec->buf_rx_in;
- if(rxlen<0) rxlen+=SGIP_TCP_RECEIVEBUFFERLENGTH;
- if(buflength>rxlen) buflength=rxlen;
- int i,j;
- j=rec->buf_rx_in;
- for(i=0;i<buflength;i++) {
- databuf[i]=rec->buf_rx[j++];
- if(j==SGIP_TCP_RECEIVEBUFFERLENGTH) j=0;
- }
-
- if(!(flags&MSG_PEEK)) {
- rec->buf_rx_in=j;
-
- if(rec->want_reack) {
- i=rec->buf_rx_out-rec->buf_rx_in;
- if(i<0) i+=SGIP_TCP_RECEIVEBUFFERLENGTH; // we now have the amount in the buffer
- i = SGIP_TCP_RECEIVEBUFFERLENGTH-i-1;
- if(i<0) i=0;
- if(i>SGIP_TCP_REACK_THRESH) {
- rec->want_reack=0;
- sgIP_TCP_SendPacket(rec,SGIP_TCP_FLAG_ACK,0);
- }
- }
- }
- SGIP_INTR_UNPROTECT();
- return buflength;
-}