/*
 * Written by Pejman Moghadam / 1401-02-30
 * Public domain.
 *
 */

/* gcc src.c -lpcap */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <pcap/pcap.h>
#include <netinet/ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>

struct byte {
        unsigned int a:1;
        unsigned int b:1;
        unsigned int c:1;
        unsigned int d:1;
        unsigned int e:1;
        unsigned int f:1;
        unsigned int g:1;
        unsigned int h:1;
};

void showbin(char *buf, int len)
{
        int i,j;
        char ch;
        struct byte *ba;

        for(i=0; i<len; i++) {
                ch = *(buf+i);
            ba = (struct byte *)&ch;
        printf("%d%d%d%d", (*ba).h, (*ba).g, (*ba).f, (*ba).e);
        printf("%d%d%d%d", (*ba).d, (*ba).c, (*ba).b, (*ba).a);
        printf(" ");
        }
}

void show_packet_bin(char *buf, int len)
{
    unsigned char ch;
    int i, j, k, space;

    for (i = 0; i < len; i+=16) {
        printf("\t0x%04x  ", i);
        for (j = i; j < i+16 && j < len; j++) {
            ch = (unsigned char)buf[j];
            printf("%02X       ", ch);
        }
 
        if (j == i+16)
            space = 1;
        else
            space = 1 + (16*9) - (j-i)*9;
 
        for (k = 0; k < space; k++)
            printf(" ");
 
        for (j = i; j < i+16 && j < len; j++) {
            ch = (unsigned char)buf[j];
            if (ch <= 32 || ch >= 127)
                ch = '.';
            printf("%c", ch);
        }
        printf("\n");

        printf("\t        ");
        for (j = i; j < i+16 && j < len; j++)
            showbin(&buf[j],1);
        printf("\n");

    }
    printf("\n");
}

char *ether_ntoa_padded(const struct ether_addr *addr, char *buf)
{
    sprintf (buf, "%02x:%02x:%02x:%02x:%02x:%02x",
        addr->ether_addr_octet[0], addr->ether_addr_octet[1],
        addr->ether_addr_octet[2], addr->ether_addr_octet[3],
        addr->ether_addr_octet[4], addr->ether_addr_octet[5]);
    return buf;
}

void show_ethernet_header(char *packet, int len)
{
    /* man 3 ether_aton */
    /* /usr/include/linux/if_ether.h */

    struct ethhdr *h;
    h = (struct ethhdr *)packet;

    struct ether_addr *dst;
    dst = (struct ether_addr *)(h->h_dest);

    struct ether_addr *src;
    src = (struct ether_addr *)(h->h_source);
    
    char addr_str[INET6_ADDRSTRLEN];
    printf("Ethernet header info:\n");
    printf("\tdst: %s (%s)\n", ether_ntoa(dst), ether_ntoa_padded(dst, addr_str));
    printf("\tsrc: %s (%s)\n", ether_ntoa(src), ether_ntoa_padded(src, addr_str));
    printf("\ttype: 0x%04X\n", ntohs(h->h_proto));
    printf("\n");
}

void show_endianness()
{
    #if __BYTE_ORDER == __LITTLE_ENDIAN
        printf("Endianness : Little Endian\n");
    #elif __BYTE_ORDER == __BIG_ENDIAN
        printf("Endianness : Big Endian\n");
    #else
        printf("Endianness : Unknown\n");
    #endif
    printf("\n");
}

void show_ip_header(char *packet, int len)
{
    /* /usr/include/netinet/ip.h */

    struct ip *h;
    h = (struct ip *)(packet + sizeof(struct ethhdr));

    printf("IP header info:\n");

    printf("\tver   : %X\n",h->ip_v);
    printf("\tihl   : %X\n", h->ip_hl);
    printf("\ttos   : 0x%X\n", h->ip_tos);
    printf("\tlen   : %u\n", ntohs(h->ip_len));
    printf("\tid    : %u\n", ntohs(h->ip_id));

    printf("\tfrag : %X ", ntohs(h->ip_off));
    if(ntohs(h->ip_off) == 0x4000)
        printf("(DF)");
    if(ntohs(h->ip_off) == 0x2000)
        printf("(MF)");
    printf("\n");

    printf("\tttl   : %u\n", h->ip_ttl);
    printf("\tproto : %u\n", h->ip_p);
    printf("\tsum   : 0x%X\n", ntohs(h->ip_sum));

    char ipv4str[INET_ADDRSTRLEN];

    inet_ntop(AF_INET, &h->ip_src, ipv4str, INET_ADDRSTRLEN);
    printf("\tsaddr : %X (%s)\n", ntohl(*(uint32_t *)(&h->ip_src)), ipv4str);

    inet_ntop(AF_INET, &h->ip_dst, ipv4str, INET_ADDRSTRLEN);
    printf("\tdaddr : %X (%s)\n", ntohl(*(uint32_t *)(&h->ip_dst)), ipv4str);

    printf("\n");

}

void show_ports(char *packet, int len)
{
    /* Source and Destination ports are in same place */
    /* /usr/include/netinet/tcp.h */
    /* /usr/include/netinet/udp.h */

    struct udphdr *h;
    h = (struct udphdr *)(packet + sizeof(struct ethhdr)+ sizeof(struct ip));

    printf("TCP/UDP Ports:\n");

    printf("\tsrc port : 0x%X (%u)\n", ntohs(h->source), ntohs(h->source));
    printf("\tdst port : 0x%X (%u)\n", ntohs(h->dest), ntohs(h->dest));

    printf("\n");

}

void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
    static int packet_count = 0;
    for (int i = 0;i < 80; i++) putchar('#');
    printf("\n# Packet: %d , Size: %d\n\n", ++packet_count, h->len);
    show_packet_bin((char *)bytes, h->len);
    show_endianness();
    show_ethernet_header((char *)bytes, h->len);
    show_ip_header((char *)bytes, h->len);
    show_ports((char *)bytes, h->len);
}

int main()
{
    pcap_t *p;
    char errbuf[PCAP_ERRBUF_SIZE];
    p = pcap_open_offline("capture.pcap", errbuf);
    if (p == NULL) {
        fprintf(stderr, "\npcap_open_offline() failed: %s\n", errbuf);
        exit(EXIT_FAILURE);
    }

    if (pcap_loop(p, 0, packet_handler, NULL) < 0) {
        fprintf(stderr, "\npcap_loop() failed: %s\n", pcap_geterr(p));
        exit(EXIT_FAILURE);
    }

    return 0;
}