/* angst - pcap.c
 * by Patroklos Argyroudis <argp@bsd.gr>
 *
 * The libpcap callback function and libpcap initialization.
 *
 * $Id: pcap.c,v 1.3 2001/02/04 03:38:43 argp Exp $
 */ 

#include "angst.h"

void
dissect(char *user, struct pcap_pkthdr *pkthdr, u_char *pkt)
{
 struct in_addr shost, dhost;		/* source and destination hosts */
 u_short sport, dport;			/* source and destination port */
 struct libnet_ip_hdr *ip;		/* IP protocol header */
 struct libnet_tcp_hdr *tcp;		/* TCP protocol header */
 struct libnet_ethernet_hdr *eth;	/* ethernet header */
 struct libnet_arp_hdr *arph;		/* ARP protocol header */ 
 u_char *data = NULL;			/* packet's payload */
 u_int datalen = 0;			/* payload's length */
 u_int iphlen = 0;			/* IP header length */
 u_int iptlen = 0;			/* IP total length */
 u_int tcphlen = 0;			/* TCP header length */
 u_int buflen = 0;			/* buffer's actual length */
 int i, j = 0;				/* counters */
 u_char temp[SNAPLEN];			/* temporary buffer */
 u_int templen = 0;			/* temporary buffer's length */
 Host *current = NULL;
 int find_flag = 0;

 /* first of all deal with ARP requests */
 if(arp_flag == 1)
 {
  eth = (struct libnet_ethernet_hdr *)pkt;
  arph = (struct libnet_arp_hdr *)(pkt + LIBNET_ETH_H);
 
  eth->ether_type = ntohs(eth->ether_type);
  arph->ar_op = ntohs(arph->ar_op);
 
  /* check if it is an ARP request */
  if((eth->ether_type == ETHERTYPE_ARP) && (arph->ar_op == ARPOP_REQUEST))
  {
   /* check if the host already exists in the linked list */
   for(current = head; current; current = current->next)
   {
    if((memcmp(arph->ar_tpa, current->tpa, 4)) == 0)
     find_flag = 1;
   }
   
   if(find_flag == 0)  
    add_host(arph->ar_tpa, arph->ar_sha, arph->ar_spa);
  }
 }
 
 /* now deal with TCP packets */
 if(pkthdr->caplen < LIBNET_IP_H)
  return; /* received truncated IP packet */ 

 ip = (struct libnet_ip_hdr *)(pkt + hdrlen);
 tcp = (struct libnet_tcp_hdr *)(pkt + hdrlen + LIBNET_IP_H);

 if(ip->ip_p == IPPROTO_TCP)
 {
  shost = ip->ip_src;
  dhost = ip->ip_dst;
  iptlen = ntohs(ip->ip_len);

  /* discard fragments */
  if(ntohs(ip->ip_off) & 0x1fff)
   return; /* throwing away fragments */

  iphlen = ip->ip_hl * 4;
  data = pkt + hdrlen + iphlen;

  if(iphlen > iptlen)
   return; /* IP packet received truncated */
  
  datalen = (iptlen - iphlen);
  if(datalen < LIBNET_TCP_H)
   return; /* received truncated TCP packet */

  tcphlen = tcp->th_off * 4;

  sport = ntohs(tcp->th_sport);
  dport = ntohs(tcp->th_dport);
  data += tcphlen;
  datalen -= tcphlen;

  init_buf(temp, sizeof(temp));
  for(i = 0; i <= datalen; i++)
  {
   if(isprint(data[i]))
   {
    temp[j] = data[i];
    j++;
   }
   else if((data[i] == '\r') || (data[i] == '\n'))
   {
    temp[j] = '\n';
    j++;
   }
  }

  templen = strlen(temp);
  strlcat(buffer, temp, sizeof(buffer));
  buflen = strlen(buffer);

  if((tcp->th_flags & TH_FIN) || (tcp->th_flags & TH_RST) || (buflen >= BUFSIZE))
  {
   /* dump everything to the log file when the connection
    * is terminated, or when the maximum number of captured 
    * bytes per connection is reached
    */
   fprintf(fp, "\n[ %s : %u  -> ", libnet_host_lookup(shost.s_addr, resolve_flag), sport);
   fprintf(fp, "%s : %u ]\n\n", libnet_host_lookup(dhost.s_addr, resolve_flag), dport);
   fwrite(buffer, buflen, 1, fp);
   fprintf(fp, "\n");
   fflush(fp);
   init_buf(buffer, sizeof(buffer));
  }
 }
}

int
find_header_length(int d)
{
#ifdef DEBUG
 printf("datalink: ");
#endif /* DEBUG */
 switch(d)
 {
#ifdef DLT_LOOP
  case DLT_LOOP:
#endif /* DLT_LOOP */
  case DLT_NULL:
#ifdef DEBUG
	printf("no encapsulation, loopback\n");
#endif /* DEBUG */
	return(4);		/* loopback header 4 bytes */
	break;
  case DLT_EN10MB:
#ifdef DEBUG
	printf("ethernet 10mb\n");
#endif /* DEBUG */
	return(14);		/* ethernet 10mb header 14 bytes */
	break;
  case DLT_FDDI:
#ifdef DEBUG
	printf("fddi\n");
#endif /* DEBUG */
	return(21);		/* fddi header 21 bytes */
	break;
  case DLT_PPP:
#ifdef DEBUG
	printf("point-to-point protocol\n");
#endif /* DEBUG */
	return(0);		/* ppp header 0 or 24 bytes? */
	break;
  default:
#ifdef DEBUG
	printf("not found, assuming ethernet 10mb\n");
#endif /* DEBUG */
	return(14);		/* ethernet 10mb header 14 bytes */
	break;
 }
}

/* EOF */
