#include "net.hpp"
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>


// for convenience
typedef struct ether_header eth_hdr_t;
typedef struct ip           ip_hdr_t;
typedef struct tcphdr       tcp_hdr_t;


bool packet_s::parse(const struct timeval& t, const unsigned char* b, size_t l) {
    // get ethernet hdr & skip to ip
    if (l < sizeof(eth_hdr_t)) return false;
    const eth_hdr_t* eth_hdr = (const eth_hdr_t*)b;
    l -= sizeof(eth_hdr_t);
    b += sizeof(eth_hdr_t);

    // get src & dst from ip header and skip to tcp
    if (ntohs(eth_hdr->ether_type) != ETHERTYPE_IP) return false;
    if (l < sizeof(ip_hdr_t)) return false;
    const ip_hdr_t* ip_hdr = (const ip_hdr_t*)b;
    if (ip_hdr->ip_p != IPPROTO_TCP) return false;
    l -= sizeof(ip_hdr_t);
    b += sizeof(ip_hdr_t);
    conn.src = ip_hdr->ip_src;
    conn.dst = ip_hdr->ip_dst;

    // tcp
    if (l < sizeof(tcp_hdr_t)) return false;
    const tcp_hdr_t* tcp_hdr = (const tcp_hdr_t*)b;
    unsigned hdrlen = tcp_hdr->doff * 4; // specifies the size of the TCP header in 32-bit words
    if (hdrlen < sizeof(tcp_hdr_t) || hdrlen > l || hdrlen > 60) { // 20 normal + max 40 options
        LOG("invalid tcp options length %u (%zu)", hdrlen, l);
        return false;
    }
    l -= hdrlen;
    b += hdrlen;
    conn.src_port = ntohs(tcp_hdr->source); // th_sport
    conn.dst_port = ntohs(tcp_hdr->dest); // th_dport

    // payload & done
    conn.ts = (t.tv_sec * 1000 * 1000) + t.tv_usec;
    syn = tcp_hdr->syn; // TODO: reassemble acc. to seq and frag
    ack = tcp_hdr->ack;
    fin = tcp_hdr->fin;
    seq = htonl(tcp_hdr->seq);
    buf = b;
    len = l;
    return true;
}