/*
 * Soubor:  detekce_protokolu.c
 * Datum:   06.08.2012
 * Autor:   Stanislav Bárta, xbarta29@stud.fit.vutbr.cz
 * Projekt: IM modul
 * Popis:   soubor obsahujici funkci ktera analyzuje prijaty paket a vrati zjistene udaje.
 *          v pripade, ze se jedna o podporovany protokol je ulozen i prenaseny obsah s aplikacnim protokolem
 */

#include "detekce_protokolu.h"

/*
 * ze zachyceneho packetu vyfiltruje informace tykajici se zkoumanych protokolu
 * v pripade jineho nez odposlouchavaneho protokolu je identifikovan jako neznamy
 */
struct data_packetu ziskej_obsah(const struct pcap_pkthdr *header, const u_char *packet, int link_type)
{
	// struktura ktera bude obsahovat potrebne informace ze zachyceneho packetu
	struct data_packetu vyfiltrovano_z_packetu;
	inicializace_struktury_dat_packetu(&vyfiltrovano_z_packetu);
	
	vyfiltrovano_z_packetu.transportni_protokol = NEUVEDENO;
	vyfiltrovano_z_packetu.typ = NEZNAME;
	vyfiltrovano_z_packetu.cas_zachyceni = header->ts;
	vyfiltrovano_z_packetu.data = NULL;

	//ethernetova_hlavicka
	if(link_type == DLT_EN10MB)
	{
		// pretypovani prijateho packetu na strukturu ethernetove hlavicky
		struct ether_header *ethernetova_hlavicka;
		ethernetova_hlavicka = (struct ether_header *) packet;

		// kontrola typu jestli jde o packet ipv4 nebo ipv6 
		if(ntohs(ethernetova_hlavicka->ether_type) == ETHERTYPE_IP) // ipv4
		{
			ip_verze_4(packet + ETHER_HDR_LEN, &vyfiltrovano_z_packetu);
		} // konec ipv4
		else if(ntohs(ethernetova_hlavicka->ether_type) == ETHERTYPE_IPV6) // ipv6
		{
			ip_verze_6(packet + ETHER_HDR_LEN, &vyfiltrovano_z_packetu);
		} // konec ipv6
	}

	//Linux cooked header
	else if(link_type == DLT_LINUX_SLL)
	{
		struct sll_header *sll_hdr;
		sll_hdr = (struct sll_header *) packet;

		if(ntohs(sll_hdr->sll_protocol) == 0x0003 || ntohs(sll_hdr->sll_protocol) == 0x0800) // všechny nebo ip
		{
			//zjistime jestli se jedna o nekterou verzi ip
			struct iphdr *ip_packet;
			ip_packet = (struct iphdr *) (packet + SLL_HDR_LEN);

			if(ip_packet->version == 4)
			{
				ip_verze_4(packet + SLL_HDR_LEN, &vyfiltrovano_z_packetu);
			}
			else if(ip_packet->version == 6)
			{
				ip_verze_6(packet + SLL_HDR_LEN, &vyfiltrovano_z_packetu);
			}
		}
	}

	return vyfiltrovano_z_packetu;
}

/*
 * podle portu vrati typ prenaseneho aplikacniho protkolu
 */
int podle_portu(const u_char *packet)
{
	struct tcphdr *tcp_header;
	tcp_header = (struct tcphdr *) packet;

	int zdroj = ntohs(tcp_header->th_sport);
	int cil = ntohs(tcp_header->th_dport);

	// XMPP komunikace
	if(zdroj == 5222)
	{
		return XMPP_prichozi;
	}

	if(cil == 5222)
	{
		return XMPP_odchozi;
	}

	// IRC komunikace
	if(zdroj == 6665 || zdroj == 6666 || zdroj == 6667 || zdroj == 6668 || zdroj == 6669)
	{
		return IRC_prichozi;
	}

	if(cil == 6665 || cil == 6666 || cil == 6667 || cil == 6668 || cil == 6669)
	{
		return IRC_odchozi;
	}

	// OSCAR komunikace
	if(zdroj == 5190 || zdroj == 5191 || zdroj == 5192 || zdroj == 5193)
	{
		return OSCAR_prichozi;
	}

	if(cil == 5190 || cil == 5191 || cil == 5192 || cil == 5193)
	{
		return OSCAR_odchozi;
	}

	// YMSG komunikace
	if(zdroj == 5050)
	{
		return YMSG_prichozi;
	}

	if(cil == 5050)
	{
		return YMSG_odchozi;
	}

	// komunikace ktera nas nezajima
	return NEURCENO;
}

void ip_verze_4(const u_char *packet, struct data_packetu *vyfiltrovano_z_packetu)
{
	vyfiltrovano_z_packetu->verze_ip = IPverze4;
	// pretypovani na strukturu ipv4 packetu
	// posunuti ukazatele o velikost ethernetoveho ramce
	struct ip *ipv4_packet;
	ipv4_packet = (struct ip *) (packet);
	int ipv4_length = (ipv4_packet->ip_hl)*4;

	if(ipv4_packet->ip_p == 6) // TCP protokol
	{
		vyfiltrovano_z_packetu->ipv4_zdroje = ipv4_packet->ip_src;
		vyfiltrovano_z_packetu->ipv4_cile = ipv4_packet->ip_dst;
		tcp(packet + ipv4_length, vyfiltrovano_z_packetu, ntohs(ipv4_packet->ip_len), ipv4_length);
	} // konec TCP protokolu

}

void ip_verze_6(const u_char *packet, struct data_packetu *vyfiltrovano_z_packetu)
{
	vyfiltrovano_z_packetu->verze_ip = IPverze6;
	// pretypovani na strukturu ipv6 packetu
	// posunuti ukazatele o velikost ethernetoveho ramce
	struct ip6_hdr *ipv6_packet;
	ipv6_packet = (struct ip6_hdr *) packet;

	if(ipv6_packet->ip6_nxt == 6) // TCP protokol
	{
		vyfiltrovano_z_packetu->ipv6_zdroje = ipv6_packet->ip6_src;
		vyfiltrovano_z_packetu->ipv6_cile = ipv6_packet->ip6_dst;
		tcp(packet + IPV6_LENGTH, vyfiltrovano_z_packetu, ntohs(ipv6_packet->ip6_plen) + IPV6_LENGTH, IPV6_LENGTH);
	}
}

void tcp(const u_char *packet, struct data_packetu *vyfiltrovano_z_packetu, int velikost_dat_v_ip, int delka_ip_hlavicky)
{
	vyfiltrovano_z_packetu->transportni_protokol = TCP;
	struct tcphdr *tcp_header;
	tcp_header = (struct tcphdr *) packet;
	// vypočítáme velikost tcp hlavičky v bajtech
	int velikost_tcp_hlavicky = tcp_header->th_off*4;

	// ziskame velikost dat v zachycenem packetu
	int velikost_dat = velikost_dat_v_ip - (delka_ip_hlavicky + velikost_tcp_hlavicky);
	vyfiltrovano_z_packetu->velikost_dat = velikost_dat;

	// podle portu zkusime urcit o jaky se jedna protkol
	int protokol = podle_portu(packet);
	if(protokol != NEURCENO)
	{
		if(protokol == XMPP_odchozi || protokol == XMPP_prichozi)
		{
			if(velikost_dat > 0)
			{
				// pomocny ukazatel ukazujici na zacatek dat v packetu
				char *pomocny = (char *) (packet + velikost_tcp_hlavicky);
				// kopie dat
				kopie_retezce(pomocny, &(vyfiltrovano_z_packetu->data), velikost_dat);
			}

			vyfiltrovano_z_packetu->typ = XMPP;

			if(protokol == XMPP_odchozi)
			{
				vyfiltrovano_z_packetu->smer = odchozi;
			}
			else if(protokol == XMPP_prichozi)
			{
				vyfiltrovano_z_packetu->smer = prichozi;
			}
		} // konec XMPP
		else if(protokol == IRC_odchozi || protokol == IRC_prichozi)
		{
			if(velikost_dat > 0)
			{
				// pomocny ukazatel ukazujici na zacatek dat v packetu
				char *pomocny = (char *) (packet + velikost_tcp_hlavicky);
				// kopie dat
				kopie_retezce(pomocny, &(vyfiltrovano_z_packetu->data), velikost_dat);
			}

			vyfiltrovano_z_packetu->typ = IRC;

			if(protokol == IRC_odchozi)
			{
				vyfiltrovano_z_packetu->smer = odchozi;
			}
			else if(protokol == IRC_prichozi)
			{
				vyfiltrovano_z_packetu->smer = prichozi;
			}
		} // konec IRC
		else if(protokol == OSCAR_odchozi || protokol == OSCAR_prichozi)
		{
			if(velikost_dat > 0)
			{
				// ulozeni ukazatele na data (oscar je binarni a zpracovava se odlisne od predchozich protokolu)
				vyfiltrovano_z_packetu->data = (char *) (packet + velikost_tcp_hlavicky);
			}

			vyfiltrovano_z_packetu->typ = OSCAR;

			if(protokol == OSCAR_odchozi)
			{
				vyfiltrovano_z_packetu->smer = odchozi;
			}
			else if(protokol == OSCAR_prichozi)
			{
				vyfiltrovano_z_packetu->smer = prichozi;
			}
		} // konec OSCAR
		else if(protokol == YMSG_odchozi || protokol == YMSG_prichozi)
		{
			if(velikost_dat > 0)
			{
				// ulozeni ukazatele na data (ymsg je binarni a zpracovava se odlisne od predchozich protokolu)
				vyfiltrovano_z_packetu->data = (char *) (packet + velikost_tcp_hlavicky);
			}

			vyfiltrovano_z_packetu->typ = YMSG;

			if(protokol == YMSG_odchozi)
			{
				vyfiltrovano_z_packetu->smer = odchozi;
			}
			else if(protokol == YMSG_prichozi)
			{
				vyfiltrovano_z_packetu->smer = prichozi;
			}
		} // konec YMSG

		vyfiltrovano_z_packetu->port_zdroje = ntohs(tcp_header->th_sport);
		vyfiltrovano_z_packetu->port_cile = ntohs(tcp_header->th_dport);

		// urceni jestli je treba zpracovany paket dale analyzovat
		if(velikost_dat > 0)
		{
			vyfiltrovano_z_packetu->stav_tcp = ZPRACOVAT;
		}
		else
		{
			vyfiltrovano_z_packetu->stav_tcp = NEZPRACOVAVAT;
		}

		// kontrola jestli nejde o ukonceni TCP spojeni
		uint8_t fin_flag = tcp_header->th_flags;
		fin_flag = fin_flag << 7;
		fin_flag = fin_flag >> 7;

		if(fin_flag)
		{
			vyfiltrovano_z_packetu->stav_tcp = UKONCENO;
		}
	}
}

