/*
 *  Linux-sniff v1.0
 *
 *  Copyright (C) 2000 Xphere (Xphere@bofh.obit.nl).
 *
 *  Linux eth/tcp/ip sniffer. This tool logs printable data
 *  in the packet or it gives detailed info about the eth/tcp/ip
 *  packet headers. For educational use only. E-mail comments
 *  and whatever you want to Xphere (Xphere@bofh.obit.nl).
 *
 *  Compile: gcc Linux-sniff.c -o Linux-sniff
 *
 *  Greetz: #phreak.nl  -  http://www.casema.net/~gin
 *
 */



#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/if.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/if_ether.h>



struct packet_id {
     unsigned long src_addr;
     unsigned long dst_addr;
     unsigned short src_port;
     unsigned short dst_port;
     int state;
     int d_size;
     long start_time;
     char *data;
} pkt_list[1000];



int open_fd(char *intf)
{
     int f, s;
     struct ifreq ifr;

     if ((f = socket(AF_INET, SOCK_PACKET, htons(0x800))) < 0) {
          return(-1);
     }
     strcpy(ifr.ifr_name, intf);
     if ((s = ioctl(f, SIOCGIFFLAGS, &ifr)) < 0) {
         close(f);
         return(-1);
     }
     ifr.ifr_flags |= IFF_PROMISC;
     if ((s = ioctl(f, SIOCSIFFLAGS, &ifr)) < 0) {
         return(-1);
     }
     return(f);
}



char *lookup(long ip)
{
     static char n[1024];
     struct in_addr ia;
     struct hostent *he;

     ia.s_addr = ip;
     he = gethostbyaddr((char *) &ia, sizeof(struct in_addr), AF_INET);

     if ((sizeof(he->h_name) <= sizeof(n)) && he != NULL) {
          strcpy(n, he->h_name);
     }
     else {
          strcpy(n, inet_ntoa(ia));
     }
     return(n);
}



void print_ethhdr(struct ethhdr *eth)
{
     fprintf(stdout, "+-----< Ethernet Header >\n|\n");
     fprintf(stdout, "| Ethernet Destination (h_dest):\t%u\n", eth->h_dest);
     fprintf(stdout, "| Ethernet Source (h_source):\t\t%u\n", eth->h_source);
     fprintf(stdout, "| Ethernet Protocol (h_proto):\t\t%u\n|\n", eth->h_proto);
     return;
}



void print_iphdr(struct iphdr *ip)
{
     fprintf(stdout, "+-----< IP Header >\n|\n");
     fprintf(stdout, "| Version (version):\t\t\t%d\n", ntohs(ip->version));
     fprintf(stdout, "| IHL (ihl):\t\t\t\t%d\n", ntohs(ip->ihl));
     fprintf(stdout, "| Type of Service (tos):\t\t%d\n", ntohs(ip->tos));
     fprintf(stdout, "| Total Length (tot_len):\t\t%d\n", ntohs(ip->tot_len));
     fprintf(stdout, "| Identification (id):\t\t\t%d\n", ntohs(ip->id));
     fprintf(stdout, "| Fragment Offset (frag_off):\t\t%u\n", ip->frag_off);
     fprintf(stdout, "| Time to Live (ttl):\t\t\t%d\n", ntohs(ip->ttl));
     fprintf(stdout, "| Protocol (protocol):\t\t\t%d\n", ntohs(ip->protocol));
     fprintf(stdout, "| Header Checksum (check):\t\t%d\n", ntohs(ip->check));
     fprintf(stdout, "| Source Address (saddr):\t\t%u\n", ip->saddr);
     fprintf(stdout, "| Destination Address (daddr):\t\t%u\n|\n", ip->daddr);
     return;
}



void print_tcphdr(struct tcphdr *tcp)
{
     fprintf(stdout, "+-----< TCP HEADER >\n|\n");
     fprintf(stdout, "| Source Port (source):\t\t\t%d\n", tcp->source);
     fprintf(stdout, "| Destination Port (dest):\t\t%d\n", tcp->dest);
     fprintf(stdout, "| Sequence Number (seq):\t\t%x\n", tcp->seq);
     fprintf(stdout, "| Acknowledgment Number (ack):\t\t%x\n", tcp->ack_seq);
     fprintf(stdout, "| Data Offset (doff):\t\t\t%d\n", ntohs(tcp->doff));
     fprintf(stdout, "| Reserved 1 (res1):\t\t\t%d\n", ntohs(tcp->res1));
     fprintf(stdout, "| Reserved 2 (res2):\t\t\t%d\n", ntohs(tcp->res2));
     fprintf(stdout, "| Flags (urg|ack|psh|rst|syn|fin):\t");

     if (tcp->urg == 1) { fprintf(stdout, "URG "); }
     if (tcp->ack == 1) { fprintf(stdout, "ACK "); }
     if (tcp->psh == 1) { fprintf(stdout, "PSH "); }
     if (tcp->rst == 1) { fprintf(stdout, "RST "); }
     if (tcp->syn == 1) { fprintf(stdout, "SYN "); }
     if (tcp->fin == 1) { fprintf(stdout, "FIN "); }

     fprintf(stdout, "\n| Window (window):\t\t\t%d\n", ntohs(tcp->window));
     fprintf(stdout, "| Header Checksum (check):\t\t%d\n", ntohs(tcp->check));
     fprintf(stdout, "| Urgent Pointer (urg_ptr):\t\t%u\n", tcp->urg_ptr);
     return;
}


void print_dat(int d_size, char *data)
{
     int i = 0;

     for (i = 0; i < d_size; ++i) {
          if(data[i] == 13) {
               fprintf(stdout, "\n");
          }
          if(isprint(data[i])) {
               fprintf(stdout, "%c", data[i]);
          }
     }
     return;
}



void print_pkt(unsigned long saddr, unsigned long daddr,
               unsigned short src_port, unsigned short dst_port,
               int d_size, char *data) {

     fprintf(stdout, "+-----< HOST: %s ", lookup(saddr));
     fprintf(stdout, "PORT: %d  ->  ", ntohs(src_port));
     fprintf(stdout, "HOST: %s ", lookup(daddr));
     fprintf(stdout, "PORT: %d >\n\n", ntohs(dst_port));
     print_dat(d_size, data);
     return;
}



void rm_pkt(int i)
{
     pkt_list[i].src_addr = 0;
     pkt_list[i].dst_addr = 0;
     pkt_list[i].src_port = 0;
     pkt_list[i].dst_port = 0;
     pkt_list[i].state = 0;
     pkt_list[i].d_size = 0;
     pkt_list[i].start_time = 0;
     free(pkt_list[i].data);
     pkt_list[i].data = NULL;
     return;
}



void check_pkt()
{
     int i;

     for (i = 0; i < 1000; ++i) {
          if (pkt_list[i].state == 1) {
               if ((pkt_list[i].start_time + 1000) < time(NULL)) {
                    print_pkt(pkt_list[i].src_addr, pkt_list[i].dst_addr,
                              pkt_list[i].src_port, pkt_list[i].dst_port,
                              pkt_list[i].d_size, pkt_list[i].data);

                    fprintf(stdout, "\n+-----< Timed out. >\n\n\n");
                    rm_pkt(i);
               }
          }
     }
     return;
}



int find_pkt(struct iphdr *ip, struct tcphdr *tcp)
{
     int i;

     for (i = 0; i < 1000; ++i) {
          if (pkt_list[i].state == 1) {
               if ((pkt_list[i].src_addr == ip->saddr) &&
                   (pkt_list[i].dst_addr == ip->daddr) &&
                   (pkt_list[i].src_port == tcp->source) &&
                   (pkt_list[i].dst_port == tcp->dest)) {

                    return(i);
               }
          }
     }
     return(-1);
}



void reset_pkt(struct iphdr *ip, struct tcphdr *tcp)
{
     int i;

     if ((i = find_pkt(ip, tcp)) > -1) {
          print_pkt(pkt_list[i].src_addr, pkt_list[i].dst_addr,
                    pkt_list[i].src_port, pkt_list[i].dst_port,
                    pkt_list[i].d_size, pkt_list[i].data);

          fprintf(stdout, "\n+-----< Received FIN/RST. >\n\n\n");
          rm_pkt(i);
     }
     return;
}



void init_pkt(struct iphdr *ip, struct tcphdr *tcp)
{
     int i;

     for (i = 0; i < 1000; ++i) {
          if (pkt_list[i].state == 0) {
               pkt_list[i].src_addr = ip->saddr;
               pkt_list[i].dst_addr = ip->daddr;
               pkt_list[i].src_port = tcp->source;
               pkt_list[i].dst_port = tcp->dest;
               pkt_list[i].state = 1;
               pkt_list[i].start_time = time(NULL);
               return;
          }
     }
     return;
}



void data_pkt(struct iphdr *ip, struct tcphdr *tcp, int d_size, char *data)
{
     int i, t, k = 0;
     char *bsd;

     if ((i = find_pkt(ip, tcp)) > -1) {
          if (d_size > 0) {
               if (pkt_list[i].data == NULL) {
                    pkt_list[i].data = malloc(d_size);
                    pkt_list[i].d_size += d_size;
                    for (t = 0; t < d_size; ++t) {
                         pkt_list[i].data[t] = data[t];
                    }
               }
               else {
                    bsd = pkt_list[i].data;
                    pkt_list[i].data = malloc(pkt_list[i].d_size + d_size);
                    for (t = 0; t < pkt_list[i].d_size; ++t) {
                         pkt_list[i].data[k] = bsd[t];
                         ++k;
                    }
                    pkt_list[i].d_size += d_size;
                    for (t = 0; t < d_size; ++t) {
                         pkt_list[i].data[k] = data[t];
                         ++k;
                    }
                    free(bsd);
               }
          }
     }
     return;
}



void proc_packet(char *buf, int mode)
{
     struct ethhdr *eth;
     struct iphdr *ip;
     struct tcphdr *tcp;
     char *data;
     int d_size, e_size, i_size, t_size;

     e_size = sizeof(struct ethhdr);
     i_size = sizeof(struct iphdr);
     t_size = sizeof(struct tcphdr);

     eth = (struct ethhdr *) buf;
     ip = (struct iphdr *) (buf + e_size);
     tcp = (struct tcphdr *) (buf + e_size + i_size);
     data = (buf + e_size + i_size + t_size);
     d_size  = (htons(ip->tot_len) - i_size - t_size);

     if (ip->protocol != 6) {
          return;
     }

     if (mode == 1) {
          check_pkt();
          if ((tcp->syn == 1) && (find_pkt(ip, tcp) == -1)) {
               init_pkt(ip, tcp);
          }
          else if ((tcp->rst == 1) || (tcp->fin == 1)) {
               reset_pkt(ip, tcp);
          }
          else {
               data_pkt(ip, tcp, d_size, data);
          }
          /* print_dat(d_size, data);
          fflush(stdout); */
     }
     else if (mode == 2) {
          fprintf(stdout, "+-----< HOST: %s ", lookup(ip->saddr));
          fprintf(stdout, "PORT: %d  ->  ", tcp->source);
          fprintf(stdout, "HOST: %s ", lookup(ip->daddr));
          fprintf(stdout, "PORT: %d >\n", ntohs(tcp->dest));

          fprintf(stdout, "| SEQ:\t\t%x\n", tcp->seq);
          fprintf(stdout, "| ACK:\t\t%x\n", tcp->ack_seq);
          fprintf(stdout, "| FLAGS:\t");

          if (tcp->urg == 1) { fprintf(stdout, "URG "); }
          if (tcp->ack == 1) { fprintf(stdout, "ACK "); }
          if (tcp->psh == 1) { fprintf(stdout, "PSH "); }
          if (tcp->rst == 1) { fprintf(stdout, "RST "); }
          if (tcp->syn == 1) { fprintf(stdout, "SYN "); }
          if (tcp->fin == 1) { fprintf(stdout, "FIN "); }

	  fprintf(stdout, "\n\n\n");
          fflush(stdout);
     }
     else if (mode == 3) {
          fprintf(stdout, "+-----< HOST: %s ", lookup(ip->saddr));
          fprintf(stdout, "PORT: %d  ->  ", tcp->source);
          fprintf(stdout, "HOST: %s ", lookup(ip->daddr));
          fprintf(stdout, "PORT: %d >\n", ntohs(tcp->dest));

          print_ethhdr(eth);
          print_iphdr(ip);
          print_tcphdr(tcp);
          fprintf(stdout, "\n\n\n");
          fflush(stdout);
     }
     return;
}



void init_pkt_list()
{
     int i;

     for (i = 0; i < 1000; ++i) {
          pkt_list[i].src_addr = 0;
          pkt_list[i].dst_addr = 0;
          pkt_list[i].src_port = 0;
          pkt_list[i].dst_port = 0;
          pkt_list[i].state = 0;
          pkt_list[i].start_time = 0;
          pkt_list[i].data = NULL;
     }
     return;
}



void terminate()
{
     fprintf(stdout, "Received signal, exiting.\n");
     exit(0);
}



int main (int argc, char *argv[])
{
     int sd, i, mode;
     char buf[1500];

     if (argc != 3) {
          fprintf(stderr, "\n\e[0;34m[ Linux-sniff v1.0 ");
          fprintf(stderr, "by: Xphere -- #phreak.nl ]\e[0m\n\n\n");
          fprintf(stderr, "Usage: %s <interface> <mode>\n\n", argv[0]);
          fprintf(stderr, "  - mode 1: data sniff mode.\n");
          fprintf(stderr, "  - mode 2: tcp/ip packet info mode.\n");
          fprintf(stderr, "  - mode 3: verbose packet info mode.\n");
          fprintf(stderr, "\nExample: %s eth0 1 > sniff.log &\n", argv[0]);
          exit(-1);
     }

     if ((mode = atoi(argv[2])) < 1) {
          fprintf(stderr, "Error: argument 2 must be 1, 2 or 3\n");
          exit(-1);
     }

     if ((sd = open_fd(argv[1])) < 0) {
          fprintf(stderr, "Error: can't get promicuous socket.\n");
	  exit(-1);
     }

     signal(SIGINT, terminate);
     signal(SIGKILL, terminate);
     signal(SIGQUIT, terminate);
     signal(SIGTERM, terminate);
     signal(SIGHUP, SIG_IGN);

     fprintf(stdout, "\n[ Linux-sniff ");
     fprintf(stdout, "by: Xphere -- #phreak.nl ]\n\n\n");

     if (mode == 1) {
          init_pkt_list();
     }

     while (1) {
          if ((i = read(sd, buf, 1499)) > 1) {
	       proc_packet(buf, mode);
          }
     }
     exit(0);
}
