/*******************************************************************************/
/*                                                                             */
/*  Copyright 2004 Pascal Gloor                                                */
/*                                                                             */
/*  Licensed under the Apache License, Version 2.0 (the "License");            */
/*  you may not use this file except in compliance with the License.           */
/*  You may obtain a copy of the License at                                    */
/*                                                                             */
/*     http://www.apache.org/licenses/LICENSE-2.0                              */
/*                                                                             */
/*  Unless required by applicable law or agreed to in writing, software        */
/*  distributed under the License is distributed on an "AS IS" BASIS,          */
/*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/*  See the License for the specific language governing permissions and        */
/*  limitations under the License.                                             */
/*                                                                             */
/*******************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>


#include <p_defs.h>
#include <p_ptoa.h>
#include <p_dump.h>

char line[100];
struct in_addr addr;

/* dump decoder tool */
int main(int argc, char *argv[])
{
	FILE *fh;
	char *file = NULL;
	int end = 0;
	int head = 0;
	int machine = 0;

	if ( argc == 2 )
	{
		if ( strcmp(argv[1],"-m" ) == 0 ) { syntax(argv[0]); }
		file = argv[1];
	}
	else if ( argc == 3 && strcmp(argv[1],"-m") == 0 )
	{
		machine = 1;
		file = argv[2];
	}
	else
	{
		syntax(argv[0]);
	}

	if ( ( fh = fopen(file,"r") ) == NULL )
	{
		fprintf(stderr,"error opening '%s'\n",file);
		return -1;
	
	}

	while(!end)
	{
		int len;
		struct dump_msg msg;
		char buffer[65536];
		if ( ( len = fread(&msg, 1, sizeof(msg), fh) ) != sizeof(msg) )
		{
			fprintf(stderr,"EOF msg\n");
			return -1;
		}
		#ifdef DEBUG
		printf("type %u len %u timestamp %u\n",ntohs(msg.type),ntohs(msg.len),(unsigned int)ntohl(msg.ts));
		#endif

		if ( machine )
		{
			#ifdef FREEBSD
			printf("%lu|",ntohl(msg.ts));
			#else
			printf("%u|",ntohl(msg.ts));
			#endif
		}
		else
		{
			mytime((time_t)ntohl(msg.ts));
			printf("%s ",line);
		}

		if ( ( len = fread(buffer, 1, ntohs(msg.len), fh) ) != ntohs(msg.len) )
		{
			fprintf(stderr,"EOF submsg read (read %u but should %u)\n",len,ntohs(msg.len));
			return -1;
		}

		#ifdef DEBUG
		{
			int a;
			for(a=0; a<len; a++)
			{
				if ( a & !(a%4) ) { printf(" "); }
				if ( a & !(a%16) ) { printf("\n"); }
				printf("%02x ",(uint8_t)buffer[a]);
			}
			printf("\n");
		}
		#endif

		if ( ntohs(msg.type) == 0 && !head )
		{
			struct dump_header *header;

			header = (struct dump_header*)buffer;

			if ( machine )
			{
				#ifdef FREEBSD
				printf("P|%lu|%u\n",ntohl(header->ip),ntohs(header->as));
				#else
				printf("P|%u|%u\n",ntohl(header->ip),ntohs(header->as));
				#endif
			}
			else
			{
				addr.s_addr = header->ip;
				printf("peer ip %s AS %u\n",inet_ntoa(addr),ntohs(header->as));
			}
			head=1;
		}
		else if ( ntohs(msg.type) == 1 && head )
		{
			if ( machine ) { printf("C\n"); }
			else { printf("connected\n"); }
		}
		else if ( ntohs(msg.type) == 2 && head )
		{
			if ( machine ) { printf("D\n"); }
			else { printf("disconnected\n"); }
		}
		else if ( ntohs(msg.type) == 3 && head )
		{
			if ( machine ) { printf("K\n"); }
			else { printf("keepalive\n"); }
		}
		else if ( ntohs(msg.type) == 4 && head )
		{
			uint32_t prefix = 0;
			int jump = 1;
			struct dump_announce *announce;
			struct dump_announce_aspath *aspath;
			struct dump_announce_community *community;

			announce = (struct dump_announce*)buffer;

			memcpy(&prefix,&announce->prefix, sizeof(prefix));
			prefix = ntohl(prefix);

			if ( announce->mask == 0 ) { prefix = 0; }
			else { prefix &= ( 0xffffffff ^ ( ( 1 << ( 32 - announce->mask ) ) - 1 ) ); }

			jump += sizeof(announce);

			aspath = (struct dump_announce_aspath*) (buffer+jump);

			jump += 3;
			jump += ntohs(aspath->len);

			if ( machine )
			{
				printf("A|%u|%u|1|",prefix,announce->mask);
			}
			else
			{
				addr.s_addr = ntohl(prefix);
				printf("prefix announce %s/%u ",inet_ntoa(addr),announce->mask);
				printf("aspath: ");
			}

			{
				int a;
				for(a=0; a<(ntohs(aspath->len)/2); a++)
				{
					printf("%u",ntohs(aspath->data[a]));
					if ( a < ((ntohs(aspath->len)/2) - 1 ) ) { printf(" "); }
				}
			}

			community = (struct dump_announce_community*) (buffer+jump);

			if ( machine ) { printf("|2|"); }
			else { printf(" community: "); }
			{
				int a;
				for(a=0; a<(ntohs(community->len)/4); a++)
				{
					struct community_record *com;
					com = (struct community_record*)&community->data[a];

					printf("%u:%u",ntohs(com->asn),ntohs(com->num));
					if ( a < ((ntohs(community->len)/4) - 1) ) { printf(" "); }
				}
			}
			printf("\n");

		}
		else if ( ntohs(msg.type) == 5 && head )
		{
			uint32_t prefix = 0;
			int jump = 1;
			struct dump_withdrawn *withdrawn;

			withdrawn = (struct dump_withdrawn*)buffer;

			memcpy(&prefix,&withdrawn->prefix, sizeof(prefix));
			prefix = ntohl(prefix);

			if ( withdrawn->mask == 0 ) { prefix = 0; }
			else { prefix &= ( 0xffffffff ^ ( ( 1 << ( 32 - withdrawn->mask ) ) - 1 ) ); }

			jump += sizeof(withdrawn);

			if ( machine )
			{
				printf("W|%u|%u\n",prefix,withdrawn->mask);
			}
			else
			{
				addr.s_addr = ntohl(prefix);
				printf("prefix withdrawn %s/%u\n",inet_ntoa(addr),withdrawn->mask);
			}
		}
		else if ( ntohs(msg.type) == 255 && head )
		{
			if ( machine )
			{
				printf("E\n");
			}
			else
			{
				printf("eof\n");
			}
			end=1;
		}
		else
		{
			fprintf(stderr,"parsing error\n");
			return -1;
		}
	}


/*
	while(len)
	{
		uint8_t buf;
		len = fread(&buf,sizeof(buf), 1, fh);
		if ( len > 0 ) {
			if ( pos && !(pos%4) ) { printf(" "); }
			if ( pos && !(pos%16) ) { printf("\n"); }
			printf("%02x ",buf);
		}
		pos++;
	}
	printf("\n");
*/

	fclose(fh);

	return 0;
}

/* converts unixtime into YYYYmmddHHMMSS format */
void mytime(time_t ts)
{
	struct tm *tm;
	tm = gmtime((time_t*)&ts);
	strftime(line, sizeof(line), "%Y-%m-%d %H:%M:%S" , tm);
}

/* syntax */
void syntax(char *prog)
{
	printf("Piranha v%s.%s.%s Dump file decoder, Copyright(c) 2004 Pascal Gloor\n",P_VER_MA,P_VER_MI,P_VER_PL);
	printf("syntax: %s [-m] <file>\n",prog);
	printf("\n");
	printf("-m for machine readable output:\n");
	printf("timestamp|P|peer_ip|peer_as # begin of every file\n");
	printf("timestamp|C                 # connected (Active -> Established)\n");
	printf("timestamp|D                 # disconnected (Established -> Active)\n");
	printf("timestamp|K                 # BGP Keepalive received\n");
	printf("timestamp|A|network|mask|opt id|opt|opt id ...\n");
	printf("                            # BGP Announce\n");
	printf("timestamp|W|network|mask    # BGP Withdrawn\n");
	printf("\n");
	
	exit(-1);
}