Logo Search packages:      
Sourcecode: karpski version File versions  Download package

karpski.c

/*
    *********************************************************************
                                  k-arp-ski

                       ye olde network niffer/analyzer

                                 Written by,                       
                               Brian Costello
                                btx@calyx.net

            built on top of libpcap by Van Jacobson and his crew
    *********************************************************************
*/

#include <stdio.h>
#include <config.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#ifdef TM_IN_SYS_TIME
 #include <sys/time.h>
#else
 #include <time.h>
#endif
#include <netdb.h>
#include <pcap.h>
#include <string.h>
#include <pthread.h>
#include <gtk/gtk.h>
#include <gtk/gtkclist.h>

#include "gtk_misc.h"
#include "proto.h"
#include "vendors.h"
#include "connects.h"
#include "karpski.h"
#include "smashy.h"
#include "help.h"
#include "gui.h"
#include "scrupdate.h"

/*

****************************************

GLOBALS

****************************************

*/

pcap_t *h_pcap;               /* The pcap reference block */
t_karpski_stats karpski_stats;      /* The statistics kept by karpski */
static int scrupdatetag = 0;
time_t start_time, stop_time; /* Time values used for calculating the total time */
int resolve_addr = 1;         /* Should we resolve all IP addresses? */
char strbuf[128];       /* A scratch buffer to be used by all */
char device[IFNAMSIZ+1];      /* The network device name (ie: eth0) 
                                 * we are capturing from */
int started = 0;        /* A flag if it's running or not */
int selected_row = -1;        /* We have currently selected no row */
int byte_read_flag = 0;       /* Used so we don't update bytes every cycle! */
int can_smashy = 1;           /* If the smashy.dat file is read in... */
int logfd = -1;               /* The output log file descriptor */
long total_malloced=0;        /* The number of bytes in use by structures */
#ifdef SCAN_SOUND_BROKEN
int soundevfd = 0;            /* Used for the sound */
static int soundplaytag = 0;  /* The tag returned by the sound play timeout */
#endif
int stablenet = 0;            /* After loading or saving a network map, 
                         * the network is considered stable.
                         * If a new item is generated, a warning is
                         * printed.
                         */
int needssave = 0;                         
pthread_mutex_t ArpRecMutex, ConnectMutex, TextItemMutex;
pthread_t CaptureThread;
pthread_t NSLookupThread;

extern int connect_row;        /* From connect.c */
extern GtkWidget *connectwindow;
extern GtkWidget *connectlist, *label2;
extern t_vendors *first_vendors; /* From vendors.c */
extern GtkWidget *helpwindow;  /* From help.c */
extern GtkWidget *logtext, *infowindow, *main_clist, *statswindow;
extern GtkWidget *nb_read_label, *mainwindow;
extern protolist *first_protolist; /* From proto.c */
extern int protocoltag;
t_arp_rec *selected_ar = NULL;
t_arp_rec *first_arp_rec = NULL;

/*
 ***********************************
 S O U N D   C O D E   O F   D O O M
 Abandon all hope, ye who enter here
 ***********************************
*/
 

#ifdef SCAN_SOUND_BROKEN

void open_soundev()
{
   if ((soundevfd = open("/dev/audio",O_WRONLY))<0)
   {
      perror("open soundev");
      exit(1);
   }
   return;
}

int play_scan_sound()
{
   int devfd;
   int nbytes;
   char buf[1024];
   char fn[256];
   
   sprintf(fn, "%sscanner.au", KARPSKI_DATA_DIR);
   
   if ((devfd = open("scanner.au", O_RDONLY))<0)
   {
      perror("open scanner.au");
      return FALSE;
   }
   
   while ((nbytes = read(devfd, buf, sizeof(buf)))>0)
   {
      if (write(soundevfd, buf, sizeof(buf))<0)
      {
         perror("write");
         return FALSE;
      }
   }
   
   return TRUE;
}
#endif

#ifdef MALLOC_TRACE

void *malloc_new(long size, char *str)
{
   void *newptr;
   
   newptr = malloc(size);
   total_malloced+=size;
   printf("Total malloced (%s): %ld\n", str, total_malloced);
   return(newptr);
}

void free_new(void *ptr, char *str)
{
   total_malloced -= 64;
   free(ptr);
   printf("Freeing %s - %ld\n", str, total_malloced);
   return;
}

char *strdup_new(char *str, char *id)
{
   char *newstr;
   
   newstr = (char *)malloc_new(strlen(str)+1, id);
   if (newstr)
      bcopy(str, newstr, strlen(str));
   return(newstr);
}

#endif

void mutex_enter(pthread_mutex_t sem)
{
   int oldval;
   
/*   printf("Entering mutex...\n"); */
   pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldval);
   pthread_mutex_lock(&sem);
}

void mutex_exit(pthread_mutex_t sem)
{
   int oldval;
   
   pthread_mutex_unlock(&sem);
   pthread_testcancel();
   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldval);
   pthread_testcancel();
/*   printf("Exiting mutex...\n"); */
}

/************************************************************
 isbroadcastaddr(char *)
 This ass code has many of the popular broadcast addresses.  
 The problem is that I generalized too much, so it will
 think an address is broadcast even if it's just unregistered
 and looks like one of these.  Also, this doesn't check vs.
 the expected ether frame type.
 This is called by find_arp_rec to figure out if it should add
 this to the clist even if there is one or more of the same
 hwaddr in the main_clist with different addresses.
 This should be rewritten.
 *************************************************************/
 

char *resv_broadcasts[] = {"FF:FF:FF:FF:FF:FF", "01:00:5E", "09:00", "AB:00:00", "CF:00:00:00:00:00", ""};

int isbroadcastaddr(char *hw_addr)
{
   int i;
      
   if ((!hw_addr) || (!hw_addr[0]))
      return 0;
      
   for (i=0; ((resv_broadcasts[i][0]) && strncmp(hw_addr, resv_broadcasts[i], strlen(resv_broadcasts[i]))) ; i++) ;
   if (resv_broadcasts[i][0])
      return 1;

   return 0;
}

int get_ar_rowno(t_arp_rec *t_ar, GtkCList *clist)
{
   int i;
   int done=-1;
   char *hwaddr;
   char *addr;
   
   for (i=0; ((i<clist->rows) && (done<0)); i++)
   {
      gtk_clist_get_text(clist, i, 1, &hwaddr);
      gtk_clist_get_text(clist, i, 2, &addr);
      if (t_ar->machine_addr[0])
      {
         if ((!strcasecmp(t_ar->hw_addr, hwaddr)) && (!strcasecmp(t_ar->machine_addr, addr)))
            done = i;
      }
      else
      {
         if (!strcmp(t_ar->hw_addr, hwaddr))
            done = i;
      }
   }
   
   return(done);
}


/**************************************************************
 find_arp_rec(char *, char *, t_arp_rec **, int *)
 
 This searches the global list pointed to by the global variable
 first_arp_rec for whatever matches hw_addr.  If the address is
 a broadcast addr, it also must match the machine_addr variable.

 On success, this returns a pointer to the found t_arp_rec and
 returns the rowno of where the item is located in main_clist.
 On failure, the routine returns NULL and sets last_arp_rec to
 a pointer to the last arp record (so that one may easily be
 added to the end of the list)
 **************************************************************/
 
t_arp_rec *find_arp_rec(char hw_addr[], char machine_addr[], t_arp_rec **last_arp_rec, int *rowno, int *insrowno)
{
   t_arp_rec *t_ar, *fnd_tr;
   int long_search;
   int j;
   
/*   long_search = isbroadcastaddr(hw_addr); */
   long_search = 1;           /* Trying this out ... */

   fnd_tr = NULL;
   
   if (last_arp_rec)
      *last_arp_rec = NULL;
   if (rowno)
      (*rowno)=0;
   if (insrowno)
      (*insrowno) =0;
      
   mutex_enter(ArpRecMutex);
      
   for (t_ar = first_arp_rec; ((t_ar) && (!fnd_tr)) ; t_ar = t_ar->next)
   {
      if (long_search)
      {
         j = strcmp(hw_addr, t_ar->hw_addr);
         if ((j<0) && (KEEP_SORTED))
         {
            t_ar = NULL;
            break;
         }
         if ((!j) && (!strcmp(machine_addr, t_ar->machine_addr)))
            fnd_tr = t_ar;
         else
            if (insrowno)
               (*insrowno)++;
      }               
      else
      {
         if (!strcmp(hw_addr, t_ar->hw_addr))
            fnd_tr = t_ar;
      }
      
      if (last_arp_rec)
         *last_arp_rec = t_ar;
   }

   
   if ((fnd_tr) && (rowno))
      *rowno = get_ar_rowno(fnd_tr, GTK_CLIST(main_clist));
   
   mutex_exit(ArpRecMutex);
   
   return(fnd_tr);
}

/**************************************************
 Returns 1 if it's a reserved subnet (only official
 reserved subnets observed)
 *************************************************/

int reserved_subnet(unsigned int addr)
{

   if ((addr & 0x000000ff) == 0x0000000a) /* 10.0.0.0/8 */
      return(1);

   if ((addr & 0x00000fff) == 0x00000fac) /* 172.16.0.0/12 */
      return(1);

   if ((addr & 0x0000ffff) == 0x0000a8c0) /* 192.168.0.0/16 */
      return(1);

   return(0);
}

/**************************************************
 This function goes with writepkt -
 it writes the string str to the filedescriptor
 outfd.
 If it fails, it bombs out of the whole program
 because it probably means the disk is full!
 *************************************************/
 

void writeit(char *str, int outfd)
{
   if (write(outfd, str, strlen(str)) < strlen(str))
   {
      perror("write");
      exit(1);
   }
}

/*************************************************************

Function: writepkt()
Desc: Writes the packet located in the packet_info structure to the file
      descriptor in packet_info (if any).
Input:      The packet info structure and a pointer to its arp_rec 
Return:     Nothing

**************************************************************/

void writepkt(packet_parse *packet_info, t_arp_rec *t_ar)
{
/* 
   Modify WPKTLEN to change the number of characters across the sniffer
   will write to capture files
 */
#define WPKTLEN 22

   int i, j, remain, leftover;
   u_char ptrval;
   time_t timeval;
   struct tm *tptr;

   time(&timeval);
   tptr = localtime(&timeval);
   
   writeit("------------------------------------------------------------------------------\n", packet_info->outfd);
   if (packet_info->src_address[0])
   {
      sprintf(strbuf,"From: %s to %s\n", packet_info->src_address, packet_info->dst_address);
      writeit(strbuf, packet_info->outfd);
   }
   sprintf(strbuf,"Protocol: %s - Length: %d - Captured at %02d:%02d:%02d %02d/%02d/%04d\n", 
                       packet_info->protostr, packet_info->dlen, tptr->tm_hour, 
                       tptr->tm_min, tptr->tm_sec, tptr->tm_mon, tptr->tm_mday, 
                       tptr->tm_year+1900);
   writeit(strbuf, packet_info->outfd);
   writeit("------------------------------------------------------------------------------\n", packet_info->outfd);

   for (i=0; i<packet_info->numaddons; i++)
   {
      if (packet_info->addonstrs[i])
         sprintf(strbuf, "%s = %s\n", packet_info->addons[i], packet_info->addonstrs[i]);
      else
         sprintf(strbuf, "%s = %ld\n", packet_info->addons[i], packet_info->addonvals[i]);
      
      writeit(strbuf, packet_info->outfd);   
   }
   
   i=0;
   while (i<packet_info->dlen)
   {
      sprintf(strbuf, "%4X | ", i);
      writeit(strbuf, packet_info->outfd);
      remain = i+WPKTLEN;
      if (remain > packet_info->dlen)
      {
         remain = packet_info->dlen;
         leftover = i+WPKTLEN - packet_info->dlen;
      }
      else
         leftover = 0;
      
      for (j=i; j<remain; j++)
      {
         sprintf(strbuf, "%02X", packet_info->dptr[j] & 0xff);
         writeit(strbuf, packet_info->outfd);
      }
      for (j=0; j<leftover; j++)
      {
         sprintf(strbuf, "  ");
         writeit(strbuf, packet_info->outfd);
      }
      
      writeit(" | ", packet_info->outfd);

      for (j=i; j<remain; j++)
      {
         ptrval = (packet_info->dptr[i] & 0x7f);
         sprintf(strbuf, "%c", (ptrval > 32) ? ptrval : ' ');
         writeit(strbuf, packet_info->outfd);
         i++;
      }
      
      writeit("\n", packet_info->outfd);
   }
}

/*************************************************************

Function: printpkt()
Desc: Writes the packet located in pkt to the gtk textbox
      *text (if it exists).  The packet is of len length
Input:      text- the gtktextbox, pkt - the packet, len - the length
Return:     Nothing
Notes:      This should REALLY be merged code with writepkt...
      but I'm a lazy lazy man :(  This prints much less info
      than writepkt... hmmmmm...

*************************************************************/

void printpkt(GtkWidget **text, packet_parse *packet_info, char *methodstr)
{
   
#define PKTWIDTH 14

   int i, j, nextspot, finishamt;
   u_char val;
   char *pkt = packet_info->dptr;
   
   if (!text)
      return;

   print_gtk(text, "------------------------------\n");
   print_gtk(text, "Protocol: %s - len %d\n", packet_info->protostr, packet_info->dlen);
   for (i=0; i<packet_info->numaddons; i++)
   {
      if (packet_info->addonstrs[i])
         print_gtk(text, "%s = %s\n", packet_info->addons[i], packet_info->addonstrs[i]);
      else
         print_gtk(text, "%s = %ld\n", packet_info->addons[i], packet_info->addonvals[i]);
   }
   i=0;
   while (i<packet_info->dlen)
   {
      nextspot = i+PKTWIDTH;
      if (nextspot > packet_info->dlen)
      {
         nextspot = packet_info->dlen;
         finishamt = (i+PKTWIDTH)-packet_info->dlen;
      }
      else
         finishamt = 0;
      
      for (j=i; (j<nextspot); j++)
         print_gtk(text, "%02X", pkt[j] & 0xff);
      for (j=0 ; j<finishamt; j++)
         print_gtk(text,"  ");
      print_gtk(text, " | ");
      for (j=i; (j<nextspot);j++)
      {
         val = pkt[j] & 0x7f;
         print_gtk(text, "%c", (val>26) ? val : ' ');
         i++;
      }
      for (j=0 ; j<finishamt; j++)
         print_gtk(text," ");
      print_gtk(text," |\n");
   }
}
         
/******************************************
 dump_packet(packet_parse *, t_arp_rec *)
 This function dumps the packet to both
 screen and file (if it matches a capture
 filter)
 ******************************************/ 

void dump_packet(packet_parse *packet_info, t_arp_rec *t_ar, int write_stats)
{
   if (t_ar->watchwindow)
      printpkt(&t_ar->watchtextbox, packet_info, packet_info->protostr);
   if ((packet_info->outfd>0) && (write_stats))
      writepkt(packet_info, t_ar);
   return;
}

/**************************************************************

return_fqdn()

Input: The address you want resolved
Output: The FQDN (minus LOCAL_DOMAIN (defined in karpski.h))
        OR N/A if resolving is off
        OR reserved subnet if the domain is reserved
Note: Does not return a malloc'd string!!
****************************************************************/

char *return_fqdn(struct sockaddr_in sin, char machine_addr[])
{
   char *machine_name;
   struct hostent *he;
   
   machine_name = strbuf;
   
   if (!machine_addr[0])
   {
      strcpy(machine_name, "Unsupp. protocol");
      return(machine_name);
   }
   
   if (reserved_subnet(sin.sin_addr.s_addr))
   {
      strcpy(machine_name, "Reserved subnet");
   }
   else
   
      if (sin.sin_addr.s_addr == 0xffffffff)
         strcpy(machine_name, "Broadcast");
      else
         if (!resolve_addr)
         {
            strcpy(machine_name, "N/A");
         }
         else
            {
/*               logmsg(&logtext, logfd, 0, "\nLooking up host %s...\n", machine_addr); */
               if (!(he = gethostbyaddr((char *)&sin.sin_addr, 4, AF_INET)))
               {
                  if (h_errno == NO_ADDRESS)
                     logmsg(&logtext, logfd, 0, "Warning: No DNS entry for %s\n", machine_addr);
                  machine_name[0] = '\0';
               }
               else
               {  
                  int hl, dl;
                 
                  dl = strlen(LOCAL_DOMAIN);
                  hl = strlen(he->h_name);

                  if ((hl > dl) && (!strcmp(&he->h_name[hl-dl], LOCAL_DOMAIN)))
                  {
                     strncpy(machine_name, he->h_name, hl-dl);
                     machine_name[hl-dl] = '\0';
                  }
                  else
                  {
                     strcpy(machine_name, he->h_name);
                  }
               }
/*               logmsg(&logtext, logfd, 0, "Lookup done.\n"); */
            }
   return(machine_name);
}

/************************************************************************

void inc_dgconns: This updates an arp rec's stats and calls the connection
management functions (in connects.c) for connection packets (conn_type = 2)

*************************************************************************/

void inc_dgconns(t_arp_rec *t_ar, packet_parse *packet_info, int our_clist_showing)
{
   int sport, dport;
   
   if (packet_info->protocol_ptr)
   {
         
      switch (packet_info->protocol_ptr->conn_type)
      {
         case 1 : 
            t_ar->ndgrams++;
            if ((infowindow) && (selected_ar == t_ar))
               set_info_update(256);
            break;
            
         case 2 :
         {
            struct in_addr srcaddr, dstaddr;
            
            if ((sport = getaddonval(packet_info, "source port"))<0)
               return;
            if ((dport = getaddonval(packet_info, "dest port"))<0)
               return;
               
            srcaddr.s_addr = inet_addr(packet_info->src_address);
            dstaddr.s_addr = inet_addr(packet_info->dst_address);
            if (add_connection(t_ar, srcaddr, sport, dstaddr, dport, our_clist_showing, packet_info, packet_info->frametype))
            {
               t_ar->nconns++;
               set_info_update(128);
            }
            break;
         }
      }
   }

}

/*************************************************************

make_new_arp_rec - Does what it says.

**************************************************************/



void make_new_arp_rec(char hw_addr[], char machine_addr[], packet_parse *packet_info, t_arp_rec *last_arp_rec, int rowno, int dumppkt, int insrowno)
{
   t_arp_rec *t_ar;
   char *vend;
   struct sockaddr_in sin;
   int our_clist_showing;
   
   needssave++;
   
   sin.sin_addr.s_addr = inet_addr(machine_addr);
/*   if (sin.sin_addr.s_addr == 0)
      return; */
      
   t_ar = (t_arp_rec *) MALLOC(sizeof(t_arp_rec), "New arp rec - karpski.c");
   if (t_ar==NULL)
   {
      perror("malloc");
      return;
   }
   
   karpski_stats.nhosts++;
   set_overall_update(1);
   
   strcpy(t_ar->hw_addr, hw_addr);
   strcpy(t_ar->machine_addr, machine_addr);
   t_ar->machine_name[0] = 0x00;
   t_ar->sin = sin;
   
/*   strcpy(t_ar->machine_name, return_fqdn(sin, machine_addr)); */
   strcpy(t_ar->methodstr, packet_info->protostr);
   
   vend = find_vendstr(hw_addr);
   if (vend)
      strcpy(t_ar->vend_str, vend);
   else
      strcpy(t_ar->vend_str, "Unknown vendor");
      
   t_ar->first_connect = NULL;
   t_ar->nconns = 0;
   t_ar->ndgrams = 0;
   t_ar->nbytes = packet_info->dlen;
   t_ar->watching = 0;
   t_ar->row = rowno;
   t_ar->num_diff_addrs = 0;
   t_ar->needs_update=1;
   t_ar->watchwindow = NULL;
   t_ar->watchtextbox = NULL;
   t_ar->alias[0] = '\0';
   strcpy(t_ar->role, "Undetermined");
   t_ar->next = NULL;
   
   mutex_enter(ArpRecMutex);
   set_main_list_update(t_ar);
   if (last_arp_rec)
   {
      t_ar->next = last_arp_rec->next;
      last_arp_rec->next = t_ar;
   }
   else
   {
      t_ar->next = first_arp_rec;
      first_arp_rec = t_ar;
   }

   mutex_exit(ArpRecMutex);

/* @@@ */   
   if (dumppkt)
      if (packet_info->dptr)
         dump_packet(packet_info, t_ar, 1);

   if ((infowindow) && (selected_ar==t_ar) && (packet_info->dlen))
   {
      set_info_update(512);
   }

   our_clist_showing=((selected_ar) && (connectlist) && (connectwindow) && 
                      (!strcmp(selected_ar->hw_addr, packet_info->saddr)));
                      
   inc_dgconns(t_ar, packet_info, our_clist_showing);

   if (stablenet)
   {
      karpski_stats.nwarnings++;
      set_overall_update(2);
      logmsg(&logtext, logfd, 1, "Warning: new station: %s / %s\n", t_ar->hw_addr, t_ar->machine_addr);
   }
   return;
}

/****************************************************************************

add_arp_rec:  This function takes care of adding a packet to the main clist,
displaying the main_clist, watch packets, outputting watch packets to files,
etc.  Needless to say, this function should be gutted.  I do a lot of stupid
things for no real good reason.

Input: The packet_info structure

*****************************************************************************/

void add_arp_rec(packet_parse *packet_info)
{
   t_arp_rec *fnd_tr, *last_arp_rec;
   int rowno=0;
   int our_clist_showing;
   int insrowno;

/* This is where the number of datagrams gets inc'd.  Connections get inc'd
 * in connects.c
 */

   if (packet_info->protocol_ptr)
   {
      switch (packet_info->protocol_ptr->conn_type)
      {
         case 1 : karpski_stats.ndatagrams++; set_overall_update(8); break;
      }
   }
   
/* Now figure out if our connection list is showing.  This is so we know
 * if we should update the connect windows if a new connection is added.
 */   
   
   our_clist_showing=((selected_ar) && (connectlist) && (connectwindow) && 
                      (!strcmp(selected_ar->hw_addr, packet_info->saddr)));

/* Find if our current packet's record is there or if it's a new HDaddr */
   
   fnd_tr = find_arp_rec(packet_info->saddr, packet_info->src_address, &last_arp_rec, &rowno, &insrowno);

/* If not found, make a new packet */
   if (!fnd_tr)
      make_new_arp_rec(packet_info->saddr, packet_info->src_address, packet_info, last_arp_rec, rowno, 1, insrowno);
   else
/* Else check to see if this has a different address as is showing */
   {
      if (strcmp(packet_info->src_address, fnd_tr->machine_addr))
      {
/* Yes, it's new, check to see if it's currently blank.  If so, we can probably
 * replace it with this new packet's address.
 */      
         if ( (!packet_info->src_address[0]) && 
              ((strspn(packet_info->src_address,".0:") < strlen(packet_info->src_address)) && 
              (strspn(fnd_tr->machine_addr, ".0:") == strlen(fnd_tr->machine_addr))) )
         {
            strcpy(fnd_tr->machine_addr, packet_info->src_address);
            mutex_enter(ArpRecMutex);
            fnd_tr->needs_update=2;
            mutex_exit(ArpRecMutex);
         }
         else
/* Well, this has a new address.  Looks like it could be a router if it's > 5.
 * What a rig :(
 */         
         {
            if (++fnd_tr->num_diff_addrs>5)
               strcpy(fnd_tr->role, "Router");
         }
      }
      
/* Update the number of bytes read for this particular arp rec */

      fnd_tr->nbytes+=packet_info->dlen;  
      if (packet_info->display_proto)
      {
         if (packet_info->dptr)
            dump_packet(packet_info, fnd_tr, 1);            /* Don't write it this time */
      }
      inc_dgconns(fnd_tr, packet_info, our_clist_showing);
      if ((infowindow) && (selected_ar == fnd_tr))
      {
         set_info_update(512);
      }
   }

/*
   Rig alert; Need to optimize above and below to make one function.  All
   that changes is daddr/dst_address saddr/src_address.
   
   Also, certain stats are duplicated in this that shouldn't be.  I hate
   this code :(
 */

   our_clist_showing=((selected_ar) && (connectlist) && (connectwindow) && 
                      (!strcmp(selected_ar->hw_addr, packet_info->daddr)));

   fnd_tr = find_arp_rec(packet_info->daddr, packet_info->dst_address, &last_arp_rec, &rowno, &insrowno);
   if (!fnd_tr)
      make_new_arp_rec(packet_info->daddr, packet_info->dst_address, packet_info, last_arp_rec, rowno, 0, insrowno);
   else
   {
      if (strcmp(packet_info->dst_address, fnd_tr->machine_addr)) 
      {
         if ((!packet_info->dst_address[0]) && 
         ((strspn(packet_info->dst_address,".0:") < strlen(packet_info->dst_address)) && 
          (strspn(fnd_tr->machine_addr, ".0:") == strlen(fnd_tr->machine_addr))))
         {
            strcpy(fnd_tr->machine_addr, packet_info->dst_address);
            mutex_enter(ArpRecMutex);
            fnd_tr->needs_update=2;
            mutex_exit(ArpRecMutex);
         } 
         else
         {
            if (++fnd_tr->num_diff_addrs>5)
               strcpy(fnd_tr->role, "Router");
         }
      }
      fnd_tr->nbytes+=packet_info->dlen;        /* @@ incs the dlen instead of the packet length */
      if (packet_info->display_proto)
      {
         if (packet_info->dptr)
            dump_packet(packet_info, fnd_tr, 0);            /* Don't write it this time */
      }
      inc_dgconns(fnd_tr, packet_info, our_clist_showing);
      if ((infowindow) && (selected_ar == fnd_tr))
      {
         set_info_update(512);
      }
   }
   
   return;
}

/************************************
 C A L L B A C K   F U N C T I O N S
 ************************************/


/*

The main capture routine.  This is what gets called by pcap when it either
times out or gets a packet.

*/

struct encap
{
   u_char dsap;
   u_char ssap;
   u_char cntl;
   u_char org_code[3];
   u_char encap_type[2];
};

/*************************************************************************

callback: This is the function called by timeout_func every time a packet
is received.  The bytes updated flag is updated once every UPDATE_BR_FREQ 
calls to this function.  Then, the decoding routine (located in proto.c)
is called.  Finally, the routine that maintains the master list of MAC
addresses, add_arp_rec, is called to possibly add this packet's information
to main_clist.

Input:      str- some pcap identifier.  Should probably read the docs...
      hdr - pcap's packet header which contains some length info.
      pkt - The raw packet from the frame down to the last byte.

Output: Bytes read in the overall stats window can be updated.

***************************************************************************/

void callback(u_char *str, struct pcap_pkthdr *hdr, u_char *pkt)
{
   packet_parse packet_info;

   karpski_stats.nbytesread += hdr->len;
   byte_read_flag = ((byte_read_flag + 1) % UPDATE_BR_FREQ);
   if ((byte_read_flag==1) && (statswindow))
   {
      set_overall_update(64);
   }  
   
   decode_frame(pkt, &packet_info, hdr->len);
   add_arp_rec(&packet_info);
   free_frame(&packet_info);
   return;
}   

/************************************************************************

scr_update_func is called once every 10 microseconds by the Gtk timer routine.

************************************************************************/

int scr_update_func()
{
//   printf("Entering update screen thread...\n");
   update_main_list(main_clist, first_arp_rec);
//   printf("Updated main list..\n");
   update_overall_stats(statswindow);
//   printf("Updated overall stats..\n");
   update_info_stats(infowindow);
//   printf("Updated info stats..\n");
   draw_new_connections(selected_ar);
//   printf("drew new connections..\n");
   display_textitems();
//   printf("Ending update screen...\n");
   return TRUE;
}


/************************************************************************

Timeout_func is called once every n microseconds by the Gtk timer routine.
It reads up to MAX_PACKET_COUNT without updating the GUI.  Until this baby
gets threads, I don't see another way of doing this to make sure we drop as
few packets as possible!


Input: None
Returns: TRUE - meaning continue with the gtk timeout function calling
************************************************************************/

int timeout_func()
{
   do
   {
      pcap_loop(h_pcap, -1, (pcap_handler) callback, "btx");
   } while (1);
   
   return TRUE;
}

/* Lookup_func */

void lookup_func()
{
   t_arp_rec *t_ar;
   t_arp_rec *found;
   char *strptr;

   do
   {
      found = NULL;
      
      mutex_enter(ArpRecMutex);
      
      for (t_ar = first_arp_rec; ((t_ar) && (!found)); t_ar=t_ar->next)
         if ((!t_ar->machine_name[0]) && (t_ar->needs_update==0) && (t_ar->machine_addr[0]))
            found = t_ar;
            
      if (!found)
      {
         usleep(POST_NSLOOKUP_SLEEP);
         continue;
      }
      
      found->needs_update = 2;
      mutex_exit(ArpRecMutex);
      
      strptr = return_fqdn(found->sin, found->machine_addr);
      if (!strptr)
      {
         fprintf(stderr,"btx is dumb\n");
         exit(1);
      }

      mutex_enter(ArpRecMutex);
      if (strptr[0])
         strcpy(found->machine_name, strptr);
      else
         strcpy(found->machine_name, "N/A");
      
      set_info_update(2);
      set_info_update(4);
      if ((found->next) && (!strcmp(found->machine_addr, found->next->machine_addr)))
      {
         found->next->needs_update=2;
         strcpy(found->next->machine_name, found->machine_name);
      }
      mutex_exit(ArpRecMutex);
      
      usleep(POST_NSLOOKUP_SLEEP);
      
   } while(1);
}

/*
*********************************************************
start_capturing:

Updates the global stats box that it has started capturing
Changes the title of the main window to say that we are capturing
Connects to the name server (if we are resolving addrs)
Has pcap start capturing data
Adds the timeout function to the timeout loop with a minimal delay
If sound is configured, sets up the sound timeout

*********************************************************
*/
void start_capturing(GtkWidget *widget)
{
   char ebuf[128];
   pthread_attr_t attr;
   
   if (started)
      return;
      
   start_time = time(NULL);
   stop_time = (time_t) 0;

   set_overall_update(16);
   
   sprintf(ebuf, "Karpski %s - Capturing data", KARPSKI_VERSION);
   gtk_window_set_title(GTK_WINDOW(mainwindow), ebuf);
   
   if (resolve_addr)
      sethostent(1);
      
   h_pcap = pcap_open_live(device, 1500, 1, 0, ebuf);
   
   if (!h_pcap)
   {
      fprintf(stderr,"ERROR: %s\n", ebuf);
      exit(1);
   }
   
   pthread_attr_init(&attr);
   pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   
   if (pthread_create(&CaptureThread, &attr, (void *)timeout_func, NULL) != 0)
   {
      fprintf(stderr, "Error creating the capture thread!\n");
      exit(1);
   }

   if (resolve_addr)
   {
      if (pthread_create(&NSLookupThread, &attr, (void *)lookup_func, NULL) != 0)
      {
         fprintf(stderr, "Error creating the nslookup thread!\n");
         exit(1);
      }
   }
   scrupdatetag = gtk_timeout_add(DRAW_INTERVAL, scr_update_func, NULL);
#ifdef SCAN_SOUND_BROKEN
   soundplaytag = gtk_timeout_add(1000*SCAN_SOUND_INTERVAL, play_scan_sound, NULL); 
#endif   
   started = 1;
   return;
}

void stop_capturing(GtkWidget *widget)
{
   char buf[128];
   char strbuf[128];
   
   if (!started)
      return;
   if (resolve_addr)
      endhostent();      
      
   if (statswindow)
   {
      sprintf(buf,"%-20s %10s", "Capturing data:", "No");
      gtk_label_set(GTK_LABEL(label2), buf);   
   }
   sprintf(strbuf, "Karpski %s - Stopped capturing data", KARPSKI_VERSION);
   gtk_window_set_title(GTK_WINDOW(mainwindow), strbuf);
   pthread_cancel(CaptureThread);
   pthread_cancel(NSLookupThread);
   gtk_timeout_remove(scrupdatetag);
#ifdef SCAN_SOUND_BROKEN
   gtk_timeout_remove(soundplaytag);
#endif   
   gtk_timeout_remove(protocoltag);
   pcap_close(h_pcap);
   stop_time = time(NULL);
   karpski_stats.nseconds += (stop_time-start_time);
   started = 0;
   return;
}         


/*
*********************************************************
Initialize the global statistics structure, karpski_stats
*********************************************************

*/

void init_stats()
{
   karpski_stats.nhosts = 0;
   karpski_stats.nwarnings = 0;
   karpski_stats.nconnections = 0;
   karpski_stats.ndatagrams = 0;
   karpski_stats.nbytesread = 0;
   karpski_stats.nseconds = 0;
   karpski_stats.noverflows = 0;
}

void savequit()
{
   savelist();
   gtk_killyesno();
}

void karpski_quit()
{
   stop_capturing(NULL);
   if (stop_time == (time_t) 0)
      stop_time = time(NULL);
   karpski_stats.nseconds += (stop_time-start_time);
   if ((needssave) && (SAVE_WARNING))
   {
      sprintf(strbuf, "There are %d new addresses\nthat haven't been saved.\nSave them?", needssave);
      gtk_yesno(strbuf, "Save changes?", 1, (void *)savequit, (void *)gtk_main_quit);
   }
   else
      gtk_main_quit();
}

void make_stats_subtree(GtkWidget *rootitem, protolist *t_pl)
{
   GtkWidget *l3_items, *l3_tree;

   l3_tree = gtk_tree_new();
   gtk_widget_show(l3_tree);
   sprintf(strbuf, "%ld packets", t_pl->npackets);
   l3_items= gtk_tree_item_new_with_label(strbuf);
   gtk_tree_append(GTK_TREE(l3_tree), l3_items);
   gtk_widget_show(l3_items);
   sprintf(strbuf, "%ld bytes", t_pl->nbytes);
   l3_items= gtk_tree_item_new_with_label(strbuf);
   gtk_tree_append(GTK_TREE(l3_tree), l3_items);
   gtk_widget_show(l3_items);
   gtk_tree_item_set_subtree(GTK_TREE_ITEM(rootitem), l3_tree);
   
}

void dump_protolist(GtkWidget *tree, int fd, int screen)
{
   protolist *t_pl, *last_pl=NULL;
   long last_frametype = 0x1234123;
   GtkWidget *root_item, *item_new=NULL;
   GtkWidget *item_subtree=NULL, *cur_item;
   
   if (tree)
   {
      item_subtree = NULL;
      root_item = GTK_WIDGET(tree);
   }
   for (t_pl = first_protolist; (t_pl); t_pl = t_pl->next)
   {
      if (t_pl->frametype == last_frametype)
      {
         if (tree)
         {
            if (!item_subtree)
            {
               item_subtree = gtk_tree_new();
               gtk_widget_show(item_subtree);
            }
            
            cur_item = gtk_tree_item_new_with_label(t_pl->protoname);
            gtk_tree_append(GTK_TREE(item_subtree), cur_item);
            gtk_widget_show(cur_item);
            make_stats_subtree(cur_item, t_pl);
         }
         sprintf(strbuf, "     %-25s %-10ld packets (%ld bytes)\n", t_pl->protoname, t_pl->npackets, t_pl->nbytes);
      }
      else
      {
         if (tree)
         {
            if (item_subtree)
               gtk_tree_item_set_subtree(GTK_TREE_ITEM(item_new), item_subtree);
            else
            if (last_pl)
               make_stats_subtree(item_new, last_pl);
            item_subtree = NULL;
            
            item_new = gtk_tree_item_new_with_label(t_pl->protoname);
            gtk_tree_append(GTK_TREE(tree), item_new);
            gtk_widget_show(item_new);
         }
         last_frametype = t_pl->frametype;
         sprintf(strbuf, "%-30s %-10ld packets (%ld bytes)\n", t_pl->protoname, t_pl->npackets, t_pl->nbytes);
      }
      logmsg(NULL, fd, screen, strbuf);
      last_pl = t_pl;
   }
   if (tree)
   {
      if ((item_subtree) && (item_new))
         gtk_tree_item_set_subtree(GTK_TREE_ITEM(item_new), item_subtree);
      else
         if (last_pl)
            make_stats_subtree(item_new, last_pl);
   }
}
                                                                  
/*********************************************************

MAIN:

Here, we init gtk, pcap, set up the logfile, 
parse the protocols (proto.c)
Load the launch/smashy file (smashy.c)
Setup sound (if sound is setup, see karpski.h)
Make the main window
call the main gtk look (gtk_main)
Print closing statistics
exit

*********************************************************/
int main(int argc, char **argv)
{
   char ebuf[256];
   struct pcap_stat psts;
   char fn[256];
   char *cptr;
   
   gtk_set_locale();
   gtk_init(&argc, &argv);

   if (argc>1)
      strncpy(device,argv[1],IFNAMSIZ);
   else
   {
      cptr = pcap_lookupdev(ebuf);
      if (cptr == NULL)
      {
         fprintf(stderr,"Device error: %s\n", ebuf);
         exit(1);
      }
      
      strncpy(device, cptr, IFNAMSIZ);
   }
   
   sprintf(fn, "karpski-logfile.dat");
   if ((logfd = open(fn, O_WRONLY | O_CREAT | O_TRUNC))<0)
   {
      perror("open logfile.dat");
      exit(1);
   }
   
   if (!read_and_parse_protocols())
   {
      fprintf(stderr,"Error parsing protocols\n");
      exit(1);
   }
   read_vendors();
   
   if (!load_smashy_dat())
      can_smashy = 0;
   init_stats();
   pthread_mutex_init(&ArpRecMutex, NULL);
   pthread_mutex_init(&ConnectMutex, NULL);
   pthread_mutex_init(&TextItemMutex, NULL);
   
#ifdef SCAN_SOUND_BROKEN
   open_soundev();
#endif
   
   printf("Using device %s\n", device);
   
   sprintf(fn, "%skarpskirc", KARPSKI_DATA_DIR);
   gtk_rc_parse(fn);
   
   create_main_window();
   switchabout();
   
   gtk_main();
   
   if ((h_pcap) && (pcap_stats(h_pcap, &psts) == 0))
   {
      logmsg(NULL, logfd, 1, "Packets received: %d  Dropped: %d  Seconds: %d  Packets/sec: %4.2f\n", psts.ps_recv, psts.ps_drop, karpski_stats.nseconds,((float)psts.ps_recv / (float)karpski_stats.nseconds) );

      logmsg(NULL, logfd, 1, "\n");   
      logmsg(NULL, logfd, 1, "Stats:\n");
      logmsg(NULL, logfd, 1, "======\n");
      logmsg(NULL, logfd, 1, "Number hosts:       %d\n", karpski_stats.nhosts);
      logmsg(NULL, logfd, 1, "Number warnings:    %d\n", karpski_stats.nwarnings);
      logmsg(NULL, logfd, 1, "Number connections: %d\n", karpski_stats.nconnections);
      logmsg(NULL, logfd, 1, "Number datagrams:   %d\n", karpski_stats.ndatagrams);
      logmsg(NULL, logfd, 1, "Number bytes read:  %ld\n", karpski_stats.nbytesread);
      logmsg(NULL, logfd, 1, "\n");
      logmsg(NULL, logfd, 1, "Found ethernet protocols:\n\n");
      logmsg(NULL, logfd, 1, "Protocol name                           # packets      # bytes\n");
      logmsg(NULL, logfd, 1, "========================================================================\n");
      dump_protolist(NULL, logfd, 1);
      logmsg(NULL, logfd, 1, "========================================================================\n\n\n");
   }
   close(logfd);
   
   close_all_frame_captures();
#ifdef SCAN_SOUND_BROKEN   
   close(soundevfd);
#endif   
   printf("Adios...\n");
   return 0;
}

Generated by  Doxygen 1.6.0   Back to index