/*
 * DIS PDU packet decoding stress-test. We must prepare to sustain the hostile
 * environment of the net: ill formed packets must be detected and rejected to
 * protect program safety and security.
 * 
 * Random packets are currently detected mostly because:
 * 
 * - The length field does not match the actual length of the packet (checked
 *   in dis_readPDU()).
 * 
 * - The type field does not match any supported DIS PDU type (checked by
 *   xdr_dis_pdu()).
 * 
 * - Packet too short for the stated variable length array.
 * 
 * On failed decoding, the xdr module guarantees any dynamically allocated
 * memory be released. It is still responsibility of the ACM program to
 * release successfully decoded DIS PDU through the dis_freePDUComponents().
 */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "../../../src/util/memory.h"
#include "../../../src/util/error.h"
#include "../../../src/util/prng.h"
#include "../../../src/dis/dis/xdr_dis.h"
#include "../../../src/dis/dis/datum.h"
#include "../../../src/dis/dis/xdr.h"

#define IS_LITTLE_ENDIAN (((char *)&(int){1})[0] == 1)

static int err;

static int packets_decoded, packets_successfully_decoded;


/**
 * Tries to decode the packet as a DIS PDU.
 * 
 * Note that the packet so generated has a random header where only the type is
 * set, not the length; in ACM the length field of the received packets is
 * checked in dis_readPDU() and not in xdr_dis_pdu(), this latter being the
 * object of our tests now.
 * 
 * Note that many arrays in the packet gets unexpectedly successfully decoded
 * despite all the controls operated by xdr_var_array() just because the length
 * filed giving their length is a small u_char; the chance decreases rapidly
 * with larger length fields.
 * @param line Source line no. of this test.
 * @param packet Random packet.
 * @param packet_len Length of the random packet.
 * @param pdu_type Type of DIS PDU to be set in the packet.
 */
static void decodePacket(int line, char *packet, int packet_len, u_char pdu_type)
{
	packet[2] = pdu_type;
	
	xdr_Type *xdr = xdr_new(packet, packet_len, xdr_DECODE);
	dis_pdu pdu;
	//printf("Parsing PDU:\n");
	packets_decoded++;
	if( xdr_dis_pdu(xdr, &pdu) ){
		packets_successfully_decoded++;
		//printf("in line %d successfully decoded pdu type %d, %d bytes decoded\n", line, pdu_type, xdr_getpos(xdr));
		dis_freePDUComponents(&pdu);
	} else {
		//("in line %d failed decoding pdu type %d: %s\n", line, pdu_type, xdr_getErrorDescription(xdr));
	}
	xdr_free(xdr);
}


int main(int argc, char** argv)
{
	int i;
	error_prog_name = argv[0];

	prng_setSeed(123456789);
	
	for(i = 0; i < 100000; i++){
		/* Use the same max packet length the dis_readPDU() accepts. */
		char packet[2048];
		prng_fill(packet, sizeof(packet));
		
		/* Test all the DIS PDU types supported by xdr_dis_pdu(): */
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeOther);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeEntityState);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeCollision);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeFire);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeDetonation);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeCreateEntity);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeRemoveEntity);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeStopFreeze);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeStartResume);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeSetData);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeComment);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeEmission);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeTransferControl);
		decodePacket(__LINE__, packet, sizeof(packet), PDUTypeAcknowledge);
	}
	
	//printf("successfully decoded %d of %d\n", packets_successfully_decoded, packets_decoded);
	
	err += memory_report();
	
	return err == 0? 0 : 1;
}
