/* This file is part of Lcrzo.
*/

/*-------------------------------------------------------------*/
typedef struct {
  lcrzo_time heuredernieremaj;
  lcrzo_ipl ipls;
  lcrzo_ipl ipld;
  lcrzo_uint16 ports;
  lcrzo_uint16 portd;
  lcrzo_uint32 octetattendu;
} lcrzo_priv_paqtcpvus;

/*-------------------------------------------------------------*/
typedef struct {
  lcrzo_time heureajout;
  lcrzo_ipl ipls;
  lcrzo_ipl ipld;
  lcrzo_uint16 ports;
  lcrzo_uint16 portd;
  lcrzo_uint32 seqnum;
  lcrzo_uint8 *pdata;
  lcrzo_uint16 nboctdata;
  lcrzo_uint32 nboctdonneestcpsynfin; /*nboct + 1 si syn ou fin*/
} lcrzo_priv_paqtcpattente;

/*-------------------------------------------------------------*/
int lcrzo_priv_effacepaqtcpattente(void *pelem);
int lcrzo_priv_effacepaqtcpattente(void *pelem)
{ lcrzo_priv_paqtcpattente *ppta;
  ppta=(lcrzo_priv_paqtcpattente*)pelem;
  return(lcrzo_data_free(ppta->pdata));
}

/*-------------------------------------------------------------*/
int lcrzo_priv_affpaqtcpvus(const void *pelem, void *pinfos);
int lcrzo_priv_affpaqtcpvus(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqtcpvus *pptv;
  pptv=(const lcrzo_priv_paqtcpvus *)pelem;
  lcrzo_uint32_print("", pptv->heuredernieremaj.sec, "%08u", "; ");
  lcrzo_uint32_print("", pptv->ipls, "%X", ":");
  lcrzo_uint16_print("", pptv->ports, "%d", "->");
  lcrzo_uint32_print("", pptv->ipld, "%X", ":");
  lcrzo_uint16_print("", pptv->portd, "%d", " attend");
  lcrzo_uint32_print("", pptv->octetattendu, "%u", "\n");
  return(LCRZO_ERR_OK);
  pinfos=pinfos; /*just for the compiler*/
}

/*-------------------------------------------------------------*/
int lcrzo_priv_affpaqtcpattente(const void *pelem, void *pinfos);
int lcrzo_priv_affpaqtcpattente(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqtcpattente *ppta;
  ppta=(const lcrzo_priv_paqtcpattente *)pelem;
  lcrzo_uint32_print("", ppta->heureajout.sec, "%08u", "; ");
  lcrzo_uint32_print("", ppta->ipls, "%X", ":");
  lcrzo_uint16_print("", ppta->ports, "%d", "->");
  lcrzo_uint32_print("", ppta->ipld, "%X", ":");
  lcrzo_uint16_print("", ppta->portd, "%d", " seqnum");
  lcrzo_uint32_print("", ppta->seqnum, "%u", " data");
  lcrzo_uint32_print("", ppta->nboctdata, "%u", " tcp+synfin");
  lcrzo_uint32_print("", ppta->nboctdonneestcpsynfin, "%u", "\n");
  return(LCRZO_ERR_OK);
  pinfos=pinfos; /*just for the compiler*/
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_peutenvoyertcpattcrit(const void *pelem, void *pinfos);
lcrzo_bool lcrzo_priv_peutenvoyertcpattcrit(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqtcpvus *pptv1;
  lcrzo_priv_paqtcpvus *pptv2;

  pptv1=(const lcrzo_priv_paqtcpvus *)pelem;
  pptv2=(lcrzo_priv_paqtcpvus *)pinfos;
  if ( pptv1->ipls==pptv2->ipls &&
       pptv1->ipld==pptv2->ipld &&
       pptv1->ports==pptv2->ports &&
       pptv1->portd==pptv2->portd &&
       pptv1->octetattendu==pptv2->octetattendu ) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_peutenvoyertcpattente(lcrzo_priv_paqtcpattente pta,
				      lcrzo_list *plptv);
int lcrzo_priv_peutenvoyertcpattente(lcrzo_priv_paqtcpattente pta,
				      lcrzo_list *plptv)
{ int retour;
  lcrzo_priv_paqtcpvus ptvtrouve, ptvcherche;
  lcrzo_int32 poselem;

  /*on se sert d'un ptv pour la recherche*/
  ptvcherche.ipls=pta.ipls;
  ptvcherche.ipld=pta.ipld;
  ptvcherche.ports=pta.ports;
  ptvcherche.portd=pta.portd;
  ptvcherche.octetattendu=pta.seqnum;
  /*on cherche*/
  retour=lcrzo_list_search_all(plptv, &lcrzo_priv_peutenvoyertcpattcrit,
			       &ptvcherche, &poselem, &ptvtrouve);
  if (retour!=LCRZO_ERR_OK) return(0);
  /*on l'a trouve, on met a jour octetattendu*/
  ptvtrouve.octetattendu+=pta.nboctdonneestcpsynfin;
  lcrzo_er(lcrzo_time_init_currenttime(&(ptvtrouve.heuredernieremaj)));
  lcrzo_er(lcrzo_list_replace_pos(plptv, poselem, &ptvtrouve));
  return(1);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_purgpaqtcpattcrit(const void *pelem, void *pinfos);
lcrzo_bool lcrzo_priv_purgpaqtcpattcrit(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqtcpattente *ppta;
  lcrzo_uint32 secmini;

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


/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_purgpaqtcpvuscrit(const void *pelem, void *pinfos);
lcrzo_bool lcrzo_priv_purgpaqtcpvuscrit(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqtcpvus *pptv;
  lcrzo_uint32 secmini;

  pptv=(const lcrzo_priv_paqtcpvus *)pelem;
  secmini=*(lcrzo_uint32 *)pinfos;
  if ( pptv->heuredernieremaj.sec < secmini ) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_purgepaqtcpvusattente(lcrzo_sniff sniff,
				     lcrzo_list *plistevus,
				     lcrzo_list *plisteattente);
int lcrzo_priv_purgepaqtcpvusattente(lcrzo_sniff sniff,
				     lcrzo_list *plistevus,
				     lcrzo_list *plisteattente)
{ lcrzo_priv_staticvars *psv;
  lcrzo_time heure;
  lcrzo_uint32 secmini;

  lcrzo_er(lcrzo_priv_staticvars_ptr_get(&psv));
  psv->sniff_reas_threshold_tcp++;
  if (!(psv->sniff_reas_threshold_tcp%5)) /*pour ne pas le faire a chaque passage*/
  { lcrzo_er(lcrzo_time_init_currenttime(&heure));
    /*on timeout apres xx secondes la liste d'attente*/
    secmini=heure.sec-sniff.para_tcpreortimeout2;
    /*on purge*/
    lcrzo_er(lcrzo_list_remove_criteria_all(plisteattente,
					    &lcrzo_priv_purgpaqtcpattcrit,
					    &secmini));
    /*on timeout apres xx secondes la liste des vues*/
    secmini=heure.sec-sniff.para_tcpreortimeout1;
    /*on purge*/
    lcrzo_er(lcrzo_list_remove_criteria_all(plistevus,
					    &lcrzo_priv_purgpaqtcpvuscrit,
					    &secmini));
    psv->sniff_reas_threshold_tcp-=5;
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
lcrzo_bool lcrzo_priv_cherpaqtcpvuscrit(const void *pelem, void *pinfos);
lcrzo_bool lcrzo_priv_cherpaqtcpvuscrit(const void *pelem, void *pinfos)
{ const lcrzo_priv_paqtcpvus *pptv1;
  lcrzo_priv_paqtcpvus *pptv2;

  pptv1=(const lcrzo_priv_paqtcpvus *)pelem;
  pptv2=(lcrzo_priv_paqtcpvus *)pinfos;
  if ( pptv1->ipls==pptv2->ipls &&
       pptv1->ipld==pptv2->ipld &&
       pptv1->ports==pptv2->ports &&
       pptv1->portd==pptv2->portd ) return(LCRZO_TRUE);
  return(LCRZO_FALSE);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_cherpaqtcpvus(lcrzo_list *pliste,
			      lcrzo_ipl ipls,
			      lcrzo_ipl ipld,
			      lcrzo_uint16 ports,
			      lcrzo_uint16 portd,
			      lcrzo_int32 *ppos,
			      lcrzo_priv_paqtcpvus *pptv);
int lcrzo_priv_cherpaqtcpvus(lcrzo_list *pliste,
			      lcrzo_ipl ipls,
			      lcrzo_ipl ipld,
			      lcrzo_uint16 ports,
			      lcrzo_uint16 portd,
			      lcrzo_int32 *ppos,
			      lcrzo_priv_paqtcpvus *pptv)
{ lcrzo_int32 retour;
  lcrzo_priv_paqtcpvus ptv;

  ptv.ipls=ipls;
  ptv.ipld=ipld;
  ptv.ports=ports;
  ptv.portd=portd;
  retour=lcrzo_list_search_all(pliste, &lcrzo_priv_cherpaqtcpvuscrit,
			       &ptv, ppos, pptv);
  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_nextmreortcp(lcrzo_sniff *psniff,
				  lcrzo_data *pdata,
				  lcrzo_int32 *pdatasize);
int lcrzo_priv_sniff_nextmreortcp(lcrzo_sniff *psniff,
				  lcrzo_data *pdata,
				  lcrzo_int32 *pdatasize)
{ lcrzo_uint32 tole;
  lcrzo_int32 poselem, nbelems, i;
  lcrzo_uint16 nboctdonneestcp;
  lcrzo_priv_paqtcpattente pta;
  lcrzo_priv_paqtcpvus ptv;
  lcrzo_hdrlip hdrip;
  lcrzo_hdrltcp hdrtcp;
  int retour;
  lcrzo_data sniffdata;
  lcrzo_int32 sniffdatasize;

  /*lcrzo_er(lcrzo_priv_affdebugsniff(*psniff));*/
  
  /*regarde si un packet en attente peut etre envoye*/
  lcrzo_er(lcrzo_list_count(&(psniff->waitingtcppackets), &nbelems));
  for ( i=1 ; i<=nbelems ; i++ )
  { lcrzo_er(lcrzo_list_value_pos(&(psniff->waitingtcppackets), i, &pta));
    /*si le packet est bon a envoyer*/
    if ( lcrzo_priv_peutenvoyertcpattente(pta, &(psniff->seentcppackets)) )
    { /*prepare les donnees a sortir*/
      lcrzo_er(lcrzo_data_initm_data(pta.pdata, pta.nboctdata,
				     pdata, pdatasize));
      /*efface le pta*/
      lcrzo_er(lcrzo_list_remove_pos(&(psniff->waitingtcppackets), i));
      /*le lcrzo_priv_paqtcpvus a ete mis a jour dans peutenvoyertcpattente*/
      /*on peut donc quitter*/
      return(LCRZO_ERR_OK);
    }
  }

  /*on purge les listes*/
  lcrzo_er(lcrzo_priv_purgepaqtcpvusattente(*psniff, &(psniff->seentcppackets),
					    &(psniff->waitingtcppackets)));

  /*sniffe et sort sur erreur*/
  lcrzo_er(lcrzo_priv_sniff_nextmreasip(psniff, &sniffdata, &sniffdatasize));

  /*si on arrive ici, on a retourne LCRZO_ERR_OK, donc on a des donnees
    quelle que soit la valeur de beblocking*/
  /*si ce n'est pas du TCP, on sort*/
  retour=lcrzo_packet_decodem_ethipopttcpoptdata(sniffdata, sniffdatasize,
						   NULL, &hdrip, NULL, NULL,
						   &hdrtcp, NULL, NULL,
						   NULL, &nboctdonneestcp);
  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 du tcp*/
  /*on cherche a savoir si on a deja vu ce packet*/
  lcrzo_efr(lcrzo_priv_cherpaqtcpvus(&(psniff->seentcppackets),
				       hdrip.saddr, hdrip.daddr,
				       hdrtcp.sport, hdrtcp.dport,
				       &poselem, &ptv),
	      lcrzo_data_free(sniffdata));
  if (poselem==0) /*non trouve dans la liste*/
  { /*on l'y ajoute*/
    lcrzo_er(lcrzo_time_init_currenttime(&(ptv.heuredernieremaj)));
    ptv.ipls=hdrip.saddr;
    ptv.ipld=hdrip.daddr;
    ptv.ports=hdrtcp.sport;
    ptv.portd=hdrtcp.dport;
    ptv.octetattendu=hdrtcp.seqnum + nboctdonneestcp
	               + ((hdrtcp.syn|hdrtcp.fin)?1:0);
    lcrzo_er(lcrzo_list_add_first(&(psniff->seentcppackets), &ptv));
    if (pdatasize!=NULL) *pdatasize=sniffdatasize;
    if (pdata!=NULL) *pdata=sniffdata;
    else lcrzo_data_free(sniffdata);
    return(LCRZO_ERR_OK);
  }

  /*on cherche maintenant a savoir si il faut l'envoyer de suite ou non*/
  if ( ptv.octetattendu==hdrtcp.seqnum )    
  { /*on peut l'envoyer, donc on fait les mises a jour et on quitte*/
    ptv.octetattendu+=nboctdonneestcp + ((hdrtcp.syn|hdrtcp.fin)?1:0);
    lcrzo_er(lcrzo_time_init_currenttime(&(ptv.heuredernieremaj)));
    lcrzo_er(lcrzo_list_replace_pos(&(psniff->seentcppackets), poselem, &ptv));
    if (pdatasize!=NULL) *pdatasize=sniffdatasize;
    if (pdata!=NULL) *pdata=sniffdata;
    else lcrzo_data_free(sniffdata);
    return(LCRZO_ERR_OK);
  }

  /*si ce packet est un vieux, ou est totalement desynchronise (on
    se donne une tolerance de 100000), on l'ignore.*/
  tole=100000;
  if ( ptv.octetattendu <= 0xFFFFFFFFu-tole )
  { if ( hdrtcp.seqnum > ptv.octetattendu+tole ||
	 hdrtcp.seqnum <= ptv.octetattendu )
    { lcrzo_data_free(sniffdata);
      return(LCRZO_ERR_OKTEMPDATAEND);
    }
  }
  else
  { if ( hdrtcp.seqnum > ptv.octetattendu+tole/*deborde*/ &&
  hdrtcp.seqnum <= ptv.octetattendu )
    { lcrzo_data_free(sniffdata);
      return(LCRZO_ERR_OKTEMPDATAEND);
    }
  }

  /*ce packet est arrive trop tot, on le met dans la liste d'attente*/
  lcrzo_efr(lcrzo_time_init_currenttime(&(pta.heureajout)), 
     lcrzo_data_free(sniffdata));
  pta.ipls=hdrip.saddr;
  pta.ipld=hdrip.daddr;
  pta.ports=hdrtcp.sport;
  pta.portd=hdrtcp.dport;
  pta.seqnum=hdrtcp.seqnum;
  if ( sniffdatasize > 0xFFFF )
  { lcrzo_data_free(sniffdata);
    return(lcrzo_ie("lcrzo_priv_sniff_nextmreortcp", 
      "sniffdatasize > 0xFFFF", NULL));
  }
  pta.nboctdata=(lcrzo_uint16)sniffdatasize;
  lcrzo_efr(lcrzo_data_alloc(pta.nboctdata, &(pta.pdata)),
	      lcrzo_data_free(sniffdata));
  memcpy(pta.pdata, sniffdata, pta.nboctdata);
  lcrzo_data_free(sniffdata);
  pta.nboctdonneestcpsynfin=nboctdonneestcp + ((hdrtcp.syn|hdrtcp.fin)?1:0);
  lcrzo_er(lcrzo_list_add_first(&(psniff->waitingtcppackets), &pta));
  return(LCRZO_ERR_OKTEMPDATAEND);
}

