/* 
 * Copyright (C) 2002, 2003 Renaud Deraison
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Shares one /dev/bpf among multiple processes. 
 *
 *
 */ 
 

#include <includes.h>
#include <setjmp.h>

#undef DEBUG_FORWARD
#undef DEBUG
#undef DEBUG_HIGH
#define NUM_CLIENTS 128


#define BPF_SOCKET_PATH NESSUS_STATE_DIR"/nessus/bpf"



/*
 * Several platforms have a libpcap which do not implement a timeout
 * in pcap_next(). Therefore, we use a sigsetjmp + sigalarm trick to
 * make them do what we want.
 *
 * With this hack, Nessus can use the system libpcap, so we don't have
 * to maintain our own separate copy any more. Woo.
 *
 * XXXX TODO: all platforms should use the bpf server now, as there might
 * be SIGALRM conflicts.
 *
 */
 
static sigjmp_buf Jmp_buf;


static void hdl_sigalrm(int sig)
{
 signal(SIGALRM, SIG_IGN);
 siglongjmp(Jmp_buf, 1);
}

static void setalarm(struct timeval * tv)
{
 signal(SIGALRM, hdl_sigalrm);
#ifdef HAVE_SETITIMER
 {
  struct itimerval value;
  bzero(&value, sizeof(value));
  value.it_value.tv_sec = tv->tv_sec;
  value.it_value.tv_usec = tv->tv_usec;
  setitimer(ITIMER_REAL, &value, NULL);
 }
#else
 alarm(1);
#endif
}


static void unsetalarm()
{
 signal(SIGALRM, SIG_IGN);
#ifdef HAVE_SETITIMER
 {
  struct itimerval value, ovalue;
  getitimer(ITIMER_REAL, &value);
  value.it_value.tv_sec = 0;
  value.it_value.tv_usec = 0;
  setitimer(ITIMER_REAL, &value, &ovalue);
 }
#else
 alarm(0);
#endif
}

/*
 * The traditional pcap code is much more handy, so we'll try to use
 * it instead if our hack is not necessary
 */
#ifdef HAVE_DEV_BPFN 

#define CLNT_BUF_SIZ 1600


struct bpf_listener {
	int soc;
	char filter[512];
	char iface[48] ;
	struct bpf_program bpf_filter;
	};
	
struct bpf_pcap {
	pcap_t * pcap;
	char iface[48];
	struct bpf_program bpf_filter;
	struct bpf_pcap * next;
	};
	
struct bpf_client {
	int soc;
	int datalink;
	char packet[CLNT_BUF_SIZ];
	};
	

/* Server-side */
static struct bpf_listener clients[NUM_CLIENTS];
static struct bpf_pcap * pcaps;

/* Client-side */
static struct bpf_client clnts[NUM_CLIENTS];

static void sigterm()
{
 unlink(BPF_SOCKET_PATH);
 _EXIT(0);
}
#ifdef DEBUG
 void dump_packet(char * p, int len)
{
 int i;
 printf("\n----\n");
 for(i=0;i<len;i+=16)
 {
  int j;
  for(j=0;j<16;j++)
  {
   printf("%.2x ", (unsigned char)p[j+i]);
   if(j %8 == 0)printf(" ");
  }
  printf("\n");
 }
}
#endif

/*------------------------------------------------------------------------*
 *				Server part 				  *
 *------------------------------------------------------------------------*/
static struct bpf_pcap * add_pcap(char * iface, pcap_t * pcap)
{
 struct bpf_pcap * bpc;
 bpf_u_int32 netmask, network;
 
 if(pcap == NULL)
  return NULL;
 bpc = malloc(sizeof(*bpc));
 bpc->pcap = pcap;
 strncpy(bpc->iface, iface, sizeof(bpc->iface) - 1);
 bpc->iface[sizeof(bpc->iface) - 1] = '\0';
 pcap_lookupnet(iface, &network, &netmask, 0);
 pcap_compile(pcap, &bpc->bpf_filter, "", 0, netmask); 
 bpc->next = pcaps;
 pcaps = bpc;
 return bpc;
}



static pcap_t * get_pcap(char * iface)
{
 struct bpf_pcap * bpc = pcaps;
 while( bpc != NULL )
 {
  if(strcmp(bpc->iface, iface) == 0)
   return bpc->pcap;
  bpc = bpc->next;
 }
 return NULL;
}

static pcap_t * new_pcap(char * iface)
{
 char errbuf[PCAP_ERRBUF_SIZE];
 pcap_t *  pcap = pcap_open_live(iface, 1500, 0, 100, errbuf);
 if(pcap == NULL)
  {
   fprintf(stderr, "new_pcap(%s) failed - %s\n", iface, errbuf);
   return NULL;
  }
 return pcap;
}

/*
 * Each call to bpf_filter() applies the filter in the bpf, so
 * we need to "reset" it after each call to process()
 */
static void pcap_reset_filter(struct bpf_pcap * p)
{
  char bogus[16];
  struct bpf_program copy;
  
  bzero(bogus, sizeof(bogus));
  bcopy(&p->bpf_filter, &copy, sizeof(copy));
  (void)bpf_filter(p->bpf_filter.bf_insns, bogus, 16, 16);
  p = p->next;
}



static pcap_t * bpf_add_pcap(char * iface)
{
 pcap_t * ret = get_pcap(iface);
 if(ret != NULL) 
  return ret;
 else 
   add_pcap(iface, new_pcap(iface));
   
 return get_pcap(iface);
}

 



static int bpf_recv_line(int soc, char * buf, int len)
{
 int r = 0;
 bzero(buf, len);
 for(;r<len;)
 {
 int e = recv(soc, &(buf[r]), 1, 0);
 if(e <= 0) return e;
 r ++;
 if(buf[r-1] == '\n'){
	return r;
	}
 }
}

static int process(char * iface, u_char * p, int len)
{
 int i;
#ifdef DEBUG_HIGH
 printf("bpf_share: process()\n");
#endif 
 for(i=0;i<NUM_CLIENTS;i++)
 {
#ifdef DEBUG_HIGH 
if(clients[i].soc != 0)
  printf("%d) %d, %s %s %s\n", i, clients[i].soc, clients[i].iface, iface, clients[i].filter);
#endif  
  if(clients[i].soc > 0 && (strcmp(clients[i].iface, iface) == 0))
  {
   if(clients[i].filter[0] == '\0' || bpf_filter(clients[i].bpf_filter.bf_insns, p, len, len) != 0)
    {
    int e;
    int n;
    int lim;
    
#ifdef DEBUG_FORWARD 
    printf("Found packet that matches %s %d\n", clients[i].filter, i);
#endif        
#ifdef DEBUG    
    printf("bpf_share: forward data to %d\n", i);
#endif    
    n = 0;
    while( n != sizeof(len))
    {
    e = send(clients[i].soc, (char*)(&len)+n, sizeof(len)-n, 0);
    if(e <= 0)
    {
     if(errno != EPIPE)perror("bpf_share.c:process():send ");
     close(clients[i].soc);
     bzero(&clients[i], sizeof(clients[i]));
     goto endfor;
    }
    else n += e;
    }
    
    n = 0;
    
    if(len > CLNT_BUF_SIZ)
    	lim = CLNT_BUF_SIZ;
    else
        lim = len;
    
    
    while(n != lim)
    {
    e =  send(clients[i].soc, (char*)p + n, lim - n, 0);
    if(e <= 0)
     {
     if(errno != EPIPE)perror("bpf_share.c:process():send ");
     close(clients[i].soc);
     bzero(&clients[i], sizeof(clients[i]));
     goto endfor;
     }
    else n+=e;
    }
#ifdef DEBUG_FORWARD    
     printf("====>PACKET FORWARDED TO %d->%d\n", i, clients[i].soc);
#endif     
  endfor:
     ;
    }
  }
 }
}


static int mklistener()
{
 struct sockaddr_un addr;
 char name[] =  BPF_SOCKET_PATH;
 int soc;
 int one = 1;
 struct stat st;
 
 if(stat(name, &st) == 0)
 {
  unlink(name);
 }
 
 
 soc = socket(AF_UNIX, SOCK_STREAM, 0);
 if(soc < 0)
  return -1;
 
 bzero(&addr, sizeof(addr));
 addr.sun_family = AF_UNIX;
 bcopy(name, addr.sun_path, strlen(name));
 setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
 if(bind(soc, (struct sockaddr*)(&addr), sizeof(addr)) == -1)
 {
  perror("bpf_share.c:mklistener():bind ");
 }
 chmod(name, 0700);
 if(listen(soc, NUM_CLIENTS - 1) < 0)
  perror("bpf_share.c:mklistener():listen ");
 return soc;
}

static int add_client(int soc)
{
 int i;
 for(i=0;i<NUM_CLIENTS && clients[i].soc;i++);
 if(clients[i].soc)
 {
  /* Too many clients already */
  close(soc);
  return -1;
 }
 
 clients[i].soc = soc;
 clients[i].filter[0] = '\0';
 clients[i].iface[0] = '\0';
#ifdef DEBUG 
 printf("Added client at index %d (%d)\n", i, clients[i].soc);
#endif
 return i;
}


static int read_clients()
{
 fd_set rd;
 int i;
 char buf[512];
 int max = -1;
 struct timeval tv = {0, 0};
 
 FD_ZERO(&rd);
 for(i=0;i<NUM_CLIENTS;i++)
 {
  if(clients[i].soc > 0)
  {
   FD_SET(clients[i].soc, &rd);
   if(clients[i].soc > max) max = clients[i].soc;
  }
 }
 
 if(max == -1)
 {
  usleep(5000);
  return 0;
 }
 
 if(select(max+1, &rd, NULL, NULL, &tv) > 0)
 {
  for(i=0;i<NUM_CLIENTS;i++)
  {
   if(clients[i].soc && FD_ISSET(clients[i].soc, &rd))
   {
    int n;
    
    n = bpf_recv_line(clients[i].soc, buf, sizeof(buf));
#ifdef DEBUG
    printf("Received %s\n", buf);
#endif    
    if(n <= 0)
    {
#ifdef DEBUG_FORWARD
     printf("Connection closed for %d\n", i);
#endif     
     /* connection was closed */
     close(clients[i].soc);
     bzero(&clients[i], sizeof(clients[i]));
    }
    else
    {
     if(clients[i].iface[0] == '\0')
     { 
       int dl; 
       pcap_t * pcap;
       bpf_u_int32 netmask, network;
       buf[strlen(buf) - 1 ] = '\0';
       clients[i].iface[sizeof(clients[i].iface) - 1] = '\0';
       strncpy(clients[i].iface, buf, sizeof(clients[i].iface) - 1);
       send(clients[i].soc, ".", 1, 0);
       
       
       pcap = bpf_add_pcap(clients[i].iface);
       
     
       if(pcap != NULL)
       {
       dl = htonl(pcap_datalink(pcap));
       send(clients[i].soc, &dl, sizeof(dl), 0);
       n = bpf_recv_line(clients[i].soc, buf, sizeof(buf));
       buf[strlen(buf) - 1 ] = '\0';
       clients[i].filter[sizeof(clients[i].filter) - 1] = '\0';
       strncpy(clients[i].filter, buf, sizeof(clients[i].filter) - 1);
#ifdef DEBUG       
       printf("FILTER = %s (%s) %d\n", buf, clients[i].filter, i);
#endif       
       pcap_lookupnet(clients[i].iface, &network, &netmask, 0);
     /*  pcap_restart(NULL);    */    
       pcap_compile(pcap, &clients[i].bpf_filter, buf, 0, netmask);
       pcap_reset_filter(get_pcap(clients[i].iface));
       send(clients[i].soc, ",", 1, 0);
       }
       else send(clients[i].soc, "e", 1, 0);
     }
    }
   }
  }
 }
 return 0;
}


static int add_clients(int soc)
{
 fd_set rd;
 struct timeval tv = {0,10};
 int clnt;
 
 FD_ZERO(&rd);
 FD_SET(soc, &rd);
 if(select(soc+1, &rd, NULL, NULL, &tv) > 0)
 {
  struct sockaddr_un soca;
  int len = sizeof(soca);
  clnt = accept(soc, (struct sockaddr*)&soca,&len); 
  if(clnt > 0)
  {
#ifdef DEBUG_FORWARD
   printf("New client!\n");
#endif     
   add_client(clnt);
   }
 }
 return 0;
}


static int pcaps_read()
{
 struct bpf_pcap * bpc = pcaps;
 while(bpc)
 {
  u_char * p;
  struct pcap_pkthdr head;

  if(sigsetjmp(Jmp_buf, 1) == 0)
   {
    struct timeval tv = {0, 100};
    setalarm(&tv);
    p = (u_char*)pcap_next(bpc->pcap, &head);
   }
  else
   p = NULL;
   
  unsetalarm();
  
  if(p != NULL)process(bpc->iface, p, head.caplen);
  pcap_reset_filter(bpc);
  
  bpc = bpc->next;
 }
 return 0;
}


int bpf_server()
{
 int i;
 int lst;
 int pid;
 
 for(i=0;i<NUM_CLIENTS;i++)bzero(&clients[i], sizeof(clients[i]));
 
 if((pid = fork()) == 0)
 {
  signal(SIGPIPE, SIG_IGN);
  signal(SIGHUP, SIG_IGN);
  signal(SIGTERM, sigterm);
  setproctitle("bpf server");
  lst = mklistener();
  for(;;)
  {
   add_clients(lst);
   read_clients();
   pcaps_read();
   }
  }
 return pid;
}


/*--------------------------------------------------------------------------*
 *			Client part 					    *
 *--------------------------------------------------------------------------*/
 


static int bpf_set_iface(int soc, char * iface)
{ 
 char buf[2]  = {0, 0};
 char snd[256];
 snprintf(snd, sizeof(snd), "%s\n", iface);
 send(soc, snd, strlen(snd), 0);
 recv(soc, buf, 1, 0);
 if(buf[0] == '.')
  {
  int dl;
  recv(soc, &dl, sizeof(dl), 0);
  dl = ntohl(dl);
  return dl;
  }
 else
  return -1;
}

static int bpf_set_filter(int soc, char * filter)
{
  char buf[2]  = {0, 0};
  char snd[1024];
  snprintf(snd, sizeof(snd), "%s\n", filter);
  send(soc, snd, strlen(snd), 0);
  recv(soc, buf, 1, 0);
  if(buf[0] == ',')
   return 0;
  else
   return -1;
}


 
 
int bpf_open_live(char * iface, char * filter)
{
 int soc;
 struct sockaddr_un addr;
 int len = sizeof(addr);
 char name[] = BPF_SOCKET_PATH;
 char errbuf[PCAP_ERRBUF_SIZE];
 
 int i;
 
 for(i=0;i<NUM_CLIENTS && clnts[i].soc != 0;i++);
 
 if(clnts[i].soc != 0)
  return -1;
  
 
 if(iface == NULL)
  iface = pcap_lookupdev(errbuf); 
  
 
 soc = socket(AF_UNIX, SOCK_STREAM, 0);
  if(soc < 0)
  {
   perror("bpf_open_live():socket ");
   return -1;
  } 
 bzero(&addr, sizeof(addr));
 addr.sun_family = AF_UNIX;
 bcopy(name, addr.sun_path, strlen(name));
 

 if(connect(soc, (struct sockaddr*)&addr, len) ==  -1 )
 { 
  perror("bpf_open_live():connect ");
  close(soc);
  return -1;
 }
 
 clnts[i].datalink = bpf_set_iface(soc, iface);
 bpf_set_filter(soc, filter);
 clnts[i].soc = soc;
 
 return i;
}


ExtFunc u_char* bpf_next_tv(int clnt, int * caplen, struct timeval * tv)
{
 fd_set rd;
 int soc = clnts[clnt].soc;
 int lim;
 struct timeval tmp;
 
 tmp = *tv;
 
 FD_ZERO(&rd);
 FD_SET(soc, &rd);
#ifdef DEBUG 
 printf("(%d) bpf_next\n", getpid());
#endif 
 if(select(soc+1, &rd, NULL, NULL, &tmp) > 0 && FD_ISSET(soc, &rd) != 0)
 {
  int n = 0;
  bzero(clnts[clnt].packet, sizeof(clnts[clnt].packet));
#ifdef DEBUG  
  printf("(%d) Select(%d->%d)\n", getpid(), clnt, soc);
#endif  
  while(n != sizeof(*caplen))
  {
  char * x = (char*)caplen;
  int e;
  e = recv(soc, x+n, sizeof(*caplen)-n, 0);
  if( e <= 0 )
   {
   perror("bpf_next():recv ");
   return NULL;
   }
  else
   n += e;
  }
  
#ifdef DEBUG  
  printf("(%d) HEAD received (%d->%d)\n", getpid(), clnt, soc);
#endif  
  n = 0;
  
 
  if( *caplen > CLNT_BUF_SIZ)
   lim = CLNT_BUF_SIZ;
  else
   lim = *caplen;
    
   
   
  while(n != lim)
  {
  int e;
  e = recv(soc, &(clnts[clnt].packet[n]), lim-n, 0);
  if(e <= 0 ) 
   return NULL;
  else
   n+=e;
  }
#ifdef DEBUG
  dump_packet(clnts[clnt].packet, lim);
#endif  
  return clnts[clnt].packet;
 }
 return NULL;
}


ExtFunc u_char* bpf_next(int clnt, int * caplen)
{
 struct timeval tv = {0, 100000};
 return bpf_next_tv(clnt, caplen, &tv);
}

int bpf_datalink(int bpf)
{
 return clnts[bpf].datalink;
}



void bpf_close(int bpf)
{
 close(clnts[bpf].soc);
 clnts[bpf].soc = clnts[bpf].datalink = 0;
}

#else

static pcap_t * pcaps[NUM_CLIENTS];

	

int bpf_open_live(char * iface, char * filter)
{
 char errbuf[PCAP_ERRBUF_SIZE];
 pcap_t * ret;
 bpf_u_int32 netmask, network;
 struct bpf_program filter_prog;
 int i;
 
 for(i=0;i< NUM_CLIENTS && pcaps[i];i++);

 if(pcaps[i])
 {
  printf("no free pcap\n");
  return -1;
 }
  
 
 if(iface == NULL)
  iface = pcap_lookupdev(errbuf);
 
 ret = pcap_open_live(iface, 1500, 0, 100, errbuf);
 if(ret == NULL)
 {
    printf("%s\n", errbuf);	 
  return -1;
 }

 if(pcap_lookupnet(iface, &network, &netmask, 0) < 0)
 { 
   printf("pcap_lookupnet failed\n");
   pcap_close(ret);
   return -1;
 }
 
 if(pcap_compile(ret, &filter_prog, filter, 0, netmask) < 0)
 {
  pcap_perror(ret, "pcap_compile");
  pcap_close(ret);
  return -1;
 }
 
 if(pcap_setfilter(ret, &filter_prog) < 0) 
 {
  pcap_perror(ret, "pcap_setfilter\n");
  pcap_close(ret);
  return -1;
 }
 pcaps[i] = ret;
 return i;
}



u_char* bpf_next_tv(int bpf, int * caplen, struct timeval * tv)
{
  u_char * p = NULL;
  struct pcap_pkthdr head;

  if(sigsetjmp(Jmp_buf, 1) == 0)
   {
   setalarm(tv); 
   p = (u_char*)pcap_next(pcaps[bpf], &head);
   }
  else 
   p = NULL;
 
 unsetalarm(); 
 *caplen  = head.caplen;
 return p;
}


u_char* bpf_next(int bpf, int * caplen)
{
 struct timeval tv = {0, 100000};
 
 return bpf_next_tv(bpf, caplen, &tv);
}


int bpf_datalink(int bpf)
{
 return pcap_datalink(pcaps[bpf]);
}


void bpf_close(int bpf)
{
 pcap_close(pcaps[bpf]);
 pcaps[bpf] = NULL;
}


int bpf_server()
{
  return 0;
}
#endif /* HAVE_DEV_BPFN */



#undef __STANDALONE__
#ifdef __STANDALONE__
/*
 * This code tests our bpf sharer
 */
int main()
{
 int bpf1, bpf2;
 bpf_server();
 sleep(1);
 
 bpf1 = bpf_open_live("rl0", "tcp");
 if(bpf1 < 0)
 {
  printf("COULD NOT OPEN BPF1!\n");
 }
 bpf2 = bpf_open_live("rl0", "icmp");
 if(bpf2 < 0)
 {
  printf("COULD NOT OPEN BPF2!\n");
 }
 
 for(;;)
 {
  char * p;
  int len;
  
  p = bpf_next(bpf1, &len);
  if(p)printf("bpf1 -> data\n");
  p = bpf_next(bpf2, &len);
  if(p)printf("bpf2 -> data\n");

 }
 
 
}
#endif
