/*
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

*********************************************
-f <filter> added by PHDATAFLEX - 03/2004
-c capture ICMP added by PHDATAFLEX - 03/2004
phdataflex@hotmail.com
http://www.geocities.com/phprogamhp 
*********************************************

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.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       40
#define MINFILTER  3
#define MAXFILTER  40

//#define ICMPDATA  // do you want icmp data?

struct ifreq intf;
int sock;
int tnum = 0;
int unum = 0;
int inum = 0;
FILE *lf;
char *filter;
u_long prefadd = 0;
u_short prefport = 0;
u_char isdat = 0;
u_char islog = 0;
u_char isfilter = 0;
u_char logicmp = 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] [-f filter] [-l logfile] [-c] [-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 ("-f control only packet with filter string\n");
  printf ("-l log in the selected logfile\n");
  printf ("-c log ICMP packets\n");
  printf ("-d print out data\n");
  printf ("-h print this help screen\n\n");
  printf ("Ex: %s -i eth0 -a 200.185.55.88 -p 80 -f password,login,user,root -l log.dat -c -d\n",name);
  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);
}

char *strsub ( char *sub, int lensub, char *str, int lenstr )
{
  //--- Added by PHDATAFLEX
  //--- substr is in str? YES return *char,  NO return 0
  int i, j;

  for( i=0; i<lenstr - lensub + 1; i++ )
  {	  
    for( j=0; j<lensub; j++ )
    {	    
      if ( ( str[i+j] | 32 ) != ( sub[j] | 32 ) ) break;
      if ( j == lensub-1 ) return str + i;
    }
  } 
  return 0;  
}

int
isfilterok ()
{
  //--- Added by PHDATAFLEX
  //--- packet has one word defined by filter option?
  //--- YES return 1, NO return 0
  int  dtl, i, j;
  char *buff;
  char palavra[MAXL+1];

  dtl  = pkin.datalen;
  buff = pkin.pkdata;
  i    = 0;
  do{ 
    for ( j=0 ; i<= strlen(filter); i++, j++ )
    {
      if ( filter[i] == ',' )
      {
         palavra[j]='\0';
         j=0;
         break;
      }
      else
        palavra[j] = filter[i];
    }
    if ( strsub( palavra, strlen(palavra), buff, dtl  ) != 0 )     
      return 1;
    i++;
  } while ( i < strlen(filter) );
  return 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))
             if (logicmp == 1) icmpwr (stdout, 1);
	  break;
	}

    }

}


udpwr (FILE * fou, int flag)
{
  int dtl;
  char *buff;
  dtl = pkin.datalen;
  buff = pkin.pkdata;

  if (isfilter != 0)
    if (isfilterok() == 0) return;

  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 (isfilter != 0)
    if (isfilterok() == 0) return;
	  
  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;

#ifdef ICMPDATA
  if (isfilter != 0)
    if (isfilterok() == 0) return;
#endif

  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);
  filter = malloc (MAXFILTER);
  
  strcpy (interface, DEFAULT_IF);

  while ((option = getopt (argc, argv, "hdcp:a:l:i:f:")) != EOF)
    switch (option)
      {
      case 'h':
	usage (argv[0]);
	break;
      case 'd':
	isdat = 1;
	break;
      case 'c':  //--- added by phdataflex
	logicmp = 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;
      case 'f': //--- added by phdataflex
     	isfilter = 1;
	strcpy (filter, optarg);
	break;
      default:
	usage (argv[0]);
      }

  if (isfilter != 0)
    {
      if ( strlen(filter) < MINFILTER )
	{
	  fprintf(stderr, "Error: filter len less than %d!\n", MINFILTER );
	  exit(1);
	}
      if ( strlen(filter) > MAXFILTER )
	{
	  fprintf(stderr, "Error: filter len greater than %d!\n", MAXFILTER );
	  exit(1);
	}
    }
  
  
  if (islog != 0)
    {
      if ((lf = fopen (logfile, "w")) == NULL)
	{
	  fprintf (stderr, "Error in opening file %s", logfile);
	  exit (1);
	}
    }

  if_init (interface);
  sniff ();
}
