/* This file is part of Lcrzo.
*/

/*-------------------------------------------------------------*/
typedef struct {
  lcrzo_time heureajout;
  lcrzo_ipl ipls;
  lcrzo_ipl ipld;
  lcrzo_uint16 id;
  lcrzo_uint8 protocol;
} lcrzo_priv_paqipvus;

typedef struct {
  lcrzo_time heurepremierfrag;
  lcrzo_hdrleth hdreth;
  lcrzo_hdrlip hdrip;
  lcrzo_uint8 hdripcorrect;
  lcrzo_ipopt ipopt;
  lcrzo_uint8 nboctipopt;
  lcrzo_uint8 dataip[65536];
  lcrzo_uint16 nboctdataip;
  lcrzo_uint8 datasauvees[8192+1]; /*car on va se servir de string*/
} lcrzo_priv_paqipattente;

/*-------------------------------------------------------------*/
int lcrzo_priv_affpaqipvus(const void *pelem, void *pinfos);
int lcrzo_priv_affpaqipvus(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqipvus *ppiv;
  ppiv=(const lcrzo_priv_paqipvus *)pelem;
  lcrzo_uint32_print("", ppiv->heureajout.sec, "%08u", "; ");
  lcrzo_uint32_print("", ppiv->ipls, "%X", "->");
  lcrzo_uint32_print("", ppiv->ipld, "%X", " ");
  printf("id%d proto%d\n", ppiv->id, ppiv->protocol);
  return(LCRZO_ERR_OK);
  pinfos=pinfos; /*just for the compiler*/
}

/*-------------------------------------------------------------*/
int lcrzo_priv_affpaqipattente(const void *pelem, void *pinfos);
int lcrzo_priv_affpaqipattente(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqipattente *ppia;
  int taille;
  ppia=(const lcrzo_priv_paqipattente *)pelem;
  if (ppia->datasauvees[8192]) taille=-1;
  else taille=strlen((const char*)ppia->datasauvees);
  lcrzo_uint32_print("", ppia->heurepremierfrag.sec, "%08u", "; ");
  lcrzo_uint32_print("", ppia->hdrip.saddr, "%X", "->");
  lcrzo_uint32_print("", ppia->hdrip.daddr, "%X", "");
  printf("(%d) nboctdataip%u strlen%d\n", ppia->hdripcorrect,
	 ppia->nboctdataip, taille);
  return(LCRZO_ERR_OK);
  pinfos=pinfos; /*just for the compiler*/
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_purgpaqipvuscrit(const void *pelem, void *pinfos);
lcrzo_bool lcrzo_priv_purgpaqipvuscrit(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqipvus *pfi;
  lcrzo_uint32 secmini;

  pfi=(const lcrzo_priv_paqipvus *)pelem;
  secmini=*(lcrzo_uint32 *)pinfos;
  if ( pfi->heureajout.sec < secmini ) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_cherpaqipvuscrit(const void *pelem, void *pinfos);
lcrzo_bool lcrzo_priv_cherpaqipvuscrit(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqipvus *pfi1;
  lcrzo_priv_paqipvus *pfi2;

  pfi1=(const lcrzo_priv_paqipvus *)pelem;
  pfi2=(lcrzo_priv_paqipvus *)pinfos;
  if ( pfi1->ipls==pfi2->ipls &&
       pfi1->ipld==pfi2->ipld &&
       pfi1->protocol==pfi2->protocol &&
       pfi1->id==pfi2->id ) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_cherpaqipvus(lcrzo_sniff sniff,
			    lcrzo_list *pliste,
			    lcrzo_ipl ipls,
			    lcrzo_ipl ipld,
			    lcrzo_uint8 protocol,
			    lcrzo_uint16 id,
			    lcrzo_int32 *ppos);
int lcrzo_priv_cherpaqipvus(lcrzo_sniff sniff,
			    lcrzo_list *pliste,
			    lcrzo_ipl ipls,
			    lcrzo_ipl ipld,
			    lcrzo_uint8 protocol,
			    lcrzo_uint16 id,
			    lcrzo_int32 *ppos)
{ lcrzo_priv_staticvars *psv;
  lcrzo_time heure;
  lcrzo_uint32 secmini;
  lcrzo_int32 retour;
  lcrzo_priv_paqipvus piv;

  /*on commence par purger*/
  lcrzo_er(lcrzo_priv_staticvars_ptr_get(&psv));
  psv->sniff_reas_threshold_ipvu++;
  if (!(psv->sniff_reas_threshold_ipvu%5)) /*pour ne pas le faire a chaque passage*/
  { /*on timeout apres xx secondes*/
    lcrzo_er(lcrzo_time_init_currenttime(&heure));
    secmini=heure.sec-sniff.para_ipreastimeout1;
    /*on purge*/
    lcrzo_er(lcrzo_list_remove_criteria_all(pliste,
					    &lcrzo_priv_purgpaqipvuscrit,
					    &secmini));
    psv->sniff_reas_threshold_ipvu-=5;
  }

  /*on se sert d'un piv pour faire la recherche*/
  piv.ipls=ipls;
  piv.ipld=ipld;
  piv.protocol=protocol;
  piv.id=id;
  retour=lcrzo_list_search_all(pliste, &lcrzo_priv_cherpaqipvuscrit,
			       &piv, ppos, NULL);
  if (retour==LCRZO_ERR_OKSEARCHNOTFOUND) {*ppos=0; retour=LCRZO_ERR_OK;}
  if (retour!=LCRZO_ERR_OK) return(retour);

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_purgpaqipattentecrit(const void *pelem, void *pinfos);
lcrzo_bool lcrzo_priv_purgpaqipattentecrit(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqipattente *pfi;
  lcrzo_uint32 secmini;

  pfi=(const lcrzo_priv_paqipattente *)pelem;
  secmini=*(lcrzo_uint32 *)pinfos;
  if ( pfi->heurepremierfrag.sec < secmini ) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_cherpaqipattentecrit(const void *pelem, void *pinfos);
lcrzo_bool lcrzo_priv_cherpaqipattentecrit(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqipattente *pfi1;
  lcrzo_priv_paqipvus *pfi2;

  pfi1=(const lcrzo_priv_paqipattente *)pelem;
  pfi2=(lcrzo_priv_paqipvus *)pinfos;
  if ( pfi1->hdrip.saddr==pfi2->ipls &&
       pfi1->hdrip.daddr==pfi2->ipld &&
       pfi1->hdrip.protocol==pfi2->protocol &&
       pfi1->hdrip.id==pfi2->id ) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_cherpaqipattente(lcrzo_sniff sniff,
				lcrzo_list *pliste,
				lcrzo_ipl ipls,
				lcrzo_ipl ipld,
				lcrzo_uint8 protocol,
				lcrzo_uint16 id,
				lcrzo_int32 *ppos,
				lcrzo_priv_paqipattente *pfi);
int lcrzo_priv_cherpaqipattente(lcrzo_sniff sniff,
				lcrzo_list *pliste,
				lcrzo_ipl ipls,
				lcrzo_ipl ipld,
				lcrzo_uint8 protocol,
				lcrzo_uint16 id,
				lcrzo_int32 *ppos,
				lcrzo_priv_paqipattente *pfi)
{ lcrzo_priv_staticvars *psv;
  lcrzo_time heure;
  lcrzo_uint32 secmini;
  lcrzo_int32 retour;
  lcrzo_priv_paqipvus piv;

  /*on commence par purger*/ 
  lcrzo_er(lcrzo_priv_staticvars_ptr_get(&psv));
  psv->sniff_reas_threshold_ipat++;
  if (!(psv->sniff_reas_threshold_ipat%5)) /*pour ne pas le faire a chaque passage*/
  { /*on timeout apres xx secondes*/
    lcrzo_er(lcrzo_time_init_currenttime(&heure));
    secmini=heure.sec-sniff.para_ipreastimeout2;
    /*on purge*/
    lcrzo_er(lcrzo_list_remove_criteria_all(pliste,
					    &lcrzo_priv_purgpaqipattentecrit,
					    &secmini));
    psv->sniff_reas_threshold_ipat-=5;
  }

  /*on se sert d'un piv pour faire la recherche*/
  piv.ipls=ipls;
  piv.ipld=ipld;
  piv.protocol=protocol;
  piv.id=id;
  retour=lcrzo_list_search_all(pliste, &lcrzo_priv_cherpaqipattentecrit,
			       &piv, ppos, pfi);
  if (retour==LCRZO_ERR_OKSEARCHNOTFOUND) {*ppos=0; retour=LCRZO_ERR_OK;}
  if (retour!=LCRZO_ERR_OK) return(retour);

  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_priv_sniff_nextmreasip(lcrzo_sniff *psniff,
				 lcrzo_data *pdata,
				 lcrzo_int32 *pdatasize);
int lcrzo_priv_sniff_nextmreasip(lcrzo_sniff *psniff,
				 lcrzo_data *pdata,
				 lcrzo_int32 *pdatasize)
{ int retour;
  lcrzo_hdrleth hdreth;
  lcrzo_hdrlip hdrip;
  lcrzo_ipopt ipopt;
  lcrzo_uint8 nboctipopt;
  lcrzo_data dataip;
  lcrzo_uint16 nboctdataip;
  lcrzo_int32 poselem;
  lcrzo_priv_paqipattente pia;
  lcrzo_priv_paqipvus piv;
  lcrzo_data sniffdata;
  lcrzo_int32 sniffdatasize;

  /*affichage de debug*/
  /*lcrzo_er(lcrzo_priv_affdebugsniff(*psniff));*/
  /*sniffe et sort sur erreur*/
  lcrzo_er(lcrzo_priv_sniff_nextmnormal(psniff, &sniffdata, &sniffdatasize));
  /*si on arrive ici, on a retourne LCRZO_ERR_OK, donc on a des donnees */
  /*si ce n'est pas de l'IP, ou que le packet n'est pas fragmente*/
  retour=lcrzo_packet_decodem_ethipoptdata(sniffdata, sniffdatasize,
					     &hdreth, &hdrip,
					     ipopt, &nboctipopt, 
					     &dataip, &nboctdataip);
  if (retour!=LCRZO_ERR_OK)
  { if (pdatasize!=NULL) *pdatasize=sniffdatasize;
    if (pdata!=NULL) *pdata=sniffdata;
    else lcrzo_data_free(sniffdata);
    return(LCRZO_ERR_OK);
  }
  /*si l'on arrive ici, ce packet est de l'ip*/
  /*on cherche a savoir si on a deja vu ce packet*/
  lcrzo_er(lcrzo_priv_cherpaqipvus(*psniff, &(psniff->seenippackets),
				      hdrip.saddr, hdrip.daddr,
				      hdrip.protocol, hdrip.id, &poselem));
  if (poselem!=0) /*trouve dans la liste : c'est un doublon*/
  { /*on n'a rien a fournir a l'appelant*/
    lcrzo_data_free(sniffdata);
    lcrzo_data_free(dataip);
    return(LCRZO_ERR_OKTEMPDATAEND);
  }

  if ( hdrip.morefrag==0 && hdrip.offsetfrag==0 )
  { /*ce packet n'est pas fragmente : ajoute dans la liste des packets vus*/
    lcrzo_er(lcrzo_time_init_currenttime(&(piv.heureajout)));
    piv.ipls=hdrip.saddr;
    piv.ipld=hdrip.daddr;
    piv.id=hdrip.id;
    piv.protocol=hdrip.protocol;
    lcrzo_efr(lcrzo_list_add_first(&(psniff->seenippackets), &piv),
		lcrzo_data_free(sniffdata));
    /*quitte : passe ce packet a l'appelant*/
    if (pdatasize!=NULL) *pdatasize=sniffdatasize;
    if (pdata!=NULL) *pdata=sniffdata;
    else lcrzo_data_free(sniffdata);
    lcrzo_data_free(dataip);
    return(LCRZO_ERR_OK);
  }
  lcrzo_data_free(sniffdata);
  /*si on arrive ici, ce packet est un fragment, on tente de le
    reassembler*/

  /*on regarde si ce packet a deja ete mis dans la liste*/
  lcrzo_efr(lcrzo_priv_cherpaqipattente(*psniff, &(psniff->waitingippackets),
					  hdrip.saddr, hdrip.daddr,
					  hdrip.protocol, hdrip.id, 
					  &poselem, &pia),
	      lcrzo_data_free(dataip));
  if (poselem==0) /*pas trouve dans la liste*/
  { /*on va l'ajouter dans la liste*/
    lcrzo_er(lcrzo_time_init_currenttime(&(pia.heurepremierfrag)));
    /*cette taille ne sera connue que lors de la reception du dernier*/
    pia.nboctdataip=0;
    /*l'hdrip correct vient du premier fragment*/
    pia.hdripcorrect=0;
    /*initialise les fragments recus*/
    memset(pia.datasauvees, 0, 8192+1); /*car on va se servir de string*/
  }

  /*toutes les entetes sont copiees en provenance du premier fragment*/
  if ( hdrip.offsetfrag==0 )
  { memcpy(&(pia.hdreth), &(hdreth), sizeof(lcrzo_hdrleth));
    memcpy(&(pia.hdrip), &(hdrip), sizeof(lcrzo_hdrlip));
    memcpy(pia.ipopt, ipopt, nboctipopt);
    pia.nboctipopt=nboctipopt;
    pia.hdripcorrect=1;
  }
  /*on a besoin de l'entete, meme si on n'a pas encore recu le premier frag*/
  if ( ! (pia.hdripcorrect) )
  { memcpy(&(pia.hdrip), &(hdrip), sizeof(lcrzo_hdrlip));
  }
  /*la taille des donnees est calculee a partir du dernier fragment*/
  if ( hdrip.morefrag==0 )
  { pia.nboctdataip=(lcrzo_uint16)(nboctdataip+8*hdrip.offsetfrag);
  }
  /*mise a jour des donnees*/
  memcpy(pia.dataip+8*hdrip.offsetfrag, dataip, nboctdataip);
  lcrzo_data_free(dataip);
  memset(pia.datasauvees+hdrip.offsetfrag, 1, (nboctdataip+7)/8);

  if (poselem==0)
  { /*on l'ajoute dans la liste*/
    lcrzo_er(lcrzo_list_add_first(&(psniff->waitingippackets), &pia));
  }
  else
  { /*on remplace le vieux par le nouveau*/
    lcrzo_er(lcrzo_list_replace_pos(&(psniff->waitingippackets),poselem, &pia));
  }
 
  /*si on a toutes les donnees du packet, il ne doit plus y avoir
    de '\0' (terminateur de string) dans datasauvees*/
  if ( pia.nboctdataip &&
	 (int)strlen((const char *)pia.datasauvees)==
	 (int)((pia.nboctdataip+7)/8) )
  { /*on reconstruit ce packet*/
    pia.hdrip.totlen=(lcrzo_uint16)( sizeof(lcrzo_hdrpip)+pia.nboctipopt+
		       pia.nboctdataip );
    pia.hdrip.offsetfrag=0;
    pia.hdrip.morefrag=0;
    pia.hdrip.check=0;
    lcrzo_er(lcrzo_packet_initm_ethipoptdata(pia.hdreth, pia.hdrip,
					       pia.ipopt, pia.nboctipopt,
					       pia.dataip, pia.nboctdataip,
					       pdata, pdatasize));
    /*ajoute ce packet dans la liste des packets vus*/
    lcrzo_er(lcrzo_time_init_currenttime(&(piv.heureajout)));
    piv.ipls=pia.hdrip.saddr;
    piv.ipld=pia.hdrip.daddr;
    piv.id=pia.hdrip.id;
    piv.protocol=pia.hdrip.protocol;
    lcrzo_er(lcrzo_list_add_first(&(psniff->seenippackets), &piv));
    /*on efface l'element de la liste*/
    lcrzo_er(lcrzo_list_remove_pos(&(psniff->waitingippackets), poselem));
    /*on quitte ici*/
    return(LCRZO_ERR_OK);
  }

  /*on n'a rien a fournir a l'appelant*/
  return(LCRZO_ERR_OKTEMPDATAEND);
}

