/*
 * pktstuff.c
 * 
 * Ceci est un petit pgm bidon pour vérifier si on est capable de
 * mettre des données bidons dans les paquets tcp dans des places
 * insolites, en jouant sur la longueur des champs.
 *
 * Le pgm est basé sur libdnet et fragroute.
 *
 * Le pgm sniffe sur le loopback adresse pour des paquets envoyés à la
 * destination.
 *
 * Chaque paquet est sniffé, modifié pour ajouter l'information
 * supplémentaire après le vrai champ TCP dans le paquet IP, et
 * ensuite envoyé vers la destination en utilisant raw socket.
 *
 * Puisque c'est un "proof of concept" seulement, je ne tient pas
 * compte du cas où les données originales remplissent la trame au
 * complet. Faudrait faire la fragmentation dans ce cas-là.
 *
 * Also the communication is 1-way only. Would have to modify packets
 * coming and going for a full-fledged tunnel.
 *
 * La réponse vient retourne à notre machine comme d'habitude.
 *
 * Emprunté de Dug Song libdnet test pgm
 *
 * $Id: pktdecode.c,v 1.3 2003/06/09 21:33:08 asaph Exp $ */

#include "config.h"
#include <sys/types.h>

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>

#include <pcap.h>
#include <dnet.h>

#include "pktstuff.h"
#include "pkt.h"
#include "pcaputil.h"

static int event_gotsig = 0;

struct tun {
    intf_t *intf;
    pcap_t *pcap;
    route_t *route;

    struct route_entry rtent;
    struct intf_entry *ifent;
    u_char buf[1024];
};

typedef struct tun tun_t;

struct fr_ctx {
    struct addr src;
    struct addr dst;
    struct addr smac;
    struct addr dmac;

    int mtu;

    arp_t *arp;
    eth_t *eth;
    intf_t *intf;
    route_t *route;
    tun_t *tun;
};

static struct fr_ctx ctx;

/*****************/
/* Stream buffer */
/*****************/

#define OPT_DATA_LEN 32
#define ORIG_OPTIONS 14		/* Length of {NOP, NOP, timestamp, NOP, EOL}
				   static char buffer[1024];       /* Buffer holding data to be sent */
static int counter;		/* Current data to be sent pointer */





tun_t *sniff_close(tun_t * tun)
{
    /* Stop sniffing. */
    if (tun->pcap != NULL)
	pcap_close(tun->pcap);

    free(tun);

    return (NULL);
}


tun_t *sniff_open(struct addr * dst)
{
    struct tun *tun;
    struct arp_entry arpent;
    struct intf_entry ifent;
    arp_t *arp;

    if ((tun = calloc(1, sizeof(*tun))) == NULL)
	return (NULL);

    tun->ifent = (struct intf_entry *) tun->buf;
    tun->ifent->intf_len = sizeof(tun->buf);
    strlcpy(tun->ifent->intf_name, ETH0_DEV,
	    sizeof(tun->ifent->intf_name));

    /* Get interface information. */
    if ((tun->intf = intf_open()) == NULL)
	return (NULL);

    if (intf_get(tun->intf, tun->ifent) < 0)
	return (sniff_close(tun));

    /* Set up to sniff on eth0. */
    if ((tun->pcap = pcap_open(tun->ifent->intf_name)) == NULL)
	return (sniff_close(tun));

    if (pcap_filter(tun->pcap, "ip src %s", addr_ntoa(dst)) < 0)
	return (sniff_close(tun));
    return (tun);
}

/*
 * tcp_handler: get data stored in options field
 *
 * pkt 	Ptr to input packet
 */
static void tcp_handler(struct pkt *pkt)
{
    char tcp_data_read[OPT_DATA_LEN + 1] = "\x00";
    /* Data added in options field - starts with */
    /* noop */
    uint16_t len_don_tcp;
    int len_tcph = 0;		/* Length tcp hdr field */
    int i = 0;
    char *options =
	pkt->pkt_buf + ETH_HDR_LEN + IP_HDR_LEN + TCP_HDR_LEN +
	TCP_OPT_LEN;

    len_tcph = pkt->pkt_tcp->th_off << 2;	/* size of Tcp hdr */
    len_don_tcp = len_tcph - TCP_HDR_LEN;

    /* Lire notre stream dans le champs options de TCP */
    for (i = 0; i < OPT_DATA_LEN + 1; i++)
	tcp_data_read[i] = '\x0';

    if (len_don_tcp <= OPT_DATA_LEN)
	memcpy(tcp_data_read, options, len_don_tcp);
    else
	warnx("Data longer than expected");

    /*
       for (i = 0; i < OPT_DATA_LEN; i++)
       printf("%d - %x\n", i, tcp_data_read[i]);
     */

    warnx("%s", tcp_data_read + ORIG_OPTIONS);

    return;
}

/*
 * udp_handler: get data at end of udp packet
 *
 * pkt 	Ptr to input packet
 */
static void udp_handler(struct pkt *pkt)
{
    char udp_data_read[3];
    uint16_t len_don_udp = sizeof(udp_data_read) - 1;
    int i;

    /* Get data from udp source port */
    udp_data_read[3] = '\0';
    memcpy(udp_data_read, pkt->pkt_t_hdr_u.udp, len_don_udp);

    warnx("%s", udp_data_read);

    return;
}

static int main_close(void)
{
    if (ctx.tun != NULL)
	sniff_close(ctx.tun);
    if (ctx.route != NULL)
	route_close(ctx.route);
    if (ctx.intf != NULL)
	intf_close(ctx.intf);
    if (ctx.eth != NULL)
	eth_close(ctx.eth);
    if (ctx.arp != NULL)
	arp_close(ctx.arp);
    return (-1);
}

/*
 * Copy sniffed data into a new packet. Verify that the packet is
 * well-formed. If the packet is TCP, or UDP, then decode
 * unauthorized data as filler.
 */

static void
main_process(u_char * client_data, const struct pcap_pkthdr *pcpkt,
	     const u_char * pktdata)
{

    /* locals */
    struct pkt *pkt;
    int len_cap = pcpkt->caplen;	/* Length of captured data */

    if (pktdata == NULL)
	return;

    if (len_cap > PKT_BUF_LEN) {
	warnx("Dropping oversize packet");
	return;
    }

    if ((pkt = pkt_new()) == NULL) {
	warnx("Error allocating new packet");
	return;
    }

    /* Copy received data into a new pkt element */
    memcpy(pkt->pkt_data, pktdata, len_cap);
    pkt->pkt_end = pkt->pkt_data + len_cap;

    pkt_decorate(pkt);		/* Verify that pkt is well-constructed */


    /* s'assurer que la réponse revient sur l'adresse normale de notre PC */

    eth_pack_hdr(pkt->pkt_eth, ctx.dmac.addr_eth,
		 ctx.smac.addr_eth, ETH_TYPE_IP);

    pkt->pkt_ip->ip_src = ctx.src.addr_ip;

    /* Switch on type of packet */
    switch (ntohs(pkt->pkt_eth->eth_type)) {

	/* IP packet */
    case ETH_TYPE_IP:

	if (pkt->pkt_ip == NULL)	/* Ignore malformed pkt */
	    break;

	/* switch on the IP protocol */
	switch (pkt->pkt_ip->ip_p) {

	    /* Tcp packet */
	case IPPROTO_TCP:

	    if (pkt->pkt_tcp == NULL)	/* Ignore malformed pkts */
		break;

	    tcp_handler(pkt);
	    break;

	    /* Udp packet - do nothing for now */
	case IP_PROTO_UDP:
	    udp_handler(pkt);
	    break;

	    /* Icmp packet: do nothing */
	default:
	    break;

	}			/* end switch on IP Protocol */
	break;


	/* Some other type of packet - ignore */
    default:
	break;
    }

    pkt_free(pkt);
    return;
}



static void main_signal(int sig)
{
    warnx("exiting on signal %d", sig);
    event_gotsig++;
}

static void main_init(const char *dst)
{
    struct arp_entry arpent;
    struct intf_entry ifent;
    struct route_entry rtent;

    signal(SIGINT, main_signal);
    signal(SIGTERM, main_signal);

    srand(time(NULL));

    if (addr_aton(dst, &ctx.dst) < 0)
	err(1, "source address invalid");

    if (ctx.dst.addr_bits != IP_ADDR_BITS)
	errx(1, "only /32 sources supported at this time");

    pkt_init(128);

    if ((ctx.arp = arp_open()) == NULL ||
	(ctx.intf = intf_open()) == NULL ||
	(ctx.route = route_open()) == NULL)
	err(1, "couldn't open kernel networking interfaces");

    /* Setup our sniff. */
    if ((ctx.tun = sniff_open(&ctx.dst)) == NULL)
	err(1, "couldn't initialize sniff on eth0");
}


int packet_decode(char *src_ip)
{
    main_init(src_ip);

    while (!event_gotsig) {
	if (pcap_dispatch
	    (ctx.tun->pcap, -1, &main_process, (u_char *) & ctx) < 0) {
	    err(1, "Error in pcap loop - EXITING: %s",
		pcap_geterr(ctx.tun->pcap));
	}
    }

    main_close();
    exit(0);
}
