/*
 * verify.c -- the connection matching engine
 * Part of the tcpick project
 *
 * Author: Francesco Stablum <duskdruid @ despammed.com>
 *
 * Copyright (C) 2003, 2004  Francesco Stablum
 * Licensed under the GPL
 *
 */

/*
  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/


#include "tcpick.h"
#include "extern.h"

#define CURRENT_CONN (prev_conn->next)

#define ACK_OFF ntohl( tcppacket->ack_seq ) - Desc->oth->sip 
/* FIXME: to def.h? */

#define IS_CLIENT_SENDER \
 CURRENT_CONN->client.ip.s_addr == ippacket->ip_src.s_addr && \
 CURRENT_CONN->server.ip.s_addr == ippacket->ip_dst.s_addr && \
 CURRENT_CONN->client.port == tcppacket->source            && \
 CURRENT_CONN->server.port == tcppacket->dest 

#define IS_SERVER_SENDER \
 CURRENT_CONN->client.ip.s_addr == ippacket->ip_dst.s_addr && \
 CURRENT_CONN->server.ip.s_addr == ippacket->ip_src.s_addr && \
 CURRENT_CONN->client.port == tcppacket->dest              && \
 CURRENT_CONN->server.port == tcppacket->source 

#define IS_SYN_SENT \
 tcppacket->syn == 1 && \
 tcppacket->ack == 0

#define IS_SYN_RECEIVED \
 tcppacket->syn         == 1        && \
 tcppacket->ack         == 1        && \
 Desc->side             == SERVER   && \
 CURRENT_CONN->status   == SYN_SENT

#define IS_ESTABLISHING \
 tcppacket->syn           == 0            && \
 tcppacket->ack           == 1            && \
 Desc->side               == CLIENT       && \
 CURRENT_CONN->status     == SYN_RECEIVED && \
 CURRENT_CONN->client.sip == ntohl( tcppacket->seq )

#define IS_DATA_FLOW       1

#define IS_FIN_WAIT_1 \
 tcppacket->fin         == 1           && \
 CURRENT_CONN->status   == ESTABLISHED

#define IS_FIN_WAIT_2__CLOSE_WAIT \
 tcppacket->ack         == 1           && \
 tcppacket->fin         == 0           && \
 CURRENT_CONN->status   == FIN_WAIT_1  && \
 CURRENT_CONN->closer   != Desc->side 

#define IS_TIME_WAIT__LAST_ACK \
 tcppacket->ack         == 1                         && \
 tcppacket->fin         == 1                         && \
 ( CURRENT_CONN->status == FIN_WAIT_2__CLOSE_WAIT ||    \
 CURRENT_CONN->status == FIN_WAIT_1 )              && \
 CURRENT_CONN->closer   != Desc->side 

#define IS_CLOSING  \
 tcppacket->ack         == 1                         && \
 tcppacket->fin         == 0                         && \
 CURRENT_CONN->status   == TIME_WAIT__LAST_ACK       && \
 CURRENT_CONN->closer   == Desc->side 

#define IS_RESET  \
 tcppacket->rst         == 1


__inline__ int
established_packet ( struct CONN * conn_ptr, struct HOST_DESC * Desc)
/* called by verify()
   packets of established connections come here */
{
	register u_int32_t off;
	if( payload_len ) {

		off = ntohl( tcppacket->seq ) - Desc->sip ;
		
		/* unacknowledged data */
		addfr( & (Desc->unack),
		       Desc->wlen,
		       off, 
		       payload, 
		       payload_len );
	}

	if( payload_len == 0) {
		/* acknowledge data */
		flush_ack ( Desc->oth, conn_ptr, ACK_OFF  ); 
	}
	else if( tcppacket->psh ) {
		/* PUSH data */
		flush_ack ( Desc, conn_ptr, ACK_OFF  );
     }

}

__inline__ int  
verify()
/* called by got_packet().
   this is the engine that matches the packets with their connection */
{

	register struct HOST_DESC * Desc = NULL;

	register struct CONN * prev_conn;
	
	prev_conn = first_conn;
	
/* SYN-SENT: (outside of the loop because the 
 * loop is used for sessions just tracked) */	 
	if( IS_SYN_SENT ) {
		newconn( last_conn ); 
		return(1);
	}
	
	if(! CURRENT_CONN ) 
		return 0 ;

	while (! Desc ) {
/* cycle until it finds a connection that 
   matches on server or client side */

		if ( IS_CLIENT_SENDER ) 
			Desc = & (CURRENT_CONN->client);
		
		else if ( IS_SERVER_SENDER )
			Desc = & (CURRENT_CONN->server);
		else {
			prev_conn = prev_conn->next;
			if(! CURRENT_CONN ) 
				return 0 ;
		}
	} 
	
/* update time of last packet in connection */
	CURRENT_CONN->lasttime = time(NULL);

/* when this point is reached, we have found
   the right matching connection */
    
	if( IS_SYN_RECEIVED ) {
		CURRENT_CONN->server.sip = ntohl(tcppacket->seq) + 1;
		status_switch( prev_conn, SYN_RECEIVED );
		return 1;
	}
    

/* 3rd packet of the 3-way-handshake*/
	if( IS_ESTABLISHING ) {
		status_switch( prev_conn, ESTABLISHED );
		return 1;
	}
    
/* data or ack packets */
	if( IS_DATA_FLOW ) {
		if( REBUILD ) 
			established_packet ( CURRENT_CONN, Desc );
	}
	
/* first FIN sent */
	if( IS_FIN_WAIT_1 ) {
		status_switch( prev_conn, FIN_WAIT_1 );
		CURRENT_CONN->closer = Desc->side; 
		return 1;
	}

     
/* ACK of first FIN sent */
	if( IS_FIN_WAIT_2__CLOSE_WAIT ) {
		status_switch( prev_conn, FIN_WAIT_2__CLOSE_WAIT );
		return 1;
	}

     
/* second FIN sent */
	if( IS_TIME_WAIT__LAST_ACK ) {
		status_switch( prev_conn, TIME_WAIT__LAST_ACK );
		return 1;
	}

/* LAST-ACK sent */
	if( IS_CLOSING ) {
		status_switch( prev_conn, CLOSED );
		return 1;
	}
/* RST sent */
	if( IS_RESET ) {
		status_switch( prev_conn, RESET );
		return 1;
	}

/* nice work :^) */
}



syntax highlighted by Code2HTML, v. 0.9.1