/*
 * fragments.c -- manages unacknowledged data
 * 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.
 */

/* 
 * Explanation of this code (fragments.c)
 * ======================================
 *
 * The stream of data of the tcp connection will be saved on 
 * the file, but we know that tcp connection aren't datagram-oriented, 
 * but stream-oriented! This means we *must* rebuild the disordered
 * flow. The problem is: we cannot store entire tcp connections in
 * memory! This is simply crazy! What if somebody is downloading cd
 * iso images, or dvd films! They will surely be corrupted.
 * Solution: write the rebuilded part of tcp flow in the file when
 * there are no missing pieces; so we will be able to store in memory
 * a linked list that reorders the tcp data flow and then, when data
 * are acknowledged by a "ack" packet, these data will be written to
 * the output file.
 *
 * Phase 1:
 *     Picking up the packets containing data from the interface.
 * 
 * Packets containing data fragments are stored into the linked list
 * conn_ptr->unack, managed by the insfr(...) function
 * These data are not yet acknowledged with the "ack"
 * packet. Duplicates are discarded and the flow will be completely
 * rebuilt.
 * 
 * Phase 2:
 *     Acks are arriving!
 * 
 * When a "ack" packet arrives, the function flush_ack(...) writes the
 * acknowledged fragments to the output file, and deleletes them from
 * the linked list.
 */

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

#define MATCH_OFF(fr, oth) fr->off + fr->len == oth->off

#define ATTACH_FR(prev, this){			\
						\
	this->next = prev->next;		\
	prev->next = this;			\
						\
	if (MATCH_OFF(this, prev->next))	\
		this->flag = CONTINUE;		\
	else					\
		this->flag = BREAK;             \
        }


#define FREE_FR(fr) S_free( fr->payload ); S_free( fr );

#define DELETE_FIRST_FR(list) {                         \
                        struct FRAGMENT * tmp;        \
                        tmp = * list;                   \
                        * list = ( * list )->next;      \
                        FREE_FR (tmp)                   \
                        }
__inline__ int 
addfr( struct FRAGMENT ** first,
       int wlen,
       u_int32_t data_off,
       u_char * payload,
       int payload_len )
/* called by established_packet().
 * Insert and reorder data fragments in a linked-list.
 * Duplicates are discarded.
 * 
 * return values: 
 * 0: fragment refused
 * 1: fragment accepted in the linked-list
 */

{
	struct FRAGMENT * new;
	struct FRAGMENT * curr;

	if ( data_off >= wlen ) {
 
		/* allocate and build the fragment */
		new = ( struct FRAGMENT * ) S_calloc ( 1, sizeof( struct FRAGMENT ));
		new->payload = ( u_char * ) S_calloc( 1, payload_len );
		new->off = data_off;
		memcpy( new->payload, payload, payload_len );
		new->len = payload_len;
		
		/* now insert the fragment in the flagged linked list */
		
		/* linked list was empty */
		if (! * first ) {
			* first = new;
			(* first)->next = NULL;
			return 1;
		}
		
		/* replace first */
		if ( (* first)->off > new->off) { 
			new->next = * first;
			* first = new;
			return 1;
		}
		
		curr = * first;
		
		while ( curr ) {
			if( curr->flag == BREAK &&
			    MATCH_OFF(curr, new) ) {
				/* the new data fragment is exactly next to curr */ 
				ATTACH_FR( curr, new );
				return 1;
			}
			else if ( curr->next ) {
				if( curr->next->off > new->off ) {
			/* the new data fragment is _not_ exactly next to curr,
			   but it must be there because the next
			   fragment has a bigger offset	*/
					ATTACH_FR( curr, new );
					return 1;
				}
			} else {
				/* it is the last */
				curr->next = new;
				return 1;
			}
			
			curr = curr->next;
		}
		return 0;
	}
}

__inline__ int
flush_ack ( struct HOST_DESC * desc,
	    struct CONN * conn_ptr,
	    int ack_num )
/* called by established_packet
 * when a "ack" packet comes to the network device,
 * data that are unacknowledged will be acknowledged and immediatly
 * sent to the write engine wrebuild 
 */
{
#define LIST (desc->unack)
	
	while( LIST &&
	       LIST->off <= ack_num ) {
		
		flowflush( conn_ptr, desc->oth, LIST->payload, LIST->len );

		debug("wrote off=%i wlen=%i len=%i ack=%i\n",
		      LIST->off, desc->wlen, LIST->len, ack_num);

		desc->wlen += LIST->len;
		DELETE_FIRST_FR ( & LIST );
	}
}


syntax highlighted by Code2HTML, v. 0.9.1