/*
** xipcap.c for  in 
** 
** Made by 
** Login   <vianney@epita.fr>
** 
** Started on  Wed Sep  1 08:27:25 1999 
** Last update Fri Oct 29 00:45:06 1999 
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <pcap.h>
#include <X11/Intrinsic.h>
#include "xip.h"
#include "xippkt.h"
#include "XmgFancy.h"
#include "pkt.h"
#include "pat_ether.h"
#include "pat_tr.h"
#include "pat_null.h"
#include "pat_data.h"

extern XtAppContext		app_context;
extern Widget			toplevel;
extern Widget			pktbox;
extern t_boolean		capflow;
extern t_boolean		big_packets;
extern t_boolean		pcap_fork;
extern char			*pcap_dev;
extern char			hard_filter[];
extern int			pcap_snaplen;
extern t_boolean		pcap_promisc;
extern int			pcap_to_ms;
extern t_boolean		pcap_optimize;
extern char			*pcap_fname;
extern int			pcap_cnt;
extern t_boolean		verbose;

pcap_t				*pcap;
int				packets_treated = 0;
int				packets_dropped = 0;
pid_t				pcap_pid;

/* compiles the bpf program and sets it to the main pcap structure. 
   It exits(2) if something goes wrong. */
VOID_FUNC			set_hard_filter(pcap,hard_filter)
pcap_t				*pcap;
char				*hard_filter;
{
  struct bpf_program		bp;

  if (pcap_compile(pcap,
                   &bp,
                   hard_filter,
                   pcap_optimize,
                   0) < 0)
    {
      fprintf(stderr,"problems in pcap_compile: %s\n",hard_filter);
      exit(1);
    }
  if (pcap_setfilter(pcap,&bp) < 0)
    {
      fprintf(stderr,"can't set pcap_setfilter\n");
      exit(1);
    }
}

struct				s_singleton
{
  struct pcap_pkthdr		*pp;
  char				*buf;
};

/* transforms a pcap "link type" to a "lay" msg_proc.
   Returns 0 if OK, ERR_XIP_NO_ENT if no equivalent is found. */
t_status			pat_get_from_pcap(pcap_link_type,pat)
int				pcap_link_type;
t_pat				**pat;
{
  switch (pcap_link_type)
    {
    case DLT_EN10MB:
      (*pat) = &ether_pat;
      return (0);
    case DLT_NULL:
      (*pat) = &null_pat;
      return (0);
    case DLT_IEEE802:
      (*pat) = &tr_pat;
      return (0);
    }
  return (ERR_XIP_NO_ENT);
}

/* fills out a "pkt" structure against pcap information.
   then create graphical packet. It uses pat_get_from_pcap(3).
   Returns 0 if OK, Might return various errors. */
VOID_FUNC			make_packet_from_pp_and_buf(pp,buf)
struct pcap_pkthdr		*pp;
char				*buf;
{
  t_status			status;
  t_pkt				pkt;
  t_pkt				*npkt;

  if (capflow)
    {
      if ((status = pat_get_from_pcap(pcap_datalink(pcap),
				      &pkt.pat)) != 0)
	pkt.pat = &data_pat;
      pkt.buf = buf;
      pkt.len = pp->caplen;
      pkt.netlen = pp->len;
      FBCOPY(&pp->ts,&pkt.timestamp,sizeof (struct timeval));
      if (verbose)
	{
	  char			text[STR_BUFSIZ];

	  text[0] = 0;
	  if ((status = xip_shortcuts_html_do(&pkt,
					      text,
					      sizeof (text))) != 0)
	    ;
	  fprintf(stderr,"%s\n",text);
	}
      if ((npkt = pkt_dup(&pkt,
			  XIP_ALLOC_PROC,
			  XIP_FREE_PROC,
			  &status)) == NULL)
	{
	  XmgErrPrint(toplevel,status,"pkt_dup");
	  return ;
	}
      if (big_packets)
	XipCreatePkt(toplevel,
		     npkt,
		     TRUE);
      else
	XipCreateSmallPkt(npkt,
			  TRUE);
      packets_treated++;
    }
  else
    {
      packets_dropped++;
    }
}

/* is a pcap_handler. 
   calls make_packet_from_pp_and_buf(3). */
VOID_FUNC			read_pcap_handler(s,pp,buf)
struct s_singleton		*s;
struct pcap_pkthdr		*pp;
char				*buf;
{
  make_packet_from_pp_and_buf(pp,buf);
}

/* is a XtInputCallbackProc.
   It reads pcap file descriptor. */
VOID_FUNC			read_pcap(closure,fd,id)
XtPointer			closure;
int				*fd;
XtInputId			*id;
{
  struct s_singleton		s;
  
  if (pcap_read(pcap,
		1,
		(pcap_handler)read_pcap_handler,
		(u_char *)(&s)) < 0)
    {
      if (verbose)
	fprintf(stderr,"bad read\n");
    }
}

/* is a XtInputCallbackProc.
   It reads pipe file descriptor. */
VOID_FUNC			read_pipe(closure,fd,id)
XtPointer			closure;
int				*fd;
XtInputId			*id;
{
  int				cc;
  struct pcap_pkthdr		pp;
  char				buf[BUFSIZ];
  
  if ((cc = read(*fd,&pp,sizeof (pp))) < 0)
    {
      err_print(ERR_XIP_READ,"reading pp");
      return ;
    }
  assert(pp.len < sizeof (buf));
  if ((cc = read(*fd,buf,pp.len)) < 0)
    {
      err_print(ERR_XIP_READ,"reading buf");
      return ;
    }
  make_packet_from_pp_and_buf(&pp,buf);
}

/* initializes the pcap part of xip.
   opens pcap, possibly opening "offline" if pcap_fname is not NULL.
   If pcap_dev is NULL, it performs pcap_lookupdev(3).
   It needs "app_context" to be initialized since it uses XtAppAddInput(3).
   It pcap_fname is not NULL, it loops on the file. */
t_status			xip_cap_init(VOID_DECL)
{
  char				errbuf[BUFSIZ];

  if (pcap_fname)
    {
      if ((pcap = pcap_open_offline(pcap_fname,
				    errbuf)) == NULL)
	{
	  fprintf(stderr,"pcap_open_offline: %s\n",errbuf);
	  exit(1);
	}
    }
  else
    {
      if (!pcap_dev)
	if ((pcap_dev = pcap_lookupdev(errbuf)) == NULL)
	  {
	    fprintf(stderr,"%s\n",errbuf);
	    exit(1);
	  }
      if ((pcap = pcap_open_live(pcap_dev,
				 pcap_snaplen,
				 pcap_promisc,
				 pcap_to_ms,
				 errbuf)) == NULL)
	{
	  fprintf(stderr,"pcap_open_live: %s\n",errbuf);
	  exit(1);
	}
    }
  set_hard_filter(pcap,hard_filter);
  if (pcap_fname)
    {
      pcap_loop(pcap,
		pcap_cnt,
		(pcap_handler)read_pcap_handler,
		NULL);
    }
  else
    {
      if (pcap_fork)
	{
	  int		p[2];

	  if (pipe(p) < 0)
	    {
	      err_print(ERR_XIP_SYSCALL,"pipe");
	      exit(1);
	    }
	  if ((pcap_pid = fork()) < 0)
	    {
	      err_print(ERR_XIP_SYSCALL,"fork");
	      exit(1);
	    }
	  if (pcap_pid)
	    {
	      close(p[1]);
	      XtAppAddInput(app_context,
			    p[0],
			    (XtPointer)XtInputReadMask,
			    read_pipe,
			    NULL);
	    }
	  else
	    {
	      close(p[0]);
	      while (1)
		{
		  struct pcap_pkthdr	pp;
		  char			*buf;

		  if (buf = (char *)pcap_next(pcap,&pp))
		    {
		      struct iovec	iov[2];
		      
		      iov[0].iov_base = (caddr_t)(&pp);
		      iov[0].iov_len = sizeof (pp);
		      iov[1].iov_base = buf;
		      iov[1].iov_len = pp.len;
		      if (writev(p[1],iov,2) < 0)
			{
			  err_print(ERR_XIP_WRITE,"writev");
			}
		    }
		}
	    }
	}
      else
	{
#if defined(__NetBSD__) || defined(__FreeBSD__) || defined (__OpenBSD__)
	  {
	    int		x;

	    x = 1;
	    if (ioctl(pcap_fileno(pcap),BIOCIMMEDIATE,&x) < 0)
	      perror("ioctl BIOCIMMEDIATE");
	  }
#endif
	  XtAppAddInput(app_context,
			pcap_fileno(pcap),
			(XtPointer)XtInputReadMask,
			read_pcap,
			NULL);
	}
    }
  return (0);
}
