#include "cap.hpp"
#include "net.hpp"
#include "stream.hpp"
#include "main.hpp"
#include <pcap.h>


static pcap_t* cap = NULL;


static void cap_callback(u_char* stream, const pcap_pkthdr* hdr, const u_char* pkt) {
    if (hdr->caplen != hdr->len) {
        LOG("ignoring truncated capture (%u != %u)", hdr->caplen, hdr->len);
        return;
    }
    uint32_t len = hdr->caplen;

    packet_t p;
    if (!p.parse(hdr->ts, pkt, len)) {
        LOG("cannot parse packet");
        return;
    }
    ((Stream*)stream)->push(p);
}


void cap_loop(const char* dev, const char* filter) {
    // open device for reading (promiscuous)
    char errbuf[PCAP_ERRBUF_SIZE] = {0};
    cap = pcap_open_live((dev && *dev)? dev: "any", BUFSIZ, 1, -1, errbuf);
    if (!cap) {
        LOG("pcap_open_live(): %s", errbuf);
        return;
    } else if (*errbuf) {
        LOG("pcap_open_live(): %s", errbuf);
    }

    // compile and set program as filter
    if (filter && *filter) {
        bpf_program fp;
        if (pcap_compile(cap, &fp, filter, 1, PCAP_NETMASK_UNKNOWN) == -1) {
            LOG("pcap_compile(): %s (ignored)", pcap_geterr(cap));
        } else {
            if (pcap_setfilter(cap, &fp) == -1) {
                LOG("pcap_setfilter(): %s (ignored)", pcap_geterr(cap));
            }
            pcap_freecode(&fp);
        }
    }

    // loop until pcap_breakloop
    Stream stream;
    int rv;
    do {
        rv = pcap_loop(cap, -1, cap_callback, (u_char*)&stream);
    } while (rv == 0);
    if (rv == -1) {
        LOG("pcap_loop(): %s", pcap_geterr(cap));
    }
    pcap_close(cap);
    cap = NULL;
}


void cap_breakloop() {
    if (cap) {
        pcap_breakloop(cap);
    }
}