/* This file is part of Lcrzo.
*/

/*-------------------------------------------------------------*/
#if LCRZODEF_LIBPCAPINSTALLED==1
int lcrzo_sniff_init(const lcrzo_device device,
		     lcrzo_int32 nbmaxoct,
		     lcrzo_int16 snifftype,
		     const char *filtre, 
		     lcrzo_sniff *psniff)
{ int retour;
  bpf_u_int32 localnet, netmask;
  struct bpf_program fcode;
  lcrzo_device dev;
  lcrzo_string filter2;
  lcrzo_bool usefilter;
  lcrzo_string errstr;

  /*parameters verification*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  lcrzo_device_verifbof(device);
  if (psniff==NULL) return(LCRZO_ERR_PANULLPTR);
  if (nbmaxoct<=0) return(LCRZO_ERR_PATOOLOW);
  switch(snifftype)
  { case LCRZO_SNIFF_TYPE_NORMAL :
    case LCRZO_SNIFF_TYPE_IPREAS :
    case LCRZO_SNIFF_TYPE_TCPREOR :
      break;
    default:
      return(lcrzo_ieuv("lcrzo_sniff_init", "snifftype"));
  }

  /* to store error messages */
  lcrzo_er(lcrzo_string_alloc(PCAP_ERRBUF_SIZE, &errstr));

  /* init */
#if defined LCRZODEF_SYSTEM_Unix
  lcrzo_er(lcrzo_device_init_noalias(device, dev));
  psniff->ppcapt = pcap_open_live(dev, nbmaxoct+sizeof(lcrzo_hdrpeth), 1, 
          500, errstr);
#elif defined LCRZODEF_SYSTEM_Windows
  /* because pcap_getevent() does not work, so we can't wait in 
     lcrzo_priv_wait_wait_sniff. So the trick is to set a one timeout. */
  { lcrzo_device devtmp;
    lcrzo_er(lcrzo_priv_ascii_init_device(device, devtmp));
    lcrzo_er(lcrzo_device_init_noalias(devtmp, dev));
  }
  psniff->ppcapt = lcrzo_priv_cs_pcap_open_live(dev, 
          nbmaxoct+sizeof(lcrzo_hdrpeth), 1, 1, errstr);
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif          
  
  if ( psniff->ppcapt == NULL )
  { lcrzo_er(lcrzo_priv_global_set_errmsgsys(errstr));
    lcrzo_er(lcrzo_string_free(errstr));
    return(LCRZO_ERR_FUPCAPOPEN);
  }

  /* get the netmask */
  retour=pcap_lookupnet(dev, &localnet, &netmask, errstr);
  if ( retour ) 
  { lcrzo_er(lcrzo_priv_global_set_errmsgsys(errstr));
    lcrzo_er(lcrzo_string_free(errstr));
    return(LCRZO_ERR_FUPCAPLOOKUPNET);
  }
  lcrzo_er(lcrzo_string_free(errstr));
  
  filter2=NULL; /* just for the compiler warning */
  if ( filtre==NULL )
  { 
#if LCRZODEF_LIBPCAPFROMREDHAT==1
    /* We always use a filter. We should not need it, but
       the libpcap package modified by RedHat requires it... */
    lcrzo_er(lcrzo_string_initm_text("", &filter2));
    usefilter=LCRZO_TRUE;
#else
    usefilter=LCRZO_FALSE;
#endif
  }
  else
  { lcrzo_er(lcrzo_string_initm_coretext(filtre, &filter2));
    usefilter=LCRZO_TRUE;
  }

  if (usefilter)
  { retour=pcap_compile(psniff->ppcapt, &fcode, filter2, 1, netmask);
    lcrzo_string_free(filter2);
    if ( retour )
    { lcrzo_priv_global_set_errmsglcrzo_filter(filtre);
      lcrzo_er(lcrzo_priv_global_set_errmsgsys(pcap_geterr(psniff->ppcapt)));
      return(LCRZO_ERR_FUPCAPCOMPILE);
    }
    retour=pcap_setfilter(psniff->ppcapt, &fcode);
    if ( retour )
    { lcrzo_priv_global_set_errmsglcrzo_filter(filtre);
      lcrzo_er(lcrzo_priv_global_set_errmsgsys(pcap_geterr(psniff->ppcapt)));
      return(LCRZO_ERR_FUPCAPSETFILTER);
    }
#if LCRZODEF_HAVEFONC_PCAP_FREECODE==1
    /* this function only exists in libpcap > 0.6 */
    pcap_freecode(&fcode);
#endif
  }

  /* get pcapfd */
  psniff->pcapfd=pcap_fileno((pcap_t*)(psniff->ppcapt));

#if defined LCRZODEF_SYSTEM_FreeBSD || defined LCRZODEF_SYSTEM_OpenBSD 
  /* under xxxBSD, a select() on pcapfd keeps waiting, so we have
     to set BIOCIMMEDIATE */
  { int one=1;
    retour=ioctl(psniff->pcapfd, BIOCIMMEDIATE, &one);
    if (retour) return(LCRZO_ERR_FUIOCTL);
  }
#elif defined LCRZODEF_SYSTEM_Solaris
 #if LCRZODEF_HAVEINCL_SYS_BUFMOD==1
  /* under Solaris, a select() on pcapfd keeps waiting till the next 
     packet, because it is buffered, so we have to set timeout and
     chunk size to zero */
  { int zero=0;
    retour=ioctl(psniff->pcapfd, SBIOCSTIME, &zero);
    if (retour) return(LCRZO_ERR_FUIOCTL);
    retour=ioctl(psniff->pcapfd, SBIOCSCHUNK, &zero);
    if (retour) return(LCRZO_ERR_FUIOCTL);
  }
 #endif
#elif defined LCRZODEF_SYSTEM_Linux
#elif defined LCRZODEF_SYSTEM_Windows
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif

  /*initialize the variables*/
  lcrzo_er(lcrzo_global_get_sniff_ipreas1timeout(&(psniff->para_ipreastimeout1)));
  lcrzo_er(lcrzo_global_get_sniff_ipreas2timeout(&(psniff->para_ipreastimeout2)));
  lcrzo_er(lcrzo_global_get_sniff_tcpreor1timeout(&(psniff->para_tcpreortimeout1)));
  lcrzo_er(lcrzo_global_get_sniff_tcpreor2timeout(&(psniff->para_tcpreortimeout2)));
  psniff->snifftype=snifftype;
  psniff->dataend=LCRZO_FALSE;
  psniff->storeddata=NULL;
  psniff->storeddatasize=0;
  
  /*initialise les listes des packets fragmentes et tcp*/
  if ( snifftype==LCRZO_SNIFF_TYPE_IPREAS || snifftype==LCRZO_SNIFF_TYPE_TCPREOR )
  { lcrzo_er(lcrzo_list_init(&(psniff->waitingippackets),
			   sizeof(lcrzo_priv_paqipattente), NULL));
    lcrzo_er(lcrzo_list_init(&(psniff->seenippackets),
			   sizeof(lcrzo_priv_paqipvus), NULL));
  }
  if ( snifftype==LCRZO_SNIFF_TYPE_TCPREOR )
  { lcrzo_er(lcrzo_list_init(&(psniff->waitingtcppackets),
			   sizeof(lcrzo_priv_paqtcpattente),
			   &lcrzo_priv_effacepaqtcpattente));
    lcrzo_er(lcrzo_list_init(&(psniff->seentcppackets),
			   sizeof(lcrzo_priv_paqtcpvus), NULL));
  }

  /* get the linktype */
  psniff->linktype=pcap_datalink(psniff->ppcapt);
  /* verify we know it (ignore error code, only display a warning) */
  lcrzo_priv_datalink_verifknown(psniff->linktype);

  return(LCRZO_ERR_OK);
}
#else
int lcrzo_sniff_init(const lcrzo_device device,
		     lcrzo_int32 nbmaxoct,
		     int snifftype,
		     const char *filtre, 
		     lcrzo_sniff *psniff)
{ return(lcrzo_priv_checkpcapincluded("lcrzo_sniff_init"));
  /* just for the compiler warning */
  device=device;
  nbmaxoct=nbmaxoct;
  snifftype=snifftype;
  filtre=filtre;
  psniff=psniff;
}
#endif

