/*
A-SnIf
V2.0

A new version of TLSniff (written about one year ago :)

A lot of things are changed...

Coded by asynchro, pkcrew   
asynchro@pkcrew.org
www.pkcrew.org

10k u bro
*/


#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
#include <linux/if.h>
#include <netdb.h>

#define DEFAULT_IF "lo"
#define MTU        1500
#define MAXL       20
//#define ICMPDATA  // do you want icmp data?

struct ifreq intf;
int sock;
int tnum = 0;
int unum = 0;
int inum = 0;
FILE *lf;
u_long prefadd = 0;
u_short prefport = 0;
u_char isdat = 0;
u_char islog = 0;

struct info
{
  u_long saddr, daddr;
  u_short sport, dport;
  u_long seq, ack;
  u_short window;
  u_short type, id;
  char *pkdata;
  u_short datalen;
  u_short urgf, ackf, pshf, rstf, synf, finf;
}
pkin;

usage (char *name)
{
  banner (stdout, 0);
  printf
    ("Usage: %s [-i interface] [-a address] [-p port] [-l logfile] [-h] [-d]\n",
     name);
  printf ("-i use <interface> instead of the default\n");
  printf ("-a control only packet from/to the selected address\n");
  printf ("-p control only packet from/to the selected port\n");
  printf ("-l log in the selected logfile\n");
  printf ("-d print out data\n");
  printf ("-h print this help screen\n\n");
  exit (0);
}


banner (FILE * fou, int flag)
{
  fprintf (fou,
	   "\nHumani generis mater nutrixque profecto stultitia est\n\n");
  fprintf (fou, "Coded by asynchro\n");
  fprintf (fou, "asynchro@pkcrew.org\n\n");

  if (flag != 0 && islog != 0)
    banner (lf, 0);
}


if_init (char *ifname)
{
  if (getuid () > 0)
    {
      printf ("Only root can do this!\n");
      exit (1);
    }

  if ((sock = socket (AF_INET, SOCK_PACKET, htons (0x0800))) < 0)
    {
      perror ("Socket error");
      exit (1);
    }

  strcpy (intf.ifr_name, ifname);
  if (ioctl (sock, SIOCGIFFLAGS, &intf) < 0)
    {
      perror ("Ioctl");
      exit (1);
    }

  intf.ifr_flags |= IFF_PROMISC;
  if (ioctl (sock, SIOCSIFFLAGS, &intf) < 0)
    {
      perror ("Ioctl");
      exit (1);
    }

}

void
if_end ()
{
  printf ("Closing TLF\n");

  intf.ifr_flags ^= IFF_PROMISC;
  if (ioctl (sock, SIOCSIFFLAGS, &intf) < 0)
    {
      perror ("Ioctl");
      exit (1);
    }
  exit (0);
}

sniff ()
{
  int pktl, datl;
  char *buff, *pkt;
  struct iphdr *ip;
  struct tcphdr *tcp;
  struct udphdr *udp;
  struct icmphdr *icmp;

  pkt = malloc (MTU);

  banner (stdout, 1);

  while (1)
    {
      memset (&pkin, 0, sizeof (struct info));
      memset (pkt, 0, MTU);
      pktl = read (sock, pkt, MTU);
      buff = (pkt + 14);
      ip = (struct iphdr *) buff;
      pkin.saddr = ip->saddr;
      pkin.daddr = ip->daddr;

      switch (ip->protocol)
	{
	case IPPROTO_TCP:
	  tcp = (struct tcphdr *) (buff + sizeof (struct iphdr));
	  pkin.sport = tcp->source;
	  pkin.dport = tcp->dest;
	  pkin.seq = tcp->seq;
	  pkin.ack = tcp->ack_seq;
	  pkin.finf = tcp->fin;
	  pkin.pshf = tcp->psh;
	  pkin.synf = tcp->syn;
	  pkin.rstf = tcp->rst;
	  pkin.ackf = tcp->ack;
	  pkin.urgf = tcp->urg;
	  pkin.window = tcp->window;
	  pkin.datalen =
	    (ntohs (ip->tot_len) - sizeof (struct iphdr) -
	     sizeof (struct tcphdr));
	  pkin.pkdata =
	    (buff + sizeof (struct iphdr) + sizeof (struct tcphdr));

	  if (
	      (prefadd == 0 || prefadd == pkin.saddr
	       || prefadd == pkin.daddr) && (prefport == 0
					     || prefport == pkin.sport
					     || prefport == pkin.dport))
	    tcpwr (stdout);
	  break;

	case IPPROTO_UDP:
	  udp = (struct udphdr *) (buff + sizeof (struct iphdr));
	  pkin.sport = udp->source;
	  pkin.dport = udp->dest;
	  pkin.datalen =
	    (ntohs (ip->tot_len) - sizeof (struct iphdr) -
	     sizeof (struct udphdr));
	  pkin.pkdata =
	    (buff + sizeof (struct iphdr) + sizeof (struct udphdr));

	  if (
	      (prefadd == 0 || prefadd == pkin.saddr
	       || prefadd == pkin.daddr) && (prefport == 0
					     || prefport == pkin.sport
					     || prefport == pkin.dport))
	    udpwr (stdout);
	  break;

	case IPPROTO_ICMP:
	  icmp = (struct icmphdr *) (buff + sizeof (struct iphdr));
	  pkin.type = icmp->type;
	  pkin.id = (icmp->un).echo.id;
	  pkin.datalen =
	    (ntohs (ip->tot_len) - sizeof (struct iphdr) -
	     sizeof (struct icmphdr));
	  pkin.pkdata =
	    (buff + sizeof (struct iphdr) + sizeof (struct icmphdr));


	  if (
	      (prefadd == 0 || prefadd == pkin.saddr
	       || prefadd == pkin.daddr) && (prefport == 0
					     || prefport == pkin.sport
					     || prefport == pkin.dport))
	    icmpwr (stdout, 1);
	  break;
	}

    }

}


udpwr (FILE * fou, int flag)
{
  int dtl;
  char *buff;
  dtl = pkin.datalen;
  buff = pkin.pkdata;
  if (flag != 0)
    unum++;
  fprintf (fou, "UDP Packet number #%d\n", unum);
  fprintf (fou, "%s:%d > %s:%d\n", inet_ntoa (pkin.saddr),
	   ntohs (pkin.sport), inet_ntoa (pkin.daddr), ntohs (pkin.dport));

  if (isdat)
    {
      fprintf (fou, "Data: ");

      while (dtl > 0)
	{
	  if (isprint (*buff))
	    fputc (*buff, fou);
	  buff++;
	  dtl--;
	}
    }

  fprintf (fou, "\n\n");

  if (flag != 0 && islog != 0)
    udpwr (lf, 0);
}

tcpwr (FILE * fou, int flag)
{
  int dtl;
  char *buff;
  dtl = pkin.datalen;
  buff = pkin.pkdata;

  if (flag != 0)
    tnum++;

  fprintf (fou, "TCP Packet number #%d\n", tnum);
  fprintf (fou, "%s:%d > %s:%d\n", inet_ntoa (pkin.saddr),
	   ntohs (pkin.sport), inet_ntoa (pkin.daddr), ntohs (pkin.dport));
  fprintf (fou, "SEQ=%d  ACK=%d\n", ntohl (pkin.seq), ntohl (pkin.ack));
  fprintf (fou, "Window=%d\n", pkin.window);
  fprintf (fou, "Flags:%c%c%c%c%c%c", (pkin.urgf == 1) ? 'U' : '-',
	   (pkin.ackf == 1) ? 'A' : '-', (pkin.pshf == 1) ? 'P' : '-',
	   (pkin.rstf == 1) ? 'R' : '-', (pkin.synf == 1) ? 'S' : '-',
	   (pkin.finf == 1) ? 'F' : '-');
  if (isdat)
    {
      fprintf (fou, "\nData: ");

      while (dtl > 0)
	{
	  if (isprint (*buff))
	    fputc (*buff, fou);
	  buff++;
	  dtl--;
	}
    }

  fprintf (fou, "\n\n");

  if (flag != 0 && islog != 0)
    tcpwr (lf, 0);
}

icmpwr (FILE * fou, int flag)
{
  int dtl;
  char *buff;
  dtl = pkin.datalen;
  buff = pkin.pkdata;


  if (flag != 0)
    inum++;

  fprintf (fou, "ICMP Packet number #%d\n", inum);
  fprintf (fou, "%s > %s\t", inet_ntoa (pkin.saddr), inet_ntoa (pkin.daddr));

  switch (pkin.type)
    {
    case 0:
      fprintf (fou, "ICMP_ECHOREPLY");
      break;
    case 3:
      fprintf (fou, "ICMP_DEST_UNREACH");
      break;
    case 4:
      fprintf (fou, "ICMP_SOURCE_QUENCH");
      break;
    case 5:
      fprintf (fou, "ICMP_REDIRECT");
      break;
    case 8:
      fprintf (fou, "ICMP_ECHO");
      break;
    case 11:
      fprintf (fou, "ICMP_TIME_EXCEEDED");
      break;
    case 12:
      fprintf (fou, "ICMP_PARAMETERPROB");
      break;
    case 13:
      fprintf (fou, "ICMP_TIMESTAMP");
      break;
    case 14:
      fprintf (fou, "ICMP_TIMESTAMPREPLY");
      break;
    case 15:
      fprintf (fou, "ICMP_INFO_REQUEST");
      break;
    case 16:
      fprintf (fou, "ICMP_INFO_REPLY");
      break;
    case 17:
      fprintf (fou, "ICMP_ADDRESS");
      break;
    case 18:
      fprintf (fou, "ICMP_ADDRESSREPLY");
      break;
    }

#ifdef ICMPDATA
  if (isdat)
    {
      fprintf (fou, "\nData: ");

      while (dtl > 0)
	{
	  if (isprint (*buff))
	    fputc (*buff, fou);
	  buff++;
	  dtl--;
	}
    }
#endif


  printf ("\n\n");
  if (flag != 0 && islog != 0)
    icmpwr (lf, 0);
}


u_long resolve (char *host)
{
  struct hostent *hent;
  u_long addr;

  if ((addr = inet_addr (host)) == -1)
    {
      if (!(hent = gethostbyname (host)))
	{
	  perror ("gethostbyname");
	  exit (0);
	}
      memcpy (&addr, hent->h_addr, hent->h_length);
    }
  return (addr);
}

u_short service (char *serv)
{
  struct servent *sent;
  u_short pnum;

  if (!(pnum = atoi (serv)))
    {
      if (!(sent = getservbyname (serv, "udp")))
	{
	  perror ("getservbyname");
	  exit (0);
	}
      pnum = htons (sent->s_port);
    }
  return (htons (pnum));
}


main (int argc, char *argv[])
{
  int option;
  char *interface;
  char *logfile;
  extern char *optarg;

  signal (SIGINT, if_end);
  signal (SIGQUIT, if_end);
  signal (SIGTERM, if_end);
  signal (SIGKILL, if_end);

  logfile = malloc (MAXL);
  interface = malloc (MAXL);

  strcpy (interface, DEFAULT_IF);

  while ((option = getopt (argc, argv, "hdp:a:l:i:")) != EOF)
    switch (option)
      {
      case 'h':
	usage (argv[0]);
	break;
      case 'd':
	isdat = 1;
	break;
      case 'p':
	prefport = service (optarg);
	break;
      case 'a':
	prefadd = resolve (optarg);
	break;
      case 'l':
	islog = 1;
	strcpy (logfile, optarg);
	break;
      case 'i':
	strcpy (interface, optarg);
	break;
      default:
	usage (argv[0]);
      }

  if (islog != 0)
    {
      if ((lf = fopen (logfile, "w")) == NULL)
	{
	  fprintf (stderr, "Error in opening file %s", logfile);
	  exit (1);
	}
    }

  if_init (interface);
  sniff ();
}
