/*
		                  LCRZO
                             Network library
             Copyright(c) 1999,2000,2001,2002 Laurent Constantin
                                  -----

  Main server    : http://www.laurentconstantin.com/
  Backup servers : http://go.to/laurentconstantin/
                   http://laurentconstantin.est-la.com/
                   http://laurentconstantin.free.fr/
                   http://lauconstantin.multimania.com/
  [my current email address is on the web servers]

                                  -----
  This file is part of Lcrzo.

  Lcrzo is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  Lcrzo 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
  Lesser General Public License for more details (http://www.gnu.org).

------------------------------------------------------------------------
*/

/* -- Note about comments and variable names --
   In versions 1.00 up to 2.08, lcrzo was only written in French (my
   mother tongue). Since version 3.00, lcrzo is written in English, but
   most of comments and variable names are still in French.
   In future versions, I'll try to translate everything into English,
   but as you may guess, that's a quite big work...
   So, for the moment, here are some word translation, if you want to
   be able to understand what's going on in the functions :
     afficher - print     ;  ajouter - add         ;  copie - copy
     dernier - last       ;  donnee - data         ;  ecrire - write
     entete - header      ;  envoye - sent         ;  ferme - close
     fichier - file       ;  lire - read           ;  masque - mask 
     nboct - byte number  ;  ouvre, ouvrir - open  ;  paq, paquet - packet
     plage - range        ;  premier - first       ;  recu - received
     reseau - network     ;  retour - return       ;  taille - size
     tunnel - pipe

*/

#include "lcrzo_incl.h"

/*   usage of control channels :
udp client :
  ipccontrol1 : unused
  ipccontrol2 : unused

udp server :
  ipccontrol1 : sniff process says who is the client to father
  ipccontrol2 : sniff process says who is the client to spoofprocess

udp multiclient server :
  ipccontrol1 : sniff process says who is the client to father (one for each packet)
  ipccontrol2 : unused

tcp client : 
  ipccontrol1 : end of handshake
  ipccontrol2 : unused

tcp server :
  ipccontrol1 : end of handshake, if OK, who is the client
  ipccontrol2 : unused
 */


/***************************************************************/
/* Private functions */

/*-------------------------------------------------------------*/
/*-- error code functions --*/
/*-------------------------------------------------------------*/
int lcrzo_priv_sock_analyzeexitchilds(lcrzo_sock *psock,
				      lcrzo_bool beblocking);
int lcrzo_priv_sock_analyzeexitchilds(lcrzo_sock *psock,
				      lcrzo_bool beblocking)
{ lcrzo_time *ptime;
  lcrzo_int16 language;

  if (beblocking)
  { ptime=LCRZO_WAIT_INFINITE;
  }
  else
  { ptime=LCRZO_WAIT_NODELAY;
  }

  if (!psock->sniffprocesshasended)
  { lcrzo_er(lcrzo_process_other_wait_end(&psock->sniffprocess, ptime,
                                          &psock->sniffprocesshasended,
                                          &psock->sniffprocessreturnedvalue));
  }
  if (!psock->spoofprocesshasended)
  { lcrzo_er(lcrzo_process_other_wait_end(&psock->spoofprocess, ptime,
                                          &psock->spoofprocesshasended,
                                          &psock->spoofprocessreturnedvalue));
  }

  /*printf("%d %d ; %d %d\n", psock->sniffprocesshasended,
	 psock->sniffprocessreturnedvalue,
         psock->spoofprocesshasended, psock->spoofprocessreturnedvalue);*/

  /* check for errors */
  if ( psock->sniffprocesshasended && 
       psock->sniffprocessreturnedvalue!=LCRZO_ERR_OK && 
       psock->sniffprocessreturnedvalue!=LCRZO_ERR_OKPROCESSABRUPTTERM )
  { lcrzo_er(lcrzo_global_get_language(&language));
    if ( language==LCRZO_GLOBAL_LANG_FR ) 
    { lcrzo_er(lcrzo_priv_global_set_errmsglcrzo("Erreur en provenance du processus de sniff"));
    }
    else
    { lcrzo_er(lcrzo_priv_global_set_errmsglcrzo("Error from sniff process"));
    }
    return(psock->sniffprocessreturnedvalue);
  }
  if ( psock->spoofprocesshasended && 
       psock->spoofprocessreturnedvalue!=LCRZO_ERR_OK && 
       psock->sniffprocessreturnedvalue!=LCRZO_ERR_OKPROCESSABRUPTTERM)
  { lcrzo_er(lcrzo_global_get_language(&language));
    if ( language==LCRZO_GLOBAL_LANG_FR ) 
    { lcrzo_er(lcrzo_priv_global_set_errmsglcrzo("Erreur en provenance du processus de spoof"));
    }
    else
    { lcrzo_er(lcrzo_priv_global_set_errmsglcrzo("Error from spoof process"));
    }
    return(psock->spoofprocessreturnedvalue);
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
/*-- wait and read functions --*/
/*-------------------------------------------------------------*/
int lcrzo_priv_wait_sock_wait(lcrzo_sock *psock, 
                              lcrzo_time *pmaxwaitingtime, 
                              lcrzo_bool *pevent)
{ lcrzo_wait waitread;
  int ret;

  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_REALCLIUDP:
    case LCRZO_PRIV_TYPESOCK_REALCLITCP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDPMULTI:
    case LCRZO_PRIV_TYPESOCK_REALSERTCPMULTI:
      ret=lcrzo_priv_wait_fd_wait(psock->socknum, pmaxwaitingtime, pevent);
      return(ret);
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTCLITCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERTCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI:
      lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_FALSE));
      lcrzo_er(lcrzo_wait_init_ipc_read(&psock->ipcsniff, &waitread));
      ret=lcrzo_wait_wait1(&waitread, pmaxwaitingtime, pevent);
      lcrzo_er(lcrzo_wait_close(&waitread));
      return(ret);
      break;
    default:
      return(lcrzo_ieuv("lcrzo_priv_wait_sock_wait", "psock->type"));
  }
  
  return(lcrzo_ierp("lcrzo_priv_wait_sock_wait"));
}

/*-------------------------------------------------------------*/
int lcrzo_priv_wait_sock_read(lcrzo_sock *psock)
{ lcrzo_data data;
  lcrzo_int32 readsize;
  struct sockaddr_in sai;
  LAURENT_socklen_t sailength;
  int ret;

  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_REALCLIUDP:
    case LCRZO_PRIV_TYPESOCK_REALCLITCP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDPMULTI:
      lcrzo_er(lcrzo_data_alloc(LCRZO_PRIV_READMAXMSGSIZE, &data));
      sailength=sizeof(struct sockaddr_in);
      readsize=LAURENT_recvfrom(psock->socknum, data,
				LCRZO_PRIV_READMAXMSGSIZE, 
				0, (struct sockaddr *)&sai, &sailength);
      /* end reached */
      if (readsize==0 && (psock->type&LCRZO_PRIV_TYPESOCK_REALCLITCP) )
      { lcrzo_data_free(data);
        return(LCRZO_ERR_OKDATAEND);
      }
      /* an error occured */
      if (readsize==-1)
      { lcrzo_data_free(data);
        if (errno==EAGAIN)
        { /*annule l'erreur, c'est simplement que rien n'etait disponible*/
          errno=0;
          return(LCRZO_ERR_OKTEMPDATAEND);
        }
        else if (errno==EBADF)
        { errno=0;
          return(LCRZO_ERR_OKDATAEND);
        }
        else
        { /*c'etait une erreur fatale*/
          return(LCRZO_ERR_OKDATAEND);
        }
      }
      /* we update client/server information */
      psock->there_ipl=lcrzo_ntohl(sai.sin_addr.s_addr);
      psock->there_port=lcrzo_ntohs(sai.sin_port);
      /* on the first read on an UDP serveur, we connect back to the client*/
      if ( !psock->udpcanwrite && psock->type==LCRZO_PRIV_TYPESOCK_REALSERUDP )
      { /*on se connecte maintenant sur le client afin de pouvoir y ecrire*/
        ret=connect(psock->socknum, (struct sockaddr *)&sai, sizeof(sai));
        if (ret<0) return(LCRZO_ERR_FUCONNECT);
        /*maintenant, comme on connait le client, on pourra ecrire*/
        psock->udpcanwrite=LCRZO_TRUE;
      }
      break;
    case LCRZO_PRIV_TYPESOCK_REALSERTCP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCPMULTI:
      lcrzo_er(lcrzo_data_alloc(LCRZO_PRIV_READMAXMSGSIZE, &data));
      readsize=LAURENT_recv(psock->socknum, data, 
			    LCRZO_PRIV_READMAXMSGSIZE, 0);
      /* an error occured*/
      if (readsize==-1)
      { lcrzo_data_free(data);
        if (errno==EAGAIN)
        { /* nothing was available */
          errno=0;
          return(LCRZO_ERR_OKTEMPDATAEND);
        }
        else if (errno==EBADF)
        { /* reached end */
          errno=0;
          return(LCRZO_ERR_OKDATAEND);
        }
        else
        { /* fatal error */
          return(LCRZO_ERR_FUREAD);
        }
      }
      /* if we read 0, we are at end of file */
      if (readsize==0)
      { lcrzo_data_free(data);
        return(LCRZO_ERR_OKDATAEND);
      }
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTCLITCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERTCP:
      lcrzo_er(lcrzo_ipc_readm(&(psock->ipcsniff), LCRZO_WAIT_NODELAY,
			       &data, &readsize));
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
      if ( ! psock->udpcanwrite )
      { /* the first time, we need to know the client */
        ret=lcrzo_ipc_read_ethaiplport(&(psock->ipccontrol1), 
				       LCRZO_WAIT_NODELAY,
				       psock->there_etha,
				       &(psock->there_ipl),
				       &(psock->there_port));
        if (ret==LCRZO_ERR_OK)
        { /* data was obtained */
          psock->udpcanwrite=LCRZO_TRUE;
        }
      } 
      lcrzo_er(lcrzo_ipc_readm(&(psock->ipcsniff), LCRZO_WAIT_NODELAY,
			       &data, &readsize));
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI:
      lcrzo_er(lcrzo_ipc_readm(&(psock->ipcsniff), LCRZO_WAIT_NODELAY,
			       &data, &readsize));
      /* if we are here, we read data, so next read can be 
	 LCRZO_WAIT_INFINITE */
      /* update who is the client */
      lcrzo_er(lcrzo_ipc_read_ethaiplport(&(psock->ipccontrol1), 
					  LCRZO_WAIT_INFINITE,
					    psock->there_etha,
					    &(psock->there_ipl),
					    &(psock->there_port)));
      break;
    default:
      return(lcrzo_ieuv("lcrzo_priv_wait_sock_read", "psock->type"));
  }
  
  /* put read data in stored data */
  /*lcrzo_data_print(data, readsize, LCRZO_PRINTTYPE_DUMP);*/
  lcrzo_er(lcrzo_priv_storedtlvdata_append(LCRZO_PRIV_IPCTYPE_MSG, data, 
					   readsize, &(psock->storeddata), 
					   &(psock->storeddatasize)));
  lcrzo_data_free(data);
  /*lcrzo_data_print(psock->storeddata, psock->storeddatasize, LCRZO_PRINTTYPE_ARRAY);*/
  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
/*-- init and close functions --*/
/*-------------------------------------------------------------*/
int lcrzo_priv_sock_ipc_init(lcrzo_sock *psock);
int lcrzo_priv_sock_ipc_init(lcrzo_sock *psock)
{ int gotoret;
  lcrzo_bool ipcsnifftoopen, ipcspooftoopen, ipcsniffendtoopen, ipcspoofendtoopen;
  lcrzo_bool ipcoptionstoopen, ipccontrol1toopen, ipccontrol2toopen;
  lcrzo_bool ipcsniffopened, ipcspoofopened, ipcsniffendopened, ipcspoofendopened;
  lcrzo_bool ipcoptionsopened, ipccontrol1opened, ipccontrol2opened;

  /* init default value */
  ipcsnifftoopen=ipcspooftoopen=ipcsniffendtoopen=ipcspoofendtoopen=LCRZO_FALSE;
  ipcoptionstoopen=ipccontrol1toopen=ipccontrol2toopen=LCRZO_FALSE;
  ipcsniffopened=ipcspoofopened=ipcsniffendopened=ipcspoofendopened=LCRZO_FALSE;
  ipcoptionsopened=ipccontrol1opened=ipccontrol2opened=LCRZO_FALSE;

  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
      ipcsnifftoopen=LCRZO_TRUE;
      ipcspooftoopen=LCRZO_TRUE;
      ipcsniffendtoopen=LCRZO_TRUE;
      ipcspoofendtoopen=LCRZO_TRUE;
      ipcoptionstoopen=LCRZO_TRUE;
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
      ipcsnifftoopen=LCRZO_TRUE;
      ipcspooftoopen=LCRZO_TRUE;
      ipcsniffendtoopen=LCRZO_TRUE;
      ipcspoofendtoopen=LCRZO_TRUE;
      ipcoptionstoopen=LCRZO_TRUE;
      ipccontrol1toopen=LCRZO_TRUE;
      ipccontrol2toopen=LCRZO_TRUE;
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI:
      ipcsnifftoopen=LCRZO_TRUE;
      ipcsniffendtoopen=LCRZO_TRUE;
      ipccontrol1toopen=LCRZO_TRUE;
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLITCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERTCP:
      ipcsnifftoopen=LCRZO_TRUE;
      ipcspooftoopen=LCRZO_TRUE;
      ipcsniffendtoopen=LCRZO_TRUE;
      ipcoptionstoopen=LCRZO_TRUE;
      ipccontrol1toopen=LCRZO_TRUE;
      break;
    default:
      return(lcrzo_ieuv("lcrzo_priv_sock_ipc_init", "psock->type"));
  }
   
  if (ipcsnifftoopen)
  { lcrzo_eg(lcrzo_ipc_init(&(psock->ipcsniff)), 
	     lcrzo_priv_sock_ipc_init_error_g);
    ipcsniffopened=LCRZO_TRUE;
  }
  if (ipcspooftoopen)
  { lcrzo_eg(lcrzo_ipc_init(&(psock->ipcspoof)), 
	     lcrzo_priv_sock_ipc_init_error_g);
    ipcspoofopened=LCRZO_TRUE;
  }
  if (ipcsniffendtoopen)
  { lcrzo_eg(lcrzo_ipc_init(&(psock->ipcsniffend)), 
	     lcrzo_priv_sock_ipc_init_error_g);
    ipcsniffendopened=LCRZO_TRUE;
  }
  if (ipcspoofendtoopen)
  { lcrzo_eg(lcrzo_ipc_init(&(psock->ipcspoofend)), 
	     lcrzo_priv_sock_ipc_init_error_g);
    ipcspoofendopened=LCRZO_TRUE;
  }
  if (ipcoptionstoopen)
  { lcrzo_eg(lcrzo_ipc_init(&(psock->ipcoptions)), 
	     lcrzo_priv_sock_ipc_init_error_g);
    ipcoptionsopened=LCRZO_TRUE;
  }
  if (ipccontrol1toopen)
  { lcrzo_eg(lcrzo_ipc_init(&(psock->ipccontrol1)), 
	     lcrzo_priv_sock_ipc_init_error_g);
    ipccontrol1opened=LCRZO_TRUE;
  }
  if (ipccontrol2toopen)
  { lcrzo_eg(lcrzo_ipc_init(&(psock->ipccontrol2)), 
	     lcrzo_priv_sock_ipc_init_error_g);
    ipccontrol2opened=LCRZO_TRUE;
  }
  return(LCRZO_ERR_OK);

  /* goto error */
lcrzo_priv_sock_ipc_init_error_g:
  if (ipccontrol2opened) { lcrzo_ipc_close(&(psock->ipccontrol2)); }
  if (ipccontrol1opened) { lcrzo_ipc_close(&(psock->ipccontrol1)); }
  if (ipcoptionsopened)  { lcrzo_ipc_close(&(psock->ipcoptions)); }
  if (ipcspoofendopened) { lcrzo_ipc_close(&(psock->ipcspoofend)); }
  if (ipcsniffendopened) { lcrzo_ipc_close(&(psock->ipcsniffend)); }
  if (ipcspoofopened)    { lcrzo_ipc_close(&(psock->ipcspoof)); }
  if (ipcsniffopened)    { lcrzo_ipc_close(&(psock->ipcsniff)); }
  return(gotoret);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_sock_ipc_close(lcrzo_sock *psock);
int lcrzo_priv_sock_ipc_close(lcrzo_sock *psock)
{ 
  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcoptions)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcspoofend)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcsniffend)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcspoof)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcsniff)));
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
      lcrzo_er(lcrzo_ipc_close(&(psock->ipccontrol2)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipccontrol1)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcoptions)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcspoofend)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcsniffend)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcspoof)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcsniff)));
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI:
      lcrzo_er(lcrzo_ipc_close(&(psock->ipccontrol1)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcsniffend)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcsniff)));
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLITCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERTCP:
      lcrzo_er(lcrzo_ipc_close(&(psock->ipccontrol1)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcoptions)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcsniffend)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcspoof)));
      lcrzo_er(lcrzo_ipc_close(&(psock->ipcsniff)));
      break;
    default:
      return(lcrzo_ieuv("lcrzo_priv_sock_ipc_close", "psock->type"));
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_sniff_init_dev(lcrzo_sock *psock,
                              lcrzo_int16 snifftype,
			      lcrzo_sniff *psniff);
int lcrzo_priv_sniff_init_dev(lcrzo_sock *psock,
			      lcrzo_int16 snifftype,
			      lcrzo_sniff *psniff)
{ int retour;
  lcrzo_uint32 mtu;
  lcrzo_string filter;
  lcrzo_ips ips;

  retour=lcrzo_mtu_init_device(psock->here_device, &mtu);
  if ( retour!=LCRZO_ERR_OK )
  { mtu=1500;
  }
  lcrzo_er(lcrzo_string_initm_text("arp or rarp or host ", &filter));
  lcrzo_er(lcrzo_ips_init_ipl(psock->here_ipl, ips));
  lcrzo_er(lcrzo_string_appendm_text(ips, &filter));
  lcrzo_er(lcrzo_sniff_init(psock->here_device, mtu, snifftype, filter, psniff));
  lcrzo_er(lcrzo_string_free(filter));

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_sock_infos_init(const lcrzo_device here_device,
			       const lcrzo_etha here_etha,
			       lcrzo_ipl here_ipl,
			       lcrzo_uint16 here_port,
			       const lcrzo_etha there_etha,
			       lcrzo_ipl there_ipl,
			       lcrzo_uint16 there_port,
			       lcrzo_sock *psock);
int lcrzo_priv_sock_infos_init(const lcrzo_device here_device,
			       const lcrzo_etha here_etha,
			       lcrzo_ipl here_ipl,
			       lcrzo_uint16 here_port,
			       const lcrzo_etha there_etha,
			       lcrzo_ipl there_ipl,
			       lcrzo_uint16 there_port,
			       lcrzo_sock *psock)
{
  lcrzo_er(lcrzo_device_init(here_device, psock->here_device));
  memcpy(psock->here_etha, here_etha, LCRZO_ETHA_MAXBYTES);
  psock->here_ipl=here_ipl;
  psock->here_port=here_port;
  if (there_etha!=NULL)
    memcpy(psock->there_etha, there_etha, LCRZO_ETHA_MAXBYTES);
  psock->there_ipl=there_ipl;
  psock->there_port=there_port;
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_sock_struct_initdefault(lcrzo_sock *psock);
int lcrzo_priv_sock_struct_initdefault(lcrzo_sock *psock)
{
  psock->type=-1;
  psock->socknum=-1;
  psock->sniffprocesshasended=LCRZO_FALSE;
  psock->spoofprocesshasended=LCRZO_FALSE;
  psock->sniffprocessreturnedvalue=-1;
  psock->spoofprocessreturnedvalue=-1;
  psock->ipoptsize=0;
  lcrzo_er(lcrzo_device_initdefault(psock->here_device));
  lcrzo_er(lcrzo_etha_initdefault(psock->here_etha));
  psock->here_ipl=0;
  psock->here_port=0;
  lcrzo_er(lcrzo_etha_initdefault(psock->there_etha));
  psock->there_ipl=0;
  psock->there_port=0;
  psock->storeddata=NULL;
  psock->storeddatasize=0;
  psock->dataend=LCRZO_FALSE;
  psock->udpcanwrite=LCRZO_FALSE;
  psock->tcpstate=-1;
  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
/*-- udp functions --*/
/*-------------------------------------------------------------*/
int lcrzo_priv_sock_spiou(lcrzo_sock *psock,
			  lcrzo_data udpdata,  
			  lcrzo_uint16 udpdatasize);
int lcrzo_priv_sock_spiou(lcrzo_sock *psock,
			  lcrzo_data udpdata,  
			  lcrzo_uint16 udpdatasize)
{ lcrzo_hdrleth hdrleth;
  lcrzo_hdrlip  hdrlip;
  lcrzo_hdrludp hdrludp;
  int ret;
  lcrzo_data ipopt;
  lcrzo_int32 ipoptsize;
  lcrzo_spoof spoof;

  /*initialisation de l'entete ETH*/
  lcrzo_er(lcrzo_hdrleth_initdefault(&hdrleth));
  memcpy(hdrleth.src, psock->here_etha, LCRZO_ETHA_MAXBYTES);
  memcpy(hdrleth.dst, psock->there_etha, LCRZO_ETHA_MAXBYTES);
  /*initialisation de l'entete IP*/
  lcrzo_er(lcrzo_hdrlip_initdefault(&hdrlip));
  hdrlip.saddr=psock->here_ipl;
  hdrlip.daddr=psock->there_ipl;
  /*initialisation de l'entete UDP*/
  lcrzo_er(lcrzo_hdrludp_initdefault(&hdrludp));
  hdrludp.sport=psock->here_port;
  hdrludp.dport=psock->there_port;

  /*mise a jour eventuelle des options IP*/
  ret=lcrzo_ipc_readm(&(psock->ipcoptions), LCRZO_WAIT_NODELAY, &ipopt, &ipoptsize);
  if ( ret==LCRZO_ERR_OK )
  { if (ipoptsize>LCRZO_IPOPT_MAXBYTES)
      return(lcrzo_ie("lcrzo_priv_sock_spiou",
		      "ipoptsize lu depuis l'IPC est > LCRZO_IPOPT_MAXBYTES",
		      "ipoptsize read from IPC is > LCRZO_IPOPT_MAXBYTES"));
    memcpy(psock->ipopt, ipopt, ipoptsize);
    psock->ipoptsize=(lcrzo_uint8)(ipoptsize);
    lcrzo_data_free(ipopt);
  }

  /*envoi d'un packet avec donnees*/
  lcrzo_er(lcrzo_spoof_init(&spoof));
  lcrzo_er(lcrzo_spoof_ethipoptudpdata(&spoof, psock->here_device,
				       hdrleth, hdrlip,
				       psock->ipopt, psock->ipoptsize,
				       hdrludp, udpdata, udpdatasize));
  lcrzo_er(lcrzo_spoof_close(&spoof));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_udp_sniffprocess(lcrzo_sock *psock);
int lcrzo_priv_udp_sniffprocess(lcrzo_sock *psock)
{ lcrzo_sniff sniff;
  lcrzo_data sniffdata, data;
  lcrzo_int32 sniffdatasize;
  lcrzo_uint16 datasize, fathermsg;
  lcrzo_hdrleth hdrleth;
  lcrzo_hdrlip hdrlip;
  lcrzo_hdrludp hdrludp;
  lcrzo_bool givepackettouser, sock_virt_answeralive, continuetoloop;
  lcrzo_wait waitsniff, waitend;
  lcrzo_bool waitsniffevent, waitendevent;
  int ret, gotoret;

  lcrzo_er(lcrzo_global_get_sock_virt_answeralive(&sock_virt_answeralive));

  /* initialize the waiting from father */
  lcrzo_er(lcrzo_wait_init_ipc_read(&psock->ipcsniffend, &waitend));

  /* initialize sniff */
  lcrzo_eg(lcrzo_priv_sniff_init_dev(psock, LCRZO_SNIFF_TYPE_IPREAS, &sniff), 
           lcrzo_priv_udp_sniffprocess_waitend_g);
  lcrzo_eg(lcrzo_wait_init_sniff_read(&sniff, &waitsniff), 
           lcrzo_priv_udp_sniffprocess_sniff_g);

  continuetoloop=LCRZO_TRUE;
  while(continuetoloop)
  { /* wait for an event */
    lcrzo_eg(lcrzo_wait_wait2(&waitend, &waitsniff, LCRZO_WAIT_INFINITE, 
                              &waitendevent, &waitsniffevent),
             lcrzo_priv_udp_sniffprocess_waitsniff_g);
    
    /* father wants to exit */
    if (waitendevent)
    { lcrzo_eg(lcrzo_ipc_read_uint16(&psock->ipcsniffend, LCRZO_WAIT_NODELAY,
				     &fathermsg),
               lcrzo_priv_udp_sniffprocess_waitsniff_g);
      if (fathermsg!=LCRZO_TRUE)
      { lcrzo_eg(LCRZO_ERR_IEINTERNALERROR, 
		 lcrzo_priv_udp_sniffprocess_waitsniff_g);
      }
      continuetoloop=LCRZO_FALSE;
    }
      
    /* there is something to sniff */
    if (waitsniffevent)
    { lcrzo_eg(lcrzo_sniff_nextm(&sniff, LCRZO_WAIT_NODELAY,
		   	         &sniffdata, &sniffdatasize),
               lcrzo_priv_udp_sniffprocess_waitsniff_g);

      /* answer for the virtual computer */
      if (sock_virt_answeralive)
      { lcrzo_eg(lcrzo_virtual_answer_alive(sniffdata, sniffdatasize,
					  psock->here_device, 
					  psock->here_etha, 
					  psock->here_ipl),
                 lcrzo_priv_udp_sniffprocess_waitsniff_g);
      }
    
      /* send data to father */
      ret=lcrzo_packet_decodem_ethipoptudpdata(sniffdata, sniffdatasize,
					     &hdrleth, &hdrlip,
					     NULL, NULL, &hdrludp,
					     &data, &datasize);
      lcrzo_data_free(sniffdata);

      /* if udp was sniffed */
      if (ret==LCRZO_ERR_OK)
      { givepackettouser=LCRZO_FALSE;
        if ( lcrzo_etha_equal(hdrleth.dst, psock->here_etha) &&
           hdrlip.daddr==psock->here_ipl &&
	   hdrludp.dport==psock->here_port )
        { /* packet is for the virtual computer */
          switch (psock->type) {
	  case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
	    /* verify also server */
	    if ( lcrzo_etha_equal(hdrleth.src, psock->there_etha) &&
	      hdrlip.saddr==psock->there_ipl &&
	       hdrludp.sport==psock->there_port )
	    { givepackettouser=LCRZO_TRUE;
	    }
	    break;
	  case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
	    /* if we do not know the client, set it */
	    if (psock->there_ipl==0)
	    { memcpy(psock->there_etha, hdrleth.src, LCRZO_ETHA_MAXBYTES);
	      psock->there_ipl=hdrlip.saddr;
	      psock->there_port=hdrludp.sport;
	      /* we write it twice because there are two readers */
	      lcrzo_eg(lcrzo_ipc_write_ethaiplport(&(psock->ipccontrol1),
						 hdrleth.src, hdrlip.saddr,
						 hdrludp.sport),
                       lcrzo_priv_udp_sniffprocess_freedata_g);
	      lcrzo_eg(lcrzo_ipc_write_ethaiplport(&(psock->ipccontrol2),
						 hdrleth.src, hdrlip.saddr,
						 hdrludp.sport),
                       lcrzo_priv_udp_sniffprocess_freedata_g);
	      /* this packet is sent to user */
	      givepackettouser=LCRZO_TRUE;
	    }  
	    /* else verify the client */
	    else if ( lcrzo_etha_equal(hdrleth.src, psock->there_etha) &&
	     hdrlip.saddr==psock->there_ipl &&
	     hdrludp.sport==psock->there_port )
	    { givepackettouser=LCRZO_TRUE;
	    }
	    break;
	  case LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI:
	    lcrzo_eg(lcrzo_ipc_write_ethaiplport(&(psock->ipccontrol1),
					       hdrleth.src, hdrlip.saddr,
					       hdrludp.sport),
                     lcrzo_priv_udp_sniffprocess_freedata_g);
	    givepackettouser=LCRZO_TRUE;
	    break;
	  default:
	    return(lcrzo_ieuv("lcrzo_priv_udp_sniffprocess", "psock->type"));
            break;
          }
        }

        /* eventually give packet to user */
        if (givepackettouser)
        { lcrzo_eg(lcrzo_ipc_write_msg(&(psock->ipcsniff), data, datasize),
                   lcrzo_priv_udp_sniffprocess_freedata_g);
        }
        lcrzo_data_free(data);
      }

    }

  }

  /* close sniff */
  lcrzo_er(lcrzo_wait_close(&waitsniff));
  lcrzo_er(lcrzo_sniff_close(&sniff));
  lcrzo_er(lcrzo_wait_close(&waitend));
  return(LCRZO_ERR_OK);

/* error gotos */
lcrzo_priv_udp_sniffprocess_freedata_g:
  lcrzo_data_free(data);
lcrzo_priv_udp_sniffprocess_waitsniff_g:
  lcrzo_wait_close(&waitsniff);
lcrzo_priv_udp_sniffprocess_sniff_g:
  lcrzo_sniff_close(&sniff);
lcrzo_priv_udp_sniffprocess_waitend_g:
  lcrzo_wait_close(&waitend);
  return(gotoret);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_udp_spoofprocess(lcrzo_sock *psock);
int lcrzo_priv_udp_spoofprocess(lcrzo_sock *psock)
{ lcrzo_data data;
  lcrzo_int32 datasize;
  lcrzo_uint16 fathermsg;
  lcrzo_bool continuetoloop;
  lcrzo_wait waitspoof, waitend;
  lcrzo_bool waitspoofevent, waitendevent;
  int gotoret;

  switch (psock->type) {
  case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
    /* nothing to do */  
    break;
  case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
    /* we have to know who is the client */
    lcrzo_er(lcrzo_ipc_read_ethaiplport(&(psock->ipccontrol2), LCRZO_WAIT_INFINITE,
					psock->there_etha,
					&(psock->there_ipl),
					&(psock->there_port)));
    break;
  default:
    return(lcrzo_ieuv("lcrzo_priv_udp_spoofprocess", "psock->type"));
    break;
  }

  /* initialize the waiting from father and ipc */
  lcrzo_er(lcrzo_wait_init_ipc_read(&psock->ipcspoofend, &waitend));
  lcrzo_eg(lcrzo_wait_init_ipc_read(&psock->ipcspoof, &waitspoof),
           lcrzo_priv_udp_spoofprocess_waitend_g);

  continuetoloop=LCRZO_TRUE;
  while(continuetoloop)
  { /* wait for an event */
    lcrzo_eg(lcrzo_wait_wait2(&waitend, &waitspoof, LCRZO_WAIT_INFINITE, 
                              &waitendevent, &waitspoofevent),
             lcrzo_priv_udp_spoofprocess_waitspoof_g);
    
    /* father wants to exit */
    if (waitendevent)
    { lcrzo_eg(lcrzo_ipc_read_uint16(&psock->ipcspoofend, LCRZO_WAIT_NODELAY, &fathermsg),
               lcrzo_priv_udp_spoofprocess_waitspoof_g);
      if (fathermsg!=LCRZO_TRUE)
      { lcrzo_eg(LCRZO_ERR_IEINTERNALERROR, lcrzo_priv_udp_spoofprocess_waitspoof_g);
      }
      continuetoloop=LCRZO_FALSE;
    }
      
    /* there is something to spoof */
    if (waitspoofevent)
    { lcrzo_eg(lcrzo_ipc_readm(&(psock->ipcspoof), LCRZO_WAIT_NODELAY, &data, &datasize),
               lcrzo_priv_udp_spoofprocess_waitspoof_g);
      lcrzo_eg(lcrzo_priv_sock_spiou(psock, data, (lcrzo_uint16)datasize),
               lcrzo_priv_udp_spoofprocess_freedata_g);
      lcrzo_er(lcrzo_data_free(data));
    }

  }

  /* close wait */
  lcrzo_er(lcrzo_wait_close(&waitspoof));
  lcrzo_er(lcrzo_wait_close(&waitend));
  return(LCRZO_ERR_OK);

/* error gotos */
lcrzo_priv_udp_spoofprocess_freedata_g:
  lcrzo_data_free(data);
lcrzo_priv_udp_spoofprocess_waitspoof_g:
  lcrzo_wait_close(&waitspoof);
lcrzo_priv_udp_spoofprocess_waitend_g:
  lcrzo_wait_close(&waitend);
  return(gotoret);
}


/*-------------------------------------------------------------*/
/*-- tcp functions --*/
/*-------------------------------------------------------------*/
#define LCRZO_PRIV_SOCK_TCPSTATE_LISTEN            1
#define LCRZO_PRIV_SOCK_TCPSTATE_SYNSENT           2
#define LCRZO_PRIV_SOCK_TCPSTATE_SYNRECEIVED       3
#define LCRZO_PRIV_SOCK_TCPSTATE_ESTABLISHED       4
#define LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT1          5
#define LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT2          6
#define LCRZO_PRIV_SOCK_TCPSTATE_CLOSEWAIT         7
#define LCRZO_PRIV_SOCK_TCPSTATE_CLOSING           8
#define LCRZO_PRIV_SOCK_TCPSTATE_LASTACK           9
#define LCRZO_PRIV_SOCK_TCPSTATE_TIMEWAIT         10
#define LCRZO_PRIV_SOCK_TCPSTATE_CLOSED           11

#define LCRZO_PRIV_SOCK_TCPSTATE_LISTEN_INIT      21
#define LCRZO_PRIV_SOCK_TCPSTATE_SYNSENT_INIT     22
#define LCRZO_PRIV_SOCK_TCPSTATE_SYNRECEIVED_INIT 23
#define LCRZO_PRIV_SOCK_TCPSTATE_ESTABLISHED_INIT 24
#define LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT1_INIT    25
#define LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT2_INIT    26
#define LCRZO_PRIV_SOCK_TCPSTATE_CLOSEWAIT_INIT   27
#define LCRZO_PRIV_SOCK_TCPSTATE_CLOSING_INIT     28
#define LCRZO_PRIV_SOCK_TCPSTATE_LASTACK_INIT     29
#define LCRZO_PRIV_SOCK_TCPSTATE_TIMEWAIT_INIT    30
#define LCRZO_PRIV_SOCK_TCPSTATE_CLOSED_INIT      31

/*-------------------------------------------------------------*/
int lcrzo_priv_sock_waitendhandshake(lcrzo_sock *psock);
int lcrzo_priv_sock_waitendhandshake(lcrzo_sock *psock)
{ lcrzo_uint32 childret;
  lcrzo_wait waitend, waithand;
  lcrzo_bool eventend, eventhand;

  lcrzo_er(lcrzo_wait_init_process_end(&psock->sniffprocess, &waitend));
  lcrzo_er(lcrzo_wait_init_ipc_read(&psock->ipccontrol1, &waithand));
  lcrzo_er(lcrzo_wait_wait2(&waitend, &waithand, LCRZO_WAIT_INFINITE, &eventend, &eventhand));
  lcrzo_er(lcrzo_wait_close(&waitend));
  lcrzo_er(lcrzo_wait_close(&waithand));

  if (eventend)
  { lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_TRUE));
    /* because a premature end of a process should not return LCRZO_ERR_OK */
    return(lcrzo_ierp("lcrzo_priv_sock_waitendhandshake"));
  }
  else if (eventhand)
  { lcrzo_er(lcrzo_ipc_read_uint32(&(psock->ipccontrol1), LCRZO_WAIT_INFINITE,
				 &childret));
    if ( childret!=LCRZO_ERR_OK )
    { /* wait the end of the child */
      lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_TRUE));
      /* return the error of the child */
      return(childret);
    }
  }
  else
  { return(lcrzo_ierp("lcrzo_priv_sock_waitendhandshake - infinite"));
  }

  /* get who is the client */
  if (psock->type==LCRZO_PRIV_TYPESOCK_VIRTSERTCP)
  {  lcrzo_er(lcrzo_ipc_read_ethaiplport(&(psock->ipccontrol1), LCRZO_WAIT_INFINITE,
				         psock->there_etha,
				         &(psock->there_ipl),
				         &(psock->there_port)));
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_sock_spiot(lcrzo_sock *psock,
			  lcrzo_bool bitsyn, lcrzo_bool bitack, 
			  lcrzo_bool bitfin, lcrzo_bool bitrst,
			  lcrzo_uint32 seqnum,
			  lcrzo_uint32 acknum,
			  lcrzo_uint16 window,
			  lcrzo_data tcpdata,  
			  lcrzo_uint16 tcpdatasize);
int lcrzo_priv_sock_spiot(lcrzo_sock *psock,
			  lcrzo_bool bitsyn, lcrzo_bool bitack, 
			  lcrzo_bool bitfin, lcrzo_bool bitrst,
			  lcrzo_uint32 seqnum,
			  lcrzo_uint32 acknum,
			  lcrzo_uint16 window,
			  lcrzo_data tcpdata,  
			  lcrzo_uint16 tcpdatasize)
{ lcrzo_hdrleth hdrleth;
  lcrzo_hdrlip  hdrlip;
  lcrzo_hdrltcp hdrltcp;
  int ret;
  lcrzo_data ipopt;
  lcrzo_int32 ipoptsize;
  lcrzo_spoof spoof;

  /*initialisation de l'entete ETH*/
  lcrzo_er(lcrzo_hdrleth_initdefault(&hdrleth));
  memcpy(hdrleth.src, psock->here_etha, LCRZO_ETHA_MAXBYTES);
  memcpy(hdrleth.dst, psock->there_etha, LCRZO_ETHA_MAXBYTES);
  /*initialisation de l'entete IP*/
  lcrzo_er(lcrzo_hdrlip_initdefault(&hdrlip));
  hdrlip.saddr=psock->here_ipl;
  hdrlip.daddr=psock->there_ipl;
  /*initialisation de l'entete TCP*/
  lcrzo_er(lcrzo_hdrltcp_initdefault(&hdrltcp));
  hdrltcp.sport=psock->here_port;
  hdrltcp.dport=psock->there_port;
  hdrltcp.seqnum=seqnum;
  hdrltcp.acknum=acknum;
  hdrltcp.window=window;
  hdrltcp.syn=(lcrzo_uint8)bitsyn;
  hdrltcp.ack=(lcrzo_uint8)bitack;
  hdrltcp.fin=(lcrzo_uint8)bitfin;
  hdrltcp.rst=(lcrzo_uint8)bitrst;

  /*mise a jour eventuelle des options IP*/
  ret=lcrzo_ipc_readm(&(psock->ipcoptions), LCRZO_WAIT_NODELAY, &ipopt, &ipoptsize);
  if ( ret==LCRZO_ERR_OK )
  { if (ipoptsize>LCRZO_IPOPT_MAXBYTES)
      return(lcrzo_ie("lcrzo_priv_sock_spiot",
		      "ipoptsize lu depuis l'IPC est > LCRZO_IPOPT_MAXBYTES",
		      "ipoptsize read from IPC is > LCRZO_IPOPT_MAXBYTES"));
    memcpy(psock->ipopt, ipopt, ipoptsize);
    psock->ipoptsize=(lcrzo_uint8)(ipoptsize);
    lcrzo_data_free(ipopt);
  }

  /*envoi d'un packet avec donnees*/
  lcrzo_er(lcrzo_spoof_init(&spoof));
  lcrzo_er(lcrzo_spoof_ethipopttcpoptdata(&spoof, psock->here_device, hdrleth,
					  hdrlip, psock->ipopt,
					  psock->ipoptsize,
					  hdrltcp, NULL, 0,
					  tcpdata, tcpdatasize));
  lcrzo_er(lcrzo_spoof_close(&spoof));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_priv_sock_tcpstate_sniff(lcrzo_sock *psock, 
				   lcrzo_sniff *psniff,
				   lcrzo_bool knowclient,
				   lcrzo_bool *ppacketforus,
				   lcrzo_bool *psyn,
				   lcrzo_bool *pack,
				   lcrzo_bool *pfin,
				   lcrzo_bool *prst,
				   lcrzo_uint32 *pseqnum,
				   lcrzo_uint32 *packnum,
				   lcrzo_uint16 *pwindow,
				   lcrzo_data *pdata,
				   lcrzo_uint16 *pdatasize);
int lcrzo_priv_sock_tcpstate_sniff(lcrzo_sock *psock, 
				   lcrzo_sniff *psniff,
				   lcrzo_bool knowclient,
				   lcrzo_bool *ppacketforus,
				   lcrzo_bool *psyn,
				   lcrzo_bool *pack,
				   lcrzo_bool *pfin,
				   lcrzo_bool *prst,
				   lcrzo_uint32 *pseqnum,
				   lcrzo_uint32 *packnum,
				   lcrzo_uint16 *pwindow,
				   lcrzo_data *pdata,
				   lcrzo_uint16 *pdatasize)
{ lcrzo_hdrleth hdrleth;
  lcrzo_hdrlip hdrlip;
  lcrzo_hdrltcp hdrltcp;
  lcrzo_data ethdata;
  lcrzo_int32 ethdatasize;
  lcrzo_bool sock_virt_answeralive;
  int ret;

  /* sniff a packet */
  ret=lcrzo_sniff_nextm(psniff, LCRZO_WAIT_NODELAY,
			&ethdata, &ethdatasize);
  if ( ret==LCRZO_ERR_OKTEMPDATAEND )
  { *ppacketforus=LCRZO_FALSE;
    return(LCRZO_ERR_OK);
  }
  else if ( ret!=LCRZO_ERR_OK )
  { return(ret);
  }

  /* answer for the virtual computer */
  lcrzo_er(lcrzo_global_get_sock_virt_answeralive(&sock_virt_answeralive));
  if (sock_virt_answeralive)
  { lcrzo_er(lcrzo_virtual_answer_alive(ethdata, ethdatasize,
					psock->here_device, 
					psock->here_etha,
					psock->here_ipl));
  }

  /* try to decode this packet as tcp */
  ret=lcrzo_packet_decodem_ethipopttcpoptdata(ethdata, ethdatasize,
					      &hdrleth, 
					      &hdrlip, NULL, NULL,
					      &hdrltcp, NULL, NULL, 
					      pdata, pdatasize);
  lcrzo_data_free(ethdata);
  *ppacketforus=LCRZO_FALSE;
  if ( ret==LCRZO_ERR_OK) 
  { /* set values if the sniffed packet is for us */
    if ( lcrzo_etha_equal(hdrleth.dst, psock->here_etha) &&
	 hdrlip.daddr == psock->here_ipl &&
	 hdrltcp.dport == psock->here_port &&
	 ( !knowclient ||
	   ( lcrzo_etha_equal(hdrleth.src, psock->there_etha) &&
	     hdrlip.saddr == psock->there_ipl &&
	     hdrltcp.sport == psock->there_port 
	   )
	 )
       )
    { *ppacketforus=LCRZO_TRUE;
      *psyn=hdrltcp.syn;	  
      *pack=hdrltcp.ack;	  
      *pfin=hdrltcp.fin;	  
      *prst=hdrltcp.rst;	  
      *pseqnum=hdrltcp.seqnum;	  
      *packnum=hdrltcp.acknum;
      *pwindow=hdrltcp.window;
      /* if we do not know client, we set its value */
      if ( !knowclient )
      { memcpy(psock->there_etha, hdrleth.src, LCRZO_ETHA_MAXBYTES);
        psock->there_ipl=hdrlip.saddr;
	psock->there_port=hdrltcp.sport;
      }
    }
    else
    { /* if the packet wasn't for us, we have to free pdata */
      lcrzo_data_free(*pdata);
    }
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
#define LCRZO_DEBUG_TCPSTATE 0
int lcrzo_priv_sock_tcpstate(lcrzo_sock *psock, lcrzo_sniff *psniff);
int lcrzo_priv_sock_tcpstate(lcrzo_sock *psock, lcrzo_sniff *psniff)
{ lcrzo_bool sn, sp, sn_packetforus;
  lcrzo_bool sn_syn, sn_ack, sn_fin, sn_rst,  sp_syn, sp_ack, sp_fin, sp_rst;
  lcrzo_uint32 sn_seqnum, sn_acknum,  sp_seqnum, sp_acknum;
  lcrzo_uint32 sn_currentseqnum, sp_currentseqnum;
  lcrzo_data sn_data,  sp_data;
  lcrzo_uint16 sn_datasize, sn_window,  sp_datasize, sp_window;
  lcrzo_int32 sp_datasizeint32;
  lcrzo_time timeouttimer;
  lcrzo_uint32 usec, isn;
  lcrzo_bool spoofedsomething, knowclient;
  lcrzo_bool cansniff, canspoof, canend, readytosniff;
  lcrzo_wait waitsniff, waitspoof, waitend;
  int numberofsynsent, ret, counterforestablishedloop;

  /* initialize needed values */
  lcrzo_er(lcrzo_uint32_init_rand(0, 0xFFFFFFFFu, &isn));
  numberofsynsent=0;
  sp_window=1500;
  knowclient=LCRZO_TRUE;

  /* just for the compiler because "might be used uninitialized" */
  sn=0; sp=0; sp_syn=0; sp_ack=0; sp_fin=0; sp_rst=0; sp_seqnum=0; sp_acknum=0;
  sp_data=NULL; sp_datasize=0; sn_currentseqnum=0; sp_currentseqnum=0;
  sn_packetforus=LCRZO_FALSE; sn_data=NULL; sn_syn=LCRZO_FALSE;
  sn_ack=LCRZO_FALSE; sn_fin=LCRZO_FALSE; sn_rst=LCRZO_FALSE;
  sn_seqnum=0; sn_acknum=0; sn_datasize=0;
  counterforestablishedloop=0; sn_window=0; spoofedsomething=0;
  readytosniff=LCRZO_FALSE;
  cansniff=LCRZO_FALSE; canspoof=LCRZO_FALSE; canend=LCRZO_FALSE;

  /* main loop */
  while (1) {
    /* determine what to do in a given state */
    /*printf("Current state : %d\n", psock->tcpstate);*/

    switch(psock->tcpstate) {

    case LCRZO_PRIV_SOCK_TCPSTATE_SYNSENT_INIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_SYNSENT_INIT");
      #endif
      sn=LCRZO_FALSE;     /* no sniff */
      sp=LCRZO_TRUE;      /* spoof */
      sp_syn=LCRZO_TRUE;  /* syn */
      sp_ack=LCRZO_FALSE; /* no ack */
      sp_fin=LCRZO_FALSE; /* no fin */
      sp_rst=LCRZO_FALSE; /* no rst */
      sp_currentseqnum=isn;
      sp_seqnum=sp_currentseqnum;
      sp_acknum=0;
      sp_data=NULL;       /* no data */
      sp_datasize=0;      /* no data */
      lcrzo_er(lcrzo_time_init_currenttime(&timeouttimer));
      numberofsynsent++;
      psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_SYNSENT;
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_SYNSENT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_SYNSENT");
      #endif
      /* change from spoof to sniff */
      if (sp) {
        sn=LCRZO_TRUE;      /* sniff */
	sp=LCRZO_FALSE;     /* no spoof */
	break;              /* change nothing else */
      }
      /* if no answer is received after 1 second...*/
      lcrzo_er(lcrzo_time_diffcurrent_usec(&timeouttimer, &usec));
      if (usec>1000000)
      { if ( numberofsynsent<3 )
	{ /* we send a new syn */
	  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_SYNSENT_INIT;
	}
	else
	{ /* the fourth time : abort */
	  return(LCRZO_ERR_SPSOCKTCPNOSA);
	}
	break;              /* continue */
      }
      /* the packet is not for us */
      if (!sn_packetforus) {
	break;              /* continue */
      }
      else
      { /* in this state, we do not need data */
	lcrzo_data_free(sn_data);
      }
      /* the server sent us a reset */
      if ( (!sn_syn) && sn_ack && (!sn_fin) && sn_rst && 
	   sn_seqnum==0 && sn_acknum==sp_currentseqnum+1 )
      { return(LCRZO_ERR_SPSOCKTCPRST);
      }
      /* the server sent us a synack */
      else if ( sn_syn && sn_ack && (!sn_fin) && (!sn_rst) && 
		sn_acknum==sp_currentseqnum+1 )
      { /* save numbers*/
        sn_currentseqnum=sn_seqnum+1;
        sp_currentseqnum++;
	/* change state and sp */
	sn=LCRZO_FALSE;     /* no sniff */
	sp=LCRZO_TRUE;      /* spoof */
	sp_syn=LCRZO_FALSE;  /* no syn */
	sp_ack=LCRZO_TRUE; /* ack */
	sp_fin=LCRZO_FALSE; /* no fin */
	sp_rst=LCRZO_FALSE; /* no rst */
	sp_seqnum=sp_currentseqnum;
	sp_acknum=sn_currentseqnum;
	sp_data=NULL;       /* no data */
	sp_datasize=0;      /* no data */
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_ESTABLISHED_INIT;
	lcrzo_er(lcrzo_ipc_write_uint32(&psock->ipccontrol1, LCRZO_ERR_OK));
      }
      else
      { /* ignore other kinds */
      }
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_ESTABLISHED_INIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_ESTABLISHED_INIT");
      #endif
      /* this state permits to initialize variables */
      cansniff=LCRZO_FALSE; canspoof=LCRZO_FALSE; canend=LCRZO_FALSE;
      readytosniff=LCRZO_FALSE;
      psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_ESTABLISHED;
      /*no break*/
    case LCRZO_PRIV_SOCK_TCPSTATE_ESTABLISHED:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_ESTABLISHED");
      #endif
      sn=LCRZO_FALSE;     /* no sniff */
      sp=LCRZO_FALSE;     /* no spoof */
      if ( ! ( cansniff && readytosniff ) )
      { /* watch sniff, spoof and control */
        #if LCRZO_DEBUG_TCPSTATE
          puts("TCPSTATE_ESTABLISHED : wait with select");
        #endif
        lcrzo_er(lcrzo_wait_init_sniff_read(psniff, &waitsniff));
        lcrzo_er(lcrzo_wait_init_ipc_read(&psock->ipcspoof, &waitspoof));
        lcrzo_er(lcrzo_wait_init_ipc_read(&psock->ipcsniffend, &waitend));
        lcrzo_er(lcrzo_wait_wait3(&waitsniff, &waitspoof, &waitend,
                                  LCRZO_WAIT_INFINITE,
				  &cansniff, &canspoof, &canend));
        lcrzo_er(lcrzo_wait_close(&waitsniff));
        lcrzo_er(lcrzo_wait_close(&waitspoof));
        lcrzo_er(lcrzo_wait_close(&waitend));
        #if LCRZO_DEBUG_TCPSTATE
	  printf("cansniff=%d  canspoof=%d  canend=%d\n", 
		 cansniff, canspoof, canend);
        #endif
      }

      /*-------*/
      if ( canspoof )
      { /* we spoof user's data */
        ret=lcrzo_ipc_readm(&(psock->ipcspoof), LCRZO_WAIT_NODELAY, 
			    &sp_data, &sp_datasizeint32);
	if (ret==LCRZO_ERR_OK)
	{ sp=LCRZO_TRUE;      /* spoof */
	  sp_syn=LCRZO_FALSE;  /* no syn */
	  sp_ack=LCRZO_TRUE; /* ack */
	  sp_fin=LCRZO_FALSE; /* no fin */
	  sp_rst=LCRZO_FALSE; /* no rst */
	  sp_seqnum=sp_currentseqnum;
	  sp_acknum=sn_currentseqnum;
	  sp_datasize=(lcrzo_uint16)sp_datasizeint32;
	  sp_currentseqnum+=sp_datasize; /* if >0xFFFFFFFF, pass to 0*/
	}
	break;
      }
      /*-------*/
      else if ( cansniff && !readytosniff )
      { readytosniff=LCRZO_TRUE; 
	/* we are going to sniff */
	sn=LCRZO_TRUE;     /* sniff */
	break;
      }
      /*-------*/
      else if ( cansniff && readytosniff )
      { readytosniff=LCRZO_FALSE;
        /* we analyze what we've just sniffed */ 
	if (!sn_packetforus) {
	  /* the packet is not for us */
	  break;              /* continue */
	}
	/* packet contains a syn */
	if (sn_syn) {
	  lcrzo_data_free(sn_data);
	  break;              /* ignore */
	}
	/* packet contains a rst */
	if (sn_rst) {
	  lcrzo_data_free(sn_data);
	  if (sn_seqnum==sn_currentseqnum)
	  { psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	  }
	  break;
	}
	/* packet contains a fin */
	if (sn_fin) {
	  lcrzo_data_free(sn_data);
	  if (sn_seqnum==sn_currentseqnum) {
	    /* send an ack */
	    sp=LCRZO_TRUE;      /* spoof */
	    sp_syn=LCRZO_FALSE;  /* no syn */
	    sp_ack=LCRZO_TRUE; /* ack */
	    sp_fin=LCRZO_FALSE; /* no fin */
	    sp_rst=LCRZO_FALSE; /* no rst */
	    sp_seqnum=sp_currentseqnum;
	    sn_currentseqnum++;
	    sp_acknum=sn_currentseqnum;
	    sp_data=NULL;       /* no data */
	    sp_datasize=0;      /* no data */
	    psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSEWAIT;
	  }
	  break;
	}
	if ( (!sn_syn) && sn_ack && (!sn_fin) && (!sn_rst) )
        { sn_currentseqnum+=sn_datasize;
	  /* give data to the user */
          lcrzo_er(lcrzo_ipc_write_msg(&(psock->ipcsniff), 
				       sn_data, sn_datasize));
	  lcrzo_data_free(sn_data);
	  /* we are going to spoof (ack for this packet) */
	  sp=LCRZO_TRUE;    /* spoof */
	  sp_syn=LCRZO_FALSE;  /* no syn */
	  sp_ack=LCRZO_TRUE; /* ack */
	  sp_fin=LCRZO_FALSE; /* no fin */
	  sp_rst=LCRZO_FALSE; /* no rst */
	  sp_seqnum=sp_currentseqnum;
	  sp_acknum=sn_currentseqnum;
	  sp_data=NULL;       /* no data */
	  sp_datasize=0;      /* no data */
	}
	else
        { lcrzo_data_free(sn_data);
	  break;  /* ignore this paquet (for example a NUL packet)*/
	}
	break;
      }
      /*-------*/
      else if ( canend )
      { /* we look if parent process want us to stop */
        /* see if parent sent us something */
        ret=lcrzo_ipc_read_uint16(&(psock->ipcsniffend), LCRZO_WAIT_NODELAY, NULL);
	if ( ret==LCRZO_ERR_OK )
	{ /* send a fin */
	  sp=LCRZO_TRUE;      /* spoof */
	  sp_syn=LCRZO_FALSE; /* no syn */
	  sp_ack=LCRZO_TRUE; /* ack */
	  sp_fin=LCRZO_TRUE;  /* fin */
	  sp_rst=LCRZO_FALSE; /* no rst */
	  sp_seqnum=sp_currentseqnum;
	  sp_acknum=sn_currentseqnum;
	  sp_data=NULL;
	  sp_datasize=0;
	  sp_currentseqnum++; /* if >0xFFFFFFFF, pass to 0*/
          psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT1_INIT;
	}
	break;
      }
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_CLOSEWAIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_CLOSEWAIT");
      #endif
      ret=lcrzo_ipc_readm(&(psock->ipcspoof), LCRZO_WAIT_NODELAY, 
			  &sp_data, &sp_datasizeint32);
      if (ret==LCRZO_ERR_OK) { 
	/* continue to send user's data */
	sn=LCRZO_FALSE;      /* no sniff */
	sp=LCRZO_TRUE;      /* spoof */
	sp_syn=LCRZO_FALSE;  /* no syn */
	sp_ack=LCRZO_TRUE; /* ack */
	sp_fin=LCRZO_FALSE; /* no fin */
	sp_rst=LCRZO_FALSE; /* no rst */
	sp_seqnum=sp_currentseqnum;
	sp_acknum=sn_currentseqnum;
	sp_datasize=(lcrzo_uint16)sp_datasizeint32;
	sp_currentseqnum+=sp_datasize; /* if >0xFFFFFFFF, pass to 0*/
      }
      else {
	/* send a fin */
	sn=LCRZO_FALSE;      /* no sniff */
	sp=LCRZO_TRUE;      /* spoof */
	sp_syn=LCRZO_FALSE;  /* no syn */
	sp_ack=LCRZO_TRUE;  /* ack */
	sp_fin=LCRZO_TRUE; /* fin */
	sp_rst=LCRZO_FALSE; /* no rst */
	sp_seqnum=sp_currentseqnum;
	sp_acknum=sn_currentseqnum;
	sp_data=NULL;
	sp_datasize=0;
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_LASTACK_INIT;
      }
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_LASTACK_INIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_LASTACK_INIT");
      #endif
      /* this state permits to initialize variables */
      sn=LCRZO_TRUE;      /* sniff */
      sp=LCRZO_FALSE;     /* no spoof */
      psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_LASTACK;
      lcrzo_er(lcrzo_time_init_currenttime(&timeouttimer));
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_LASTACK:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_LASTACK");
      #endif
      /* we sniff to have the ack of our fin */
      /* if no answer is received after 1 second...*/
      lcrzo_er(lcrzo_time_diffcurrent_usec(&timeouttimer, &usec));
      if (usec>1000000)
      { /* jump to closed state */
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	if (sn_packetforus) lcrzo_data_free(sn_data);
	break;
      }
      /* look at what we've sniffed */
      if (!sn_packetforus) {
	/* the packet is not for us */
	break;              /* continue */
      }
      /* packet contains a rst */
      if ( sn_rst ) {
	if (sn_seqnum==sn_currentseqnum) { 
	  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	  lcrzo_data_free(sn_data);
	  break;
	}
      }
      /* packet contains an ack */
      if ( sn_ack ) {
	if (sn_acknum==sp_currentseqnum+1) { 
	  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	  lcrzo_data_free(sn_data);
	  break;
	}
      }
      /* ignore other kinds of packets */
      lcrzo_data_free(sn_data);
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT1_INIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_FINWAIT1_INIT");
      #endif
      /* this state permits to initialize variables */
      sn=LCRZO_TRUE;      /* sniff */
      sp=LCRZO_FALSE;     /* no spoof */
      psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT1;
      lcrzo_er(lcrzo_time_init_currenttime(&timeouttimer));
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT1:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_FINWAIT1");
      #endif
      /* if no answer is received after 1 second...*/
      lcrzo_er(lcrzo_time_diffcurrent_usec(&timeouttimer, &usec));
      if (usec>1000000)
      { /* jump to closed state */
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	if (sn_packetforus) lcrzo_data_free(sn_data);
	break;
      }
      /* look at what we've sniffed */
      if (!sn_packetforus) {
	/* the packet is not for us */
	break;              /* continue */
      }
      /* packet contains a rst */
      if ( sn_rst ) {
	if (sn_seqnum==sn_currentseqnum) { 
	  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	  lcrzo_data_free(sn_data);
	  break;
	}
      }
      /* packet contains an ack */
      if ( sn_ack ) {
	if (sn_acknum==sp_currentseqnum) { 
	  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT2_INIT;
	  lcrzo_data_free(sn_data);
	  sn=LCRZO_FALSE;     /* no sniff */
	  sp=LCRZO_FALSE;     /* no spoof */
	  break;
	}
      }
      /* packet contains a fin */
      if ( sn_fin ) {
	lcrzo_data_free(sn_data);
	sn=LCRZO_FALSE;      /* no sniff */
	sp=LCRZO_TRUE;      /* spoof */
	sp_syn=LCRZO_FALSE;  /* no syn */
	sp_ack=LCRZO_TRUE;  /* ack */
	sp_fin=LCRZO_FALSE; /* no fin */
	sp_rst=LCRZO_FALSE; /* no rst */
	sp_seqnum=sp_currentseqnum;
	sn_currentseqnum=sn_seqnum; /* user asked to close so we do not
				     give him data between sn_currentseqnum
				     and sn_seqnum */
	sp_acknum=sn_currentseqnum+1;
	sp_data=NULL;
	sp_datasize=0;
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSING_INIT;
	break;
      }
      /* ignore other kinds of packets */
      lcrzo_data_free(sn_data);
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT2_INIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_FINWAIT2_INIT");
      #endif
      /* this state permits to initialize variables */
      sn=LCRZO_TRUE;      /* sniff */
      sp=LCRZO_FALSE;     /* no spoof */
      psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT2;
      lcrzo_er(lcrzo_time_init_currenttime(&timeouttimer));
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_FINWAIT2 :
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_FINWAIT2");
      #endif
      /* if no answer is received after 1 second...*/
      lcrzo_er(lcrzo_time_diffcurrent_usec(&timeouttimer, &usec));
      if (usec>1000000)
      { /* jump to closed state */
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	if (sn_packetforus) lcrzo_data_free(sn_data);
	break;
      }
      /* look at what we've sniffed */
      if (!sn_packetforus) {
	/* the packet is not for us */
	break;              /* continue */
      }
      /* packet contains a rst */
      if ( sn_rst ) {
	if (sn_seqnum==sn_currentseqnum) { 
	  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	  lcrzo_data_free(sn_data);
	  break;
	}
      }
      /* packet contains a fin */
      if ( sn_fin ) {
	lcrzo_data_free(sn_data);
	sn=LCRZO_FALSE;      /* no sniff */
	sp=LCRZO_TRUE;      /* spoof */
	sp_syn=LCRZO_FALSE;  /* no syn */
	sp_ack=LCRZO_TRUE;  /* ack */
	sp_fin=LCRZO_FALSE; /* no fin */
	sp_rst=LCRZO_FALSE; /* no rst */
	sp_seqnum=sp_currentseqnum;
	sn_currentseqnum=sn_seqnum; /* user asked to close so we do not
				     give him data between sn_currentseqnum
				     and sn_seqnum */
	sp_acknum=sn_currentseqnum+1;
	sp_data=NULL;
	sp_datasize=0;
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSING_INIT;
	break;
      }
      /* ignore other kinds of packets */
      lcrzo_data_free(sn_data);
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_CLOSING_INIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_CLOSING_INIT");
      #endif
      /* this state permits to initialize variables */
      sn=LCRZO_TRUE;      /* sniff */
      sp=LCRZO_FALSE;     /* no spoof */
      psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSING;
      lcrzo_er(lcrzo_time_init_currenttime(&timeouttimer));
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_CLOSING:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_CLOSING");
      #endif
      /* if no answer is received after 1 second...*/
      lcrzo_er(lcrzo_time_diffcurrent_usec(&timeouttimer, &usec));
      if (usec>1000000)
      { /* jump to closed state */
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	if (sn_packetforus) lcrzo_data_free(sn_data);
	break;
      }
      /* look at what we've sniffed */
      if (!sn_packetforus) {
	/* the packet is not for us */
	break;              /* continue */
      }
      /* packet contains a rst */
      if ( sn_rst ) {
	if (sn_seqnum==sn_currentseqnum) { 
	  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
	  lcrzo_data_free(sn_data);
	  break;
	}
      }
      /* packet contains an ack */
      if ( sn_ack ) {
	if (sn_acknum==sp_currentseqnum) { 
	  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_TIMEWAIT;
	  lcrzo_data_free(sn_data);
	  break;
	}
      }
      /* ignore other kinds of packets */
      lcrzo_data_free(sn_data);
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_TIMEWAIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_TIMEWAIT");
      #endif
      sn=LCRZO_FALSE;      /* no sniff */
      sp=LCRZO_FALSE;      /* no spoof */
      psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_CLOSED;
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_LISTEN_INIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("CPSTATE_LISTEN_INIT");
      #endif
      sn=LCRZO_TRUE;      /* sniff */
      sp=LCRZO_FALSE;      /* no spoof */
      psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_LISTEN;
      knowclient=LCRZO_FALSE;
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_LISTEN:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_LISTEN");
      #endif
      knowclient=LCRZO_FALSE;
      /* look at what we've sniffed */
      if (!sn_packetforus) {
	/* the packet is not for us */
	break;              /* continue */
      }
      /* packet contains a syn */
      if ( sn_syn && (!sn_ack) && (!sn_fin) && (!sn_rst) ) {
	lcrzo_data_free(sn_data);
	sn=LCRZO_FALSE;      /* no sniff */
	sp=LCRZO_TRUE;      /* spoof */
	sp_syn=LCRZO_TRUE;  /* syn */
	sp_ack=LCRZO_TRUE;  /* ack */
	sp_fin=LCRZO_FALSE; /* no fin */
	sp_rst=LCRZO_FALSE; /* no rst */
	sp_currentseqnum=isn;
	sn_currentseqnum=sn_seqnum+1;
	sp_seqnum=sp_currentseqnum;
	sp_acknum=sn_currentseqnum;
	sp_data=NULL;
	sp_datasize=0;
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_SYNRECEIVED_INIT;
	break;
      }
      /* ignore other kinds of packets */
      lcrzo_data_free(sn_data);
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_SYNRECEIVED_INIT:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_SYNRECEIVED_INIT");
      #endif
      sn=LCRZO_TRUE;      /* sniff */
      sp=LCRZO_FALSE;      /* no spoof */
      psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_SYNRECEIVED;
      lcrzo_er(lcrzo_time_init_currenttime(&timeouttimer));
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_SYNRECEIVED :
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_SYNRECEIVED");
      #endif
      /* if no answer is received after 1 second...*/
      lcrzo_er(lcrzo_time_diffcurrent_usec(&timeouttimer, &usec));
      if (usec>1000000)
      { /* jump to listen state */
	psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_LISTEN_INIT;
	if (sn_packetforus) lcrzo_data_free(sn_data);
	break;
      }
      /* look at what we've sniffed */
      if (!sn_packetforus) {
	/* the packet is not for us */
	break;              /* continue */
      }
      /* packet contains an ack */
      if ( (!sn_syn) && sn_ack && (!sn_fin) && (!sn_rst) ) {
	lcrzo_data_free(sn_data);
	if ( sn_acknum==sp_currentseqnum+1 )
	{ sn=LCRZO_FALSE;      /* no sniff */
	  sp=LCRZO_TRUE;      /* no spoof */
	  sp_currentseqnum++;
	  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_ESTABLISHED_INIT;
	  /* inform parent process */
	  lcrzo_er(lcrzo_ipc_write_uint32(&psock->ipccontrol1, LCRZO_ERR_OK));
          lcrzo_er(lcrzo_ipc_write_ethaiplport(&(psock->ipccontrol1),
					       psock->there_etha,
					       psock->there_ipl,
					       psock->there_port));
	}
	break;
      }
      /* ignore other kinds of packets */
      lcrzo_data_free(sn_data);
      break;

    case LCRZO_PRIV_SOCK_TCPSTATE_CLOSED:
      #if LCRZO_DEBUG_TCPSTATE
        puts("TCPSTATE_CLOSED");
      #endif
      return(LCRZO_ERR_OK);
      break;

    default:
      return(lcrzo_ieuv("lcrzo_priv_sock_tcpstate", "psock->tcpstate"));
    }

    /* check sn and sp */
    if (sn&&sp) {
      return(lcrzo_ie("lcrzo_priv_sock_tcpstate", 
		      "sn et sp sont affectes",
		      "sn and sp both set"));
    }
  
    /* sniff */
    if (sn) {  
      lcrzo_er(lcrzo_priv_sock_tcpstate_sniff(psock, psniff, knowclient,
					      &sn_packetforus,
					      &sn_syn,&sn_ack,&sn_fin,&sn_rst,
					      &sn_seqnum, &sn_acknum,
					      &sn_window,
					      &sn_data, &sn_datasize));
    }

    /* spoof */
    if (sp) { 
      /* spoof a packet */
      lcrzo_er(lcrzo_priv_sock_spiot(psock, sp_syn, sp_ack, sp_fin, sp_rst,
				     sp_seqnum, sp_acknum, sp_window,
				     sp_data, sp_datasize));
      if (sp_data!=NULL) lcrzo_data_free(sp_data);
    }

  }

  /* should never reach this point */
  return(lcrzo_ierp("lcrzo_priv_sock_tcpstate"));
}

/* Fin zone de fonctions privees */
/***************************************************************/

/*-------------------------------------------------------------*/
int lcrzo_sock_udpcli_real(lcrzo_ipl    ipl, 
			  lcrzo_uint16 port,
			  lcrzo_sock   *psock)
{ return(lcrzo_sock_udpcli_real2(0, ipl, 0, port, NULL, 0, psock));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_udpcli_virt(const lcrzo_device device,
			   const lcrzo_etha ethaclient,
			   const lcrzo_etha ethaserveur,
			   lcrzo_ipl iplclient,
			   lcrzo_ipl iplserveur, 
			   lcrzo_uint16 portclient,
			   lcrzo_uint16 portserveur,
			   lcrzo_sock *psock)
{ return(lcrzo_sock_udpcli_virt2(device, ethaclient, ethaserveur,
				 iplclient, iplserveur,
				 portclient, portserveur,
				 NULL, 0,
				 psock));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_udpcli_real2(lcrzo_ipl    iplclient,
			    lcrzo_ipl    iplserveur, 
			    lcrzo_uint16 portclient,
			    lcrzo_uint16 portserveur,
			    const lcrzo_ipopt ipopt,
			    lcrzo_uint8 ipoptsize,
			    lcrzo_sock   *psock)
{ struct sockaddr_in cli;
  int retour, sock;

  /*parameters verification*/
  if (ipopt==NULL && ipoptsize) return(LCRZO_ERR_PANULLPTRSIZE);

  /*creation de la socket*/
  sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if (sock<0) return(LCRZO_ERR_FUSOCKET);

  /*ajout eventuel des options IP*/
  if (ipoptsize)
  { retour = LAURENT_setsockopt(sock, IPPROTO_IP, IP_OPTIONS, 
				ipopt, ipoptsize);
    if (retour)
      return(LCRZO_ERR_FUSETSOCKOPT);
  }

  /*le client specifie des infos locales*/
  if ( iplclient || portclient )
  { memset(&cli, 0, sizeof(cli));
    cli.sin_family = AF_INET;
    cli.sin_addr.s_addr=lcrzo_htonl(iplclient);
    cli.sin_port = lcrzo_htons(portclient);
    retour=bind(sock, (struct sockaddr *)&cli, sizeof(cli));
    if (retour<0) return(LCRZO_ERR_FUBIND);
  }

  /*connexion sur le serveur*/
  memset(&cli, 0, sizeof(cli));
  cli.sin_family = AF_INET;
  cli.sin_addr.s_addr=lcrzo_htonl(iplserveur);
  cli.sin_port = lcrzo_htons(portserveur);
  retour=connect(sock, (struct sockaddr *)&cli, sizeof(cli));
  if (retour<0) return(LCRZO_ERR_FUCONNECT);

  /*if the user only want to test, its ok, so we close*/
  if (psock==NULL)
  { retour=LAURENT_closesocket(sock);
    if ( retour == -1 ) return(LCRZO_ERR_FUCLOSE);
    return(LCRZO_ERR_OK);
  }

  /*remplissage des caracteristiques de la socket*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_REALCLIUDP;
  psock->socknum=sock;
  psock->there_ipl=iplserveur;
  psock->there_port=portserveur;
  psock->here_ipl=iplclient;
  psock->here_port=portclient;
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_udpcli_virt2_sniff(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess);
int lcrzo_sock_udpcli_virt2_sniff(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess)
{ lcrzo_sock sock;

  /* copy socket data */
  if (infossize!=sizeof(sock))
  { return(LCRZO_ERR_IEINTERNALERROR);
  }
  memcpy(&sock, infos, infossize);

  /* close the process data */
  lcrzo_er(lcrzo_process_close(pparentprocess));

  /* close unneeded IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcspoof));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcspoofend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcoptions));

  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcsniffend)));
  
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcspoofend)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcspoofend)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcoptions)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcoptions)));
  
  /* sniff function */
  lcrzo_er(lcrzo_priv_udp_sniffprocess(&sock));
  
  /* end */
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcspoof));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcspoofend));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcoptions));
  return(LCRZO_ERR_OK);
}
int lcrzo_sock_udpcli_virt2_spoof(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess);
int lcrzo_sock_udpcli_virt2_spoof(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess)
{ lcrzo_sock sock;

  /* copy socket data */
  if (infossize!=sizeof(sock))
  { return(LCRZO_ERR_IEINTERNALERROR);
  }
  memcpy(&sock, infos, infossize);

  /* close the process data */
  lcrzo_er(lcrzo_process_close(pparentprocess));

  /* close unneeded IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcspoof));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcspoofend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcoptions));

  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcspoofend)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcoptions)));
  
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcsniffend)));

  /* spoof function */
  lcrzo_er(lcrzo_priv_udp_spoofprocess(&sock));
  
  /* end */
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcspoof));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcspoofend));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcoptions));
  return(LCRZO_ERR_OK);
}
int lcrzo_sock_udpcli_virt2(const lcrzo_device device,
			    const lcrzo_etha ethaclient,
			    const lcrzo_etha ethaserveur,
			    lcrzo_ipl iplclient,
			    lcrzo_ipl iplserveur, 
			    lcrzo_uint16 portclient,
			    lcrzo_uint16 portserveur,
			    const lcrzo_ipopt ipopt,
			    lcrzo_uint8 ipoptsize,
			    lcrzo_sock *psock)
{ 
  /*parameters verification*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  if (ethaserveur==NULL) return(LCRZO_ERR_PANULLPTR);
  if (ethaclient==NULL) return(LCRZO_ERR_PANULLPTR);
  if (ipopt==NULL && ipoptsize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  /*initialisations*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_VIRTCLIUDP;
  lcrzo_er(lcrzo_priv_sock_infos_init(device, ethaclient, iplclient, portclient,
				    ethaserveur, iplserveur, portserveur,
				    psock));
  lcrzo_er(lcrzo_priv_sock_ipc_init(psock));

  /* sniff process */
  lcrzo_er(lcrzo_process_other_init(&lcrzo_sock_udpcli_virt2_sniff,
                                    (lcrzo_data)psock, sizeof(lcrzo_sock), 
				    &psock->sniffprocess));

  /* spoof process */
  lcrzo_er(lcrzo_process_other_init(&lcrzo_sock_udpcli_virt2_spoof,
                                    (lcrzo_data)psock, sizeof(lcrzo_sock), 
				    &psock->spoofprocess));

  /* close unnecessary IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcspoof));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcspoofend));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcoptions));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcspoofend)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcoptions)));

  /* send options */
  lcrzo_er(lcrzo_sock_ipopt_set(psock, ipopt, ipoptsize));
  
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_tcpcli_real(lcrzo_ipl    ipl,
			  lcrzo_uint16 port, 
			  lcrzo_sock   *psock)
{ return(lcrzo_sock_tcpcli_real2(0, ipl, 0, port, NULL, 0, psock));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_tcpcli_virt(const lcrzo_device device,
			   const lcrzo_etha ethaclient,
			   const lcrzo_etha ethaserveur,
			   lcrzo_ipl iplclient,
			   lcrzo_ipl iplserveur, 
			   lcrzo_uint16 portclient,
			   lcrzo_uint16 portserveur,
			   lcrzo_sock *psock)
{ return(lcrzo_sock_tcpcli_virt2(device, ethaclient, ethaserveur,
				 iplclient, iplserveur, 
				 portclient, portserveur,
				 NULL, 0,
				 psock));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_tcpcli_real2(lcrzo_ipl    iplclient,
			    lcrzo_ipl    iplserveur, 
			    lcrzo_uint16 portclient,
			    lcrzo_uint16 portserveur,
			    const lcrzo_ipopt ipopt,
			    lcrzo_uint8 ipoptsize,
			    lcrzo_sock   *psock)
{ struct sockaddr_in cli;
  int retour, sock;

  /*parameters verification*/
  if (ipopt==NULL && ipoptsize) return(LCRZO_ERR_PANULLPTRSIZE);

  /*creation de la socket*/
  sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sock<0) return(LCRZO_ERR_FUSOCKET);

  /*ajout eventuel des options IP*/
  if (ipoptsize)
  { retour = LAURENT_setsockopt(sock, IPPROTO_IP, IP_OPTIONS,
				ipopt, ipoptsize);
    if (retour)
      return(LCRZO_ERR_FUSETSOCKOPT);
  }

  /*le client specifie des infos locales*/
  if ( iplclient || portclient )
  { memset(&cli, 0, sizeof(cli));
    cli.sin_family = AF_INET;
    cli.sin_addr.s_addr=lcrzo_htonl(iplclient);
    cli.sin_port = lcrzo_htons(portclient);
    retour=bind(sock, (struct sockaddr *)&cli, sizeof(cli));
    if (retour<0) return(LCRZO_ERR_FUBIND);
  }

  /*connexion sur le serveur*/
  memset(&cli, 0, sizeof(cli));
  cli.sin_family = AF_INET;
  cli.sin_addr.s_addr=lcrzo_htonl(iplserveur);
  cli.sin_port = lcrzo_htons(portserveur);
  retour=connect(sock, (struct sockaddr *)&cli, sizeof(cli));
  if (retour<0) return(LCRZO_ERR_FUCONNECT);

  /*if the user only want to test, its ok, so we close*/
  if (psock==NULL)
  { retour=LAURENT_closesocket(sock);
    if ( retour == -1 ) return(LCRZO_ERR_FUCLOSE);
    return(LCRZO_ERR_OK);
  }

  /*remplissage des caracteristiques de la socket*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_REALCLITCP;
  psock->socknum=sock;
  psock->there_ipl=iplserveur;
  psock->there_port=portserveur;
  psock->here_ipl=iplclient;
  psock->here_port=portclient;
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_tcpcli_virt_sniff(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess);
int lcrzo_sock_tcpcli_virt_sniff(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess)
{ lcrzo_sock sock;
  lcrzo_sniff sniff;

  /* copy socket data */
  if (infossize!=sizeof(sock))
  { return(LCRZO_ERR_IEINTERNALERROR);
  }
  memcpy(&sock, infos, infossize);

  /* close the process data */
  lcrzo_er(lcrzo_process_close(pparentprocess));

  /* close unneeded IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcspoof));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcoptions));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipccontrol1));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcoptions)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipccontrol1)));
  
  /* sniff function */
  lcrzo_er(lcrzo_priv_sniff_init_dev(&sock, LCRZO_SNIFF_TYPE_TCPREOR, &sniff));
  lcrzo_er(lcrzo_priv_sock_tcpstate(&sock, &sniff));
  lcrzo_er(lcrzo_sniff_close(&sniff));
  
  /* end */
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcspoof));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcoptions));
  lcrzo_er(lcrzo_ipc_close(&sock.ipccontrol1));
  return(LCRZO_ERR_OK);
}
int lcrzo_sock_tcpcli_virt2(const lcrzo_device device,
			    const lcrzo_etha ethaclient,
			    const lcrzo_etha ethaserveur,
			    lcrzo_ipl iplclient,
			    lcrzo_ipl iplserveur, 
			    lcrzo_uint16 portclient,
			    lcrzo_uint16 portserveur,
			    const lcrzo_ipopt ipopt,
			    lcrzo_uint8 ipoptsize,
			    lcrzo_sock *psock)
{ 
  /*parameters verification*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  if (ethaserveur==NULL) return(LCRZO_ERR_PANULLPTR);
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  /*initialisations*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_VIRTCLITCP;
  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_SYNSENT_INIT;
  lcrzo_er(lcrzo_priv_sock_infos_init(device, ethaclient, iplclient, 
				      portclient,
				      ethaserveur, iplserveur, portserveur,
				      psock));
  lcrzo_er(lcrzo_priv_sock_ipc_init(psock));

  /* sniff process */
  lcrzo_er(lcrzo_process_other_init2(&lcrzo_sock_tcpcli_virt_sniff,
				     (lcrzo_data)psock, sizeof(lcrzo_sock), 
				     lcrzo_process_int2uint8_socktcp,
				     lcrzo_process_uint82int_socktcp,
				     &psock->sniffprocess));

  /* no spoof process */
  psock->spoofprocesshasended=LCRZO_TRUE;
  psock->spoofprocessreturnedvalue=LCRZO_ERR_OK;

  /* close unnecessary IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcspoof));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcoptions));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipccontrol1));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcoptions)));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipccontrol1)));

  /* send options */
  lcrzo_er(lcrzo_sock_ipopt_set(psock, ipopt, ipoptsize));
  
  /* wait the end of the handshake */
  lcrzo_er(lcrzo_priv_sock_waitendhandshake(psock));

  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_sock_udpser_real(lcrzo_uint16 port,
			  lcrzo_sock *psock)
{ return(lcrzo_sock_udpser_real2(0, port, NULL, 0, psock));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_udpser_virt(const lcrzo_device device,
			  const lcrzo_etha ethaserveur,
			  lcrzo_ipl iplserveur, 
			  lcrzo_uint16 portserveur,
			  lcrzo_sock *psock)
{ return(lcrzo_sock_udpser_virt2(device, ethaserveur,
				 iplserveur, portserveur,
				 NULL, 0, psock));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_udpser_real2(lcrzo_ipl ipl,
			    lcrzo_uint16 port,
			    const lcrzo_ipopt ipopt,
			    lcrzo_uint8 ipoptsize,
			    lcrzo_sock *psock)
{ struct sockaddr_in sain;
  int retour, sock;

  /*parameters verification*/
  if (ipopt==NULL && ipoptsize) return(LCRZO_ERR_PANULLPTRSIZE);

  /*creation de la socket*/
  sock=socket(AF_INET, SOCK_DGRAM, 0);
  if (sock<0) return(LCRZO_ERR_FUSOCKET);

  /*ajout eventuel des options IP*/
  if (ipoptsize)
  { retour = LAURENT_setsockopt(sock, IPPROTO_IP, IP_OPTIONS,
				ipopt, ipoptsize);
    if (retour)
      return(LCRZO_ERR_FUSETSOCKOPT);
  }

  /*ecoute sur le port*/
  memset(&sain, 0, sizeof(sain));
  sain.sin_family = AF_INET;
  if ( ipl==0 )
    sain.sin_addr.s_addr=lcrzo_htonl(INADDR_ANY);
  else
    sain.sin_addr.s_addr=lcrzo_htonl(ipl);
  sain.sin_port = lcrzo_htons(port);
  retour=bind(sock, (struct sockaddr *)&sain, sizeof(sain));
  if (retour<0) return(LCRZO_ERR_FUBIND);

  /*if the user only want to test, its ok, so we close*/
  if (psock==NULL)
  { retour=LAURENT_closesocket(sock);
    if ( retour == -1 ) return(LCRZO_ERR_FUCLOSE);
    return(LCRZO_ERR_OK);
  }

  /*remplissage des caracteristiques de la socket*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_REALSERUDP;
  psock->socknum=sock;
  psock->here_ipl=ipl;
  psock->here_port=port;
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_udpser_virt2_sniff(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess);
int lcrzo_sock_udpser_virt2_sniff(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess)
{ lcrzo_sock sock;

  /* copy socket data */
  if (infossize!=sizeof(sock))
  { return(LCRZO_ERR_IEINTERNALERROR);
  }
  memcpy(&sock, infos, infossize);

  /* close the process data */
  lcrzo_er(lcrzo_process_close(pparentprocess));

  /* close unneeded IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcspoof));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcspoofend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcoptions));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipccontrol1));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&sock.ipccontrol2));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipccontrol1)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipccontrol2)));
  
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcspoofend)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcspoofend)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcoptions)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcoptions)));
  
  /* sniff function */
  lcrzo_er(lcrzo_priv_udp_sniffprocess(&sock));
  
  /* end */
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcspoof));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcspoofend));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcoptions));
  lcrzo_er(lcrzo_ipc_close(&sock.ipccontrol1));
  lcrzo_er(lcrzo_ipc_close(&sock.ipccontrol2));
  return(LCRZO_ERR_OK);
}
int lcrzo_sock_udpser_virt2_spoof(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess);
int lcrzo_sock_udpser_virt2_spoof(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess)
{ lcrzo_sock sock;

  /* copy socket data */
  if (infossize!=sizeof(sock))
  { return(LCRZO_ERR_IEINTERNALERROR);
  }
  memcpy(&sock, infos, infossize);

  /* close the process data */
  lcrzo_er(lcrzo_process_close(pparentprocess));

  /* close unneeded IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcspoof));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcspoofend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcoptions));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipccontrol1));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipccontrol2));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcspoofend)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcoptions)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipccontrol2)));
  
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipccontrol1)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipccontrol1)));

  /* spoof function */
  lcrzo_er(lcrzo_priv_udp_spoofprocess(&sock));
  
  /* end */
  return(LCRZO_ERR_OK);
}
int lcrzo_sock_udpser_virt2(const lcrzo_device device,
			    const lcrzo_etha ethaserveur,
			    lcrzo_ipl iplserveur, 
			    lcrzo_uint16 portserveur,
			    const lcrzo_ipopt ipopt,
			    lcrzo_uint8 ipoptsize,
			    lcrzo_sock *psock)
{ 
  /*parameters verification*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  if (ethaserveur==NULL) return(LCRZO_ERR_PANULLPTR);
  if (ipopt==NULL && ipoptsize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  /*initialisations*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_VIRTSERUDP;
  lcrzo_er(lcrzo_device_init(device, psock->here_device));
  memcpy(psock->here_etha, ethaserveur, LCRZO_ETHA_MAXBYTES);
  psock->here_ipl=iplserveur;
  psock->here_port=portserveur;
  lcrzo_er(lcrzo_priv_sock_ipc_init(psock));

  /* sniff process */
  lcrzo_er(lcrzo_process_other_init(&lcrzo_sock_udpser_virt2_sniff,
                                    (lcrzo_data)psock, sizeof(lcrzo_sock), &psock->sniffprocess));

  /* spoof process */
  lcrzo_er(lcrzo_process_other_init(&lcrzo_sock_udpser_virt2_spoof,
                                    (lcrzo_data)psock, sizeof(lcrzo_sock), &psock->spoofprocess));

  /* close unnecessary IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcspoof));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcspoofend));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcoptions));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipccontrol1));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipccontrol2));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcspoofend)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcoptions)));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipccontrol1)));

  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipccontrol2)));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipccontrol2)));
  
  /* send options */
  lcrzo_er(lcrzo_sock_ipopt_set(psock, ipopt, ipoptsize));
  
  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_sock_udpmulser_real(lcrzo_uint16 port,
			      lcrzo_sock *psock)
{ return(lcrzo_sock_udpmulser_real2(0, port, psock));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_udpmulser_virt_sniff(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess);
int lcrzo_sock_udpmulser_virt_sniff(void *infos, lcrzo_int32 infossize, lcrzo_process *pparentprocess)
{ lcrzo_sock sock;

  /* copy socket data */
  if (infossize!=sizeof(sock))
  { return(LCRZO_ERR_IEINTERNALERROR);
  }
  memcpy(&sock, infos, infossize);

  /* close the process data */
  lcrzo_er(lcrzo_process_close(pparentprocess));

  /* close unneeded IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissideb(&sock.ipccontrol1));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_write(&(sock.ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_read(&(sock.ipccontrol1)));
  
  /* sniff function */
  lcrzo_er(lcrzo_priv_udp_sniffprocess(&sock));
  
  /* end */
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniff));
  lcrzo_er(lcrzo_ipc_close(&sock.ipcsniffend));
  lcrzo_er(lcrzo_ipc_close(&sock.ipccontrol1));
  return(LCRZO_ERR_OK);
}
int lcrzo_sock_udpmulser_virt(const lcrzo_device device,
			      const lcrzo_etha ethaserveur,
			      lcrzo_ipl iplserveur, 
			      lcrzo_uint16 portserveur,
			      lcrzo_sock *psock)
{ 
  /*parameters verification*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  if (ethaserveur==NULL) return(LCRZO_ERR_PANULLPTR);
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  /*initialisations*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI;
  lcrzo_er(lcrzo_device_init(device, psock->here_device));
  memcpy(psock->here_etha, ethaserveur, LCRZO_ETHA_MAXBYTES);
  psock->here_ipl=iplserveur;
  psock->here_port=portserveur;
  lcrzo_er(lcrzo_priv_sock_ipc_init(psock));

  /* sniff process */
  lcrzo_er(lcrzo_process_other_init(&lcrzo_sock_udpmulser_virt_sniff,
                                    (lcrzo_data)psock, sizeof(lcrzo_sock), &psock->sniffprocess));

  /* no spoof process */
  psock->spoofprocesshasended=LCRZO_TRUE;
  psock->spoofprocessreturnedvalue=LCRZO_ERR_OK;

  /* close unnecessary IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipccontrol1));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipccontrol1)));

  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_sock_udpmulser_real2(lcrzo_ipl ipl,
			       lcrzo_uint16 port,
			       lcrzo_sock *psock)
{ struct sockaddr_in sain;
  int retour, sock;

  /*creation de la socket*/
  sock=socket(AF_INET, SOCK_DGRAM, 0);
  if (sock<0) return(LCRZO_ERR_FUSOCKET);

  memset(&sain, 0, sizeof(sain));
  sain.sin_family = AF_INET;
  if ( ipl==0 )
    sain.sin_addr.s_addr=lcrzo_htonl(INADDR_ANY);
  else
    sain.sin_addr.s_addr=lcrzo_htonl(ipl);
  sain.sin_port = lcrzo_htons(port);
  retour=bind(sock, (struct sockaddr *)&sain, sizeof(sain));
  if (retour<0) return(LCRZO_ERR_FUBIND);

  /*if the user only want to test, its ok, so we close*/
  if (psock==NULL)
  { retour=LAURENT_closesocket(sock);
    if ( retour == -1 ) return(LCRZO_ERR_FUCLOSE);
    return(LCRZO_ERR_OK);
  }

  /*remplissage des caracteristiques de la socket*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_REALSERUDPMULTI;
  psock->socknum=sock;
  psock->here_port=port;
  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_sock_tcpser_real(lcrzo_uint16 port,
			  lcrzo_sock *psock)
{ return(lcrzo_sock_tcpser_real2(0, port, NULL, 0, psock));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_tcpser_virt(const lcrzo_device device,
			  const lcrzo_etha ethaserveur,
			  lcrzo_ipl iplserveur, 
			  lcrzo_uint16 portserveur,
			  lcrzo_sock *psock)
{ return(lcrzo_sock_tcpser_virt2(device, ethaserveur, iplserveur,
				 portserveur, NULL, 0, psock));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_tcpser_real2(lcrzo_ipl ipl,
			    lcrzo_uint16 port,
			    const lcrzo_ipopt ipopt,
			    lcrzo_uint8 ipoptsize,
			    lcrzo_sock *psock)
{ struct sockaddr_in sain, sain2;
  LAURENT_socklen_t sizeof_sain2;
  int retour, soc, un, sock;

  /*parameters verification*/
  if (ipopt==NULL && ipoptsize) return(LCRZO_ERR_PANULLPTRSIZE);

  /*creation de la socket*/
  soc=socket(AF_INET, SOCK_STREAM, 0);
  if (soc<0) return(LCRZO_ERR_FUSOCKET);

  /*pour que l'adresse puisse etre immediatement reutilisee*/
  un=1;
  retour=LAURENT_setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &un, sizeof(un));
  if (retour) return(LCRZO_ERR_FUSETSOCKOPT);

  /*ajout eventuel des options IP*/
  if (ipoptsize)
  { retour = LAURENT_setsockopt(soc, IPPROTO_IP, IP_OPTIONS, ipopt, ipoptsize);
    if (retour)
      return(LCRZO_ERR_FUSETSOCKOPT);
  }

  /*ecoute sur le port*/
  memset(&sain, 0, sizeof(sain));
  sain.sin_family = AF_INET;
  if ( ipl==0 )
    sain.sin_addr.s_addr=lcrzo_htonl(INADDR_ANY);
  else
    sain.sin_addr.s_addr=lcrzo_htonl(ipl);
  sain.sin_port = lcrzo_htons(port);
  retour=bind(soc, (struct sockaddr *)&sain, sizeof(sain));
  if (retour<0) return(LCRZO_ERR_FUBIND);

  retour=listen(soc, 1); /*maximum 1 connexion en parallele*/
  if (retour<0) return(LCRZO_ERR_FULISTEN);

  sizeof_sain2=sizeof(sain2);
  memset(&sain2, 0, sizeof_sain2);
  sock=accept(soc,  (struct sockaddr *)&sain2, &sizeof_sain2);
  if (sock<0) return(LCRZO_ERR_FUACCEPT);

  retour=LAURENT_closesocket(soc);
  if (retour==-1) return(LCRZO_ERR_FUCLOSE);

  /*ajout eventuel des options IP*/
  if (ipoptsize)
  { retour = LAURENT_setsockopt(sock, IPPROTO_IP, IP_OPTIONS,
				ipopt, ipoptsize);
    if (retour)
      return(LCRZO_ERR_FUSETSOCKOPT);
  }

  /*if the user only want to test, its ok, so we close*/
  if (psock==NULL)
  { retour=LAURENT_closesocket(sock);
    if ( retour == -1 ) return(LCRZO_ERR_FUCLOSE);
    return(LCRZO_ERR_OK);
  }

  /*remplissage des caracteristiques de la socket*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_REALSERTCP;
  psock->socknum=sock;
  psock->here_port=port;
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_tcpser_virt2(const lcrzo_device device,
			    const lcrzo_etha ethaserveur,
			    lcrzo_ipl iplserveur, 
			    lcrzo_uint16 portserveur,
			    const lcrzo_ipopt ipopt,
			    lcrzo_uint8 ipoptsize,
			    lcrzo_sock *psock)
{ 
  /*parameters verification*/
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  if (ethaserveur==NULL) return(LCRZO_ERR_PANULLPTR);
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  /*initialisations*/
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(psock));
  psock->type=LCRZO_PRIV_TYPESOCK_VIRTSERTCP;
  psock->tcpstate=LCRZO_PRIV_SOCK_TCPSTATE_LISTEN_INIT;
  lcrzo_er(lcrzo_priv_sock_infos_init(device, 
				    ethaserveur, iplserveur, portserveur,
				    NULL, 0, 0,
                                    psock));
  lcrzo_er(lcrzo_priv_sock_ipc_init(psock));

  /* sniff process (use the same function as VIRTCLITCP) */
  lcrzo_er(lcrzo_process_other_init2(&lcrzo_sock_tcpcli_virt_sniff,
				     (lcrzo_data)psock, sizeof(lcrzo_sock),
				     lcrzo_process_int2uint8_socktcp,
				     lcrzo_process_uint82int_socktcp,
				     &psock->sniffprocess));

  /* no spoof process */
  psock->spoofprocesshasended=LCRZO_TRUE;
  psock->spoofprocessreturnedvalue=LCRZO_ERR_OK;

  /* close unnecessary IPC */
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniff));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcspoof));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcsniffend));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipcoptions));
  lcrzo_er(lcrzo_ipc_currentprocessissidea(&psock->ipccontrol1));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipcsniff)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcspoof)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcsniffend)));
  lcrzo_er(lcrzo_ipc_close_read(&(psock->ipcoptions)));
  lcrzo_er(lcrzo_ipc_close_write(&(psock->ipccontrol1)));

  /* send options */
  lcrzo_er(lcrzo_sock_ipopt_set(psock, ipopt, ipoptsize));

  /* wait the end of the handshake */
  lcrzo_er(lcrzo_priv_sock_waitendhandshake(psock));

  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_sock_tcpmulser_real(lcrzo_uint16 port,
			      int (*pfonc)(lcrzo_sock *psock,
					   void *pinfos),
			      void *pinfos)
{ return(lcrzo_sock_tcpmulser_real2(0, port, NULL, 0, pfonc, pinfos));
}

/*-------------------------------------------------------------*/
typedef struct
{ int sockglo;
  int socknum;
  lcrzo_ipopt ipopt;
  lcrzo_int8 ipoptsize;
  lcrzo_ipl ipl;
  lcrzo_uint16 port;
  int (*pfunc)(lcrzo_sock *psock, void *pinfos);
  void *pfuncinfos;
} lcrzo_priv_sock_tcpmulser_real2_st;
int lcrzo_priv_sock_tcpmulser_real2(void *infos, 
                                    lcrzo_int32 infossize, 
                                    lcrzo_process *pparentprocess);
int lcrzo_priv_sock_tcpmulser_real2(void *infos, 
                                    lcrzo_int32 infossize, 
                                    lcrzo_process *pparentprocess)
{ lcrzo_priv_sock_tcpmulser_real2_st *pst=infos;
  lcrzo_sock sock;
  int ret;

  /* close information on father */
  lcrzo_er(lcrzo_process_close(pparentprocess));

#if defined LCRZODEF_SYSTEM_Unix
  /* close the father's socket */
  ret=LAURENT_closesocket(pst->sockglo);
  if (ret==-1) return(LCRZO_ERR_FUCLOSE);
#elif defined LCRZODEF_SYSTEM_Windows
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif

  /* init socket data */
  lcrzo_er(lcrzo_priv_sock_struct_initdefault(&sock));
  sock.socknum=pst->socknum;
  sock.type=LCRZO_PRIV_TYPESOCK_REALSERTCPMULTI;
  sock.here_ipl=pst->ipl;
  sock.here_port=pst->port;

  /* eventually add IP options */
  if (pst->ipoptsize)
  { ret = LAURENT_setsockopt(sock.socknum, IPPROTO_IP, IP_OPTIONS,
			     pst->ipopt, pst->ipoptsize);
    if (ret) return(LCRZO_ERR_FUSETSOCKOPT);
  }

  /* call the function */
  ret=(*pst->pfunc)(&sock, pst->pfuncinfos);

  /*close*/
  LAURENT_closesocket(sock.socknum);

  return(LCRZO_ERR_OK);
  infossize=infossize; /* for the compiler warning */
}
int lcrzo_sock_tcpmulser_real2(lcrzo_ipl ipl,
			       lcrzo_uint16 port,
			       const lcrzo_ipopt ipopt,
			       lcrzo_uint8 ipoptsize,
			       int (*pfonc)(lcrzo_sock *psock,
					    void *pinfos),
			       void *pinfos)
{ struct sockaddr_in sain, sain2;
  lcrzo_priv_sock_tcpmulser_real2_st tmsst;
  lcrzo_multiprocess multiprocess;
  lcrzo_uint8 sock_tcpser_backlog;
  int ret, sockglo, socknum, un, processnumber, returnedvalue;
  lcrzo_bool aprocesshasended;
  LAURENT_socklen_t sizeof_sain2;

  /*parameters verification*/
  if (ipopt==NULL && ipoptsize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (pfonc==NULL) return(LCRZO_ERR_PANULLPTR);
  lcrzo_ipopt_verifbof(ipoptsize);

  /*creation de la socket*/
  sockglo=socket(AF_INET, SOCK_STREAM, 0);
  if (sockglo<0) return(LCRZO_ERR_FUSOCKET);

  /*pour que l'adresse puisse etre immediatement reutilisee*/
  un=1;
  ret=LAURENT_setsockopt(sockglo, SOL_SOCKET, SO_REUSEADDR, &un, sizeof(un));
  if (ret) return(LCRZO_ERR_FUSETSOCKOPT);

  /*ajout eventuel des options IP*/
  if (ipoptsize)
  { ret = LAURENT_setsockopt(sockglo, IPPROTO_IP, IP_OPTIONS, 
			     ipopt, ipoptsize);
    if (ret)
      return(LCRZO_ERR_FUSETSOCKOPT);
  }

  /*ecoute sur le port*/
  memset(&sain, 0, sizeof(sain));
  sain.sin_family = AF_INET;
  if ( ipl==0 )
    sain.sin_addr.s_addr=lcrzo_htonl(INADDR_ANY);
  else
    sain.sin_addr.s_addr=lcrzo_htonl(ipl);
  sain.sin_port = lcrzo_htons(port);
  ret=bind(sockglo, (struct sockaddr *)&sain, sizeof(sain));
  if (ret<0) return(LCRZO_ERR_FUBIND);

  lcrzo_er(lcrzo_global_get_sock_tcpser_backlog(&sock_tcpser_backlog));
  ret=listen(sockglo, sock_tcpser_backlog);
  if (ret<0) return(LCRZO_ERR_FULISTEN);

  /*on ecoute indefiniment*/
  lcrzo_er(lcrzo_multiprocess_init(&multiprocess));
  processnumber=1;
  while (1)
  { sizeof_sain2=sizeof(sain2);
    memset(&sain2, 0, sizeof_sain2);
    socknum=accept(sockglo, (struct sockaddr *)&sain2, &sizeof_sain2);
    if (socknum<0) return(LCRZO_ERR_FUACCEPT);

    /* set child info */
    tmsst.sockglo=sockglo;
    tmsst.socknum=socknum;
    memcpy(tmsst.ipopt, ipopt, ipoptsize);
    tmsst.ipoptsize=ipoptsize;;
    tmsst.ipl=ipl;
    tmsst.port=port;
    tmsst.pfunc=pfonc;
    tmsst.pfuncinfos=pinfos;

    /* create a child */
    processnumber++;
    ret=lcrzo_multiprocess_process_init(&multiprocess, processnumber,
                                        &lcrzo_priv_sock_tcpmulser_real2,
                                        (lcrzo_data)&tmsst, sizeof(tmsst));
    if (ret!=LCRZO_ERR_OK) break;

    /* eventually wait for the end of childs */
    do
    { ret=lcrzo_multiprocess_process_wait_end(&multiprocess, LCRZO_WAIT_NODELAY,
                                              &aprocesshasended, NULL, &returnedvalue);
      if (ret!=LCRZO_ERR_OK) break;
      if (returnedvalue!=LCRZO_ERR_OK) break;
    }
    while(aprocesshasended);

#if defined LCRZODEF_SYSTEM_Unix
    /* close the child's socket */
    ret=LAURENT_closesocket(socknum);
    if (ret==-1) return(LCRZO_ERR_FUCLOSE);
#elif defined LCRZODEF_SYSTEM_Windows
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif

  }
  lcrzo_er(lcrzo_multiprocess_close(&multiprocess));
  /*jamais atteint, mais pour faire plaisir au compilateur*/
  return(lcrzo_ierp("lcrzo_sock_tcpmulser_real2"));
}


/*-------------------------------------------------------------*/
int lcrzo_sock_write(lcrzo_sock *psock,
		     lcrzo_constdata data, 
		     lcrzo_int32 datasize)
{ 
  /*parameters verification*/
  if (data==NULL && datasize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (datasize==0) return(LCRZO_ERR_OK);

  /* error cases */
  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_REALSERUDPMULTI:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI:
      return(LCRZO_ERR_LOUDPSERMULTINOWRITE);
      break;
    case LCRZO_PRIV_TYPESOCK_REALSERUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
      if (psock->udpcanwrite==0)
	return(LCRZO_ERR_LOUDPSERREADBEFWRITE);
      break;
  }

  /* normal cases */
  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_REALCLIUDP:
    case LCRZO_PRIV_TYPESOCK_REALCLITCP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCPMULTI:
      return(lcrzo_priv_fd_send(psock->socknum, data, datasize));
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
      /* look if we can still write */
      lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_FALSE));
      if ( psock->spoofprocesshasended ) return(LCRZO_ERR_LONOTOPENFORWRITING);
      /* write */
      return(lcrzo_ipc_write(&(psock->ipcspoof), data, datasize));
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLITCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERTCP:
      /* look if we can still write */
      lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_FALSE));
      if ( psock->sniffprocesshasended ) return(LCRZO_ERR_LONOTOPENFORWRITING);
      /* write */
      return(lcrzo_ipc_write(&(psock->ipcspoof), data, datasize));
      break;
    default:
      return(lcrzo_ieuv("lcrzo_sock_write", "psock->type"));
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_write_msg(lcrzo_sock *psock,
			 lcrzo_constdata data, 
			 lcrzo_int32 datasize)
{ 
  /*parameters verification*/
  if (data==NULL && datasize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (datasize==0) return(LCRZO_ERR_OK);

  /* error cases */
  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_REALSERUDPMULTI:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI:
      return(LCRZO_ERR_LOUDPSERMULTINOWRITE);
      break;
    case LCRZO_PRIV_TYPESOCK_REALSERUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
      if (psock->udpcanwrite==0)
	return(LCRZO_ERR_LOUDPSERREADBEFWRITE);
      break;
  }

  /* normal cases */
  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_REALCLIUDP:
    case LCRZO_PRIV_TYPESOCK_REALCLITCP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCPMULTI:
      return(lcrzo_priv_fd_send_msg(psock->socknum, data, datasize));
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
      /* look if we can still write */
      lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_FALSE));
      if ( psock->spoofprocesshasended ) return(LCRZO_ERR_LONOTOPENFORWRITING);
      return(lcrzo_ipc_write_msg(&(psock->ipcspoof), data, datasize));
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLITCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERTCP:
      /* look if we can still write */
      lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_FALSE));
      if ( psock->sniffprocesshasended ) return(LCRZO_ERR_LONOTOPENFORWRITING);
      return(lcrzo_ipc_write_msg(&(psock->ipcspoof), data, datasize));
      break;
    default:
      return(lcrzo_ieuv("lcrzo_sock_write_msg", "psock->type"));
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_readm(lcrzo_sock *psock,
		     lcrzo_time *pmaxwaitingtime, 
	             lcrzo_data *pdataout,
		     lcrzo_int32 *pdataoutsize)
{ lcrzo_wait waitread;
  lcrzo_bool mayread;

  lcrzo_er(lcrzo_wait_init_sock_read(psock, &waitread));
  lcrzo_er(lcrzo_wait_wait1(&waitread, pmaxwaitingtime, &mayread));
  lcrzo_er(lcrzo_wait_close(&waitread));
  if (!mayread) return(LCRZO_ERR_OKTEMPDATAEND);
  
  /* read data, and check it corresponds to wanted data */
  lcrzo_er(lcrzo_priv_storedtlvdata_extract(&(psock->storeddata),
					    &(psock->storeddatasize), 
                                            psock->dataend, 
					    pdataout, pdataoutsize));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_readm_fixed(lcrzo_sock *psock,
			   lcrzo_time *pmaxwaitingtime, 
			   lcrzo_int32 datasizetoread,
			   lcrzo_data *pdataout)
{ lcrzo_wait waitread;
  lcrzo_bool mayread;
  
  lcrzo_er(lcrzo_wait_init_sock_read_fixed(psock, datasizetoread, &waitread));
  lcrzo_er(lcrzo_wait_wait1(&waitread, pmaxwaitingtime, &mayread));
  lcrzo_er(lcrzo_wait_close(&waitread));
  if (!mayread) return(LCRZO_ERR_OKTEMPDATAEND);

  /* read data, and check it corresponds to wanted data */
  lcrzo_er(lcrzo_priv_storedtlvdata_extract_fixed(&(psock->storeddata), 
						  &(psock->storeddatasize), 
                                                  psock->dataend,
						  datasizetoread, pdataout));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_readm_line(lcrzo_sock *psock,
			  lcrzo_time *pmaxwaitingtime, 
			  lcrzo_data *pdataout,
			  lcrzo_int32 *pdataoutsize)
{ lcrzo_wait waitread;
  lcrzo_bool mayread;
  
  lcrzo_er(lcrzo_wait_init_sock_read_line(psock, &waitread));
  lcrzo_er(lcrzo_wait_wait1(&waitread, pmaxwaitingtime, &mayread));
  lcrzo_er(lcrzo_wait_close(&waitread));
  if (!mayread) return(LCRZO_ERR_OKTEMPDATAEND);

  /* read data, and check it corresponds to wanted data */
  lcrzo_er(lcrzo_priv_storedtlvdata_extract_line(&(psock->storeddata), 
						 &(psock->storeddatasize), 
                                                 psock->dataend, 
						 pdataout, pdataoutsize));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_close(lcrzo_sock *psock)
{ int ret;

  /* free stored data */
  if (psock->storeddatasize)
  { lcrzo_er(lcrzo_data_free(psock->storeddata));
    psock->storeddatasize=0;
  }
  
  /* close */
  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_REALCLIUDP:
    case LCRZO_PRIV_TYPESOCK_REALCLITCP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDPMULTI:
    case LCRZO_PRIV_TYPESOCK_REALSERTCPMULTI:
      ret=LAURENT_closesocket(psock->socknum);
      if (ret==-1) return(LCRZO_ERR_FUCLOSE);
      psock->socknum=-1;
      return(LCRZO_ERR_OK);
      break;

    case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
      /* ask to the two child processes to exit */
      lcrzo_er(lcrzo_ipc_write_uint16(&psock->ipcsniffend, LCRZO_TRUE));
      lcrzo_er(lcrzo_ipc_write_uint16(&psock->ipcspoofend, LCRZO_TRUE));
      /* wait till the end of the processes */
      lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_TRUE));
      /* close IPC */
      lcrzo_er(lcrzo_priv_sock_ipc_close(psock));
      /* end */
      return(LCRZO_ERR_OK);
      break;

    case LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI:
    case LCRZO_PRIV_TYPESOCK_VIRTCLITCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERTCP:
      /* ask to the child process to exit */
      ret=lcrzo_ipc_write_uint16(&psock->ipcsniffend, LCRZO_TRUE);
      if ( ret!=LCRZO_ERR_OK && ret!=LCRZO_ERR_LONOTOPENFORWRITING )
      { /* ignore LONOTOPENFORWRITING because child might have exited */
        return(ret);
      }
      /* wait till the end of the processes */
      lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_TRUE));
      /* close IPC */
      lcrzo_er(lcrzo_priv_sock_ipc_close(psock));
      /* end */
      return(LCRZO_ERR_OK);
      break;

    default:
      return(lcrzo_ieuv("lcrzo_sock_close", "psock->type"));
  }

  return(lcrzo_ierp("lcrzo_sock_close"));
}

/*-------------------------------------------------------------*/
int lcrzo_sock_get_infos(lcrzo_sock *psock,
			lcrzo_device here_device,
			lcrzo_etha here_etha,
			lcrzo_ipl *phere_ipl,
			lcrzo_uint16 *phere_port,
			lcrzo_etha there_etha,
			lcrzo_ipl *pthere_ipl,
			lcrzo_uint16 *pthere_port)
{ int retour;
  struct sockaddr sa;
  struct sockaddr_in sai;
  LAURENT_socklen_t taillesa;

  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_REALCLIUDP:
    case LCRZO_PRIV_TYPESOCK_REALCLITCP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCPMULTI:
      if (here_device!=NULL)
	lcrzo_er(lcrzo_device_initdefault(here_device));
      if (here_etha!=NULL)
	lcrzo_er(lcrzo_etha_initdefault(here_etha));
      if (there_etha!=NULL)
	lcrzo_er(lcrzo_etha_initdefault(there_etha));
      taillesa=sizeof(struct sockaddr);
      retour=getsockname(psock->socknum, &sa, &taillesa);
      if (retour==-1) return(LCRZO_ERR_FUGETSOCKNAME);
      memcpy(&sai, &sa, sizeof(struct sockaddr_in));
      if (phere_ipl!=NULL)
	*phere_ipl=lcrzo_ntohl(sai.sin_addr.s_addr);
      if (phere_port!=NULL)
	*phere_port=lcrzo_ntohs(sai.sin_port);
      taillesa=sizeof(struct sockaddr);
      retour=getpeername(psock->socknum, &sa, &taillesa);
      if (retour==-1)
      { 
#if defined LCRZODEF_SYSTEM_Unix
        if (errno!=ENOTCONN) return(LCRZO_ERR_FUGETPEERNAME);
#elif defined LCRZODEF_SYSTEM_Windows
        if (h_errno!=WSAENOTCONN) return(LCRZO_ERR_FUGETPEERNAME);
        lcrzo_er(lcrzo_err_purge());
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif
        /*personne n'est connecte*/
        errno=0;
        if (pthere_ipl!=NULL)
	  lcrzo_er(lcrzo_ipl_initdefault(pthere_ipl));
	if (pthere_port!=NULL)
	  *pthere_port=0;
      }
      else
      { memcpy(&sai, &sa, sizeof(struct sockaddr_in));
        if (pthere_ipl!=NULL)
	  *pthere_ipl=lcrzo_ntohl(sai.sin_addr.s_addr);
	if (pthere_port!=NULL)
	  *pthere_port=lcrzo_ntohs(sai.sin_port);
      }
      break;
    case LCRZO_PRIV_TYPESOCK_REALSERUDPMULTI:
      taillesa=sizeof(struct sockaddr);
      retour=getsockname(psock->socknum, &sa, &taillesa);
      if (retour==-1) return(LCRZO_ERR_FUGETSOCKNAME);
      memcpy(&sai, &sa, sizeof(struct sockaddr_in));
      if (phere_ipl!=NULL)
	*phere_ipl=lcrzo_ntohl(sai.sin_addr.s_addr);
      if (phere_port!=NULL)
	*phere_port=lcrzo_ntohs(sai.sin_port);
      if (here_device!=NULL)
	memcpy(here_device, psock->here_device, LCRZO_DEVICE_MAXBYTES);
      if (here_etha!=NULL)
	memcpy(here_etha, psock->here_etha, LCRZO_ETHA_MAXBYTES);
      if (there_etha!=NULL)
	memcpy(there_etha, psock->there_etha, LCRZO_ETHA_MAXBYTES);
      if (pthere_ipl!=NULL)
	*pthere_ipl=psock->there_ipl;
      if (pthere_port!=NULL)
	*pthere_port=psock->there_port;
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTCLITCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERTCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDPMULTI:
      lcrzo_er(lcrzo_priv_sock_analyzeexitchilds(psock, LCRZO_FALSE));
      if (here_device!=NULL)
	memcpy(here_device, psock->here_device, LCRZO_DEVICE_MAXBYTES);
      if (here_etha!=NULL)
	memcpy(here_etha, psock->here_etha, LCRZO_ETHA_MAXBYTES);
      if (phere_ipl!=NULL)
	*phere_ipl=psock->here_ipl;
      if (phere_port!=NULL)
	*phere_port=psock->here_port;
      if (there_etha!=NULL)
	memcpy(there_etha, psock->there_etha, LCRZO_ETHA_MAXBYTES);
      if (pthere_ipl!=NULL)
	*pthere_ipl=psock->there_ipl;
      if (pthere_port!=NULL)
	*pthere_port=psock->there_port;
      break;
    default:
      return(lcrzo_ieuv("lcrzo_sock_get_infos", "psock->type"));
  }
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_fprint_infos(LCRZOFILE *pf,
			    lcrzo_sock *psock)
{ lcrzo_device here_device;
  lcrzo_etha here_etha;
  lcrzo_ipl here_ipl;
  lcrzo_uint16 here_port;
  lcrzo_etha there_etha;
  lcrzo_ipl there_ipl;
  lcrzo_uint16 there_port;

  /*parameters verification*/
  if (pf==NULL) return(LCRZO_ERR_PANULLPTR);

  lcrzo_er(lcrzo_sock_get_infos(psock, here_device, here_etha, &here_ipl, 
				&here_port,  there_etha, &there_ipl,
				&there_port));
  lcrzo_er(lcrzo_device_fprint(pf, "Local : device=", here_device, ", "));
  lcrzo_er(lcrzo_etha_fprint(pf, "eth=", here_etha, ", "));
  lcrzo_er(lcrzo_ipl_fprint(pf, "ip=", here_ipl, ", "));
  fprintf(pf, "port=%d\n", here_port);
  lcrzo_er(lcrzo_etha_fprint(pf, "Distant : eth=", there_etha, ", "));
  lcrzo_er(lcrzo_ipl_fprint(pf, "ip=", there_ipl, ", "));
  fprintf(pf, "port=%d\n", there_port);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_ipopt_set(lcrzo_sock *psock,
			 const lcrzo_ipopt ipopt,
			 lcrzo_uint8 ipoptsize)
{ int ret;

  /* check parameters */
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);
  if (ipopt==NULL && ipoptsize) return(LCRZO_ERR_PANULLPTRSIZE);
  if (ipoptsize>LCRZO_IPOPT_MAXBYTES)
    return(LCRZO_ERR_SPOPTMAX10);

  /* set structure */
  psock->ipoptsize=ipoptsize;
  memcpy(psock->ipopt, ipopt, ipoptsize);
  
  /* inform socket */
  switch(psock->type)
  { case LCRZO_PRIV_TYPESOCK_REALCLIUDP:
    case LCRZO_PRIV_TYPESOCK_REALCLITCP:
    case LCRZO_PRIV_TYPESOCK_REALSERUDP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCP:
    case LCRZO_PRIV_TYPESOCK_REALSERTCPMULTI:
      ret = LAURENT_setsockopt(psock->socknum, IPPROTO_IP, IP_OPTIONS, 
			       ipopt,ipoptsize);
      if (ret) return(LCRZO_ERR_FUSETSOCKOPT);
      break;
    case LCRZO_PRIV_TYPESOCK_VIRTCLIUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTCLITCP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERUDP:
    case LCRZO_PRIV_TYPESOCK_VIRTSERTCP:
      lcrzo_er(lcrzo_ipc_write_msg(&(psock->ipcoptions), ipopt, ipoptsize));
      break;
    case LCRZO_PRIV_TYPESOCK_REALSERUDPMULTI:
      return(LCRZO_ERR_LOUDPSERMULTINOWRITE);
      break;
    default:
      return(lcrzo_ieuv("lcrzo_sock_ipopt_set", "psock->type"));
  } 
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_ipopt_get(lcrzo_sock *psock,
			 lcrzo_ipopt ipopt,
			 lcrzo_uint8 *pipoptsize)
{
  /*parameters verification*/
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  if (pipoptsize!=NULL) *pipoptsize=psock->ipoptsize;
  if (ipopt!=NULL) memcpy(ipopt, psock->ipopt, psock->ipoptsize);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_ipopt_noop(lcrzo_sock *psock)
{ lcrzo_ipopt opt;
  lcrzo_uint8 ipoptsize;

  /*parameters verification*/
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  lcrzo_er(lcrzo_ipopt_init_noop(opt, &ipoptsize));
  lcrzo_er(lcrzo_sock_ipopt_set(psock, opt, ipoptsize));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_ipopt_lsrr(lcrzo_sock *psock,
			 lcrzo_ipl ipl1,
			 lcrzo_ipl ipl2,
			 lcrzo_ipl ipl3,
			 lcrzo_ipl ipl4,
			 lcrzo_ipl ipl5,
			 lcrzo_ipl ipl6,
			 lcrzo_ipl ipl7,
			 lcrzo_ipl ipl8,
			 lcrzo_ipl ipl9,
			 lcrzo_int8 nbiplaffectes)
{ lcrzo_ipopt opt;
  lcrzo_uint8 ipoptsize;

  /*parameters verification*/
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  lcrzo_er(lcrzo_ipopt_init_lsrr(ipl1, ipl2, ipl3, ipl4, ipl5, ipl6,
				ipl7, ipl8, ipl9, nbiplaffectes,
				opt, &ipoptsize));
  lcrzo_er(lcrzo_sock_ipopt_set(psock, opt, ipoptsize));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_ipopt_ssrr(lcrzo_sock *psock,
			 lcrzo_ipl ipl1,
			 lcrzo_ipl ipl2,
			 lcrzo_ipl ipl3,
			 lcrzo_ipl ipl4,
			 lcrzo_ipl ipl5,
			 lcrzo_ipl ipl6,
			 lcrzo_ipl ipl7,
			 lcrzo_ipl ipl8,
			 lcrzo_ipl ipl9,
			 lcrzo_int8 nbiplaffectes)
{ lcrzo_ipopt opt;
  lcrzo_uint8 ipoptsize;

  /*parameters verification*/
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  lcrzo_er(lcrzo_ipopt_init_ssrr(ipl1, ipl2, ipl3, ipl4, ipl5, ipl6,
				ipl7, ipl8, ipl9, nbiplaffectes,
				opt, &ipoptsize));
  lcrzo_er(lcrzo_sock_ipopt_set(psock, opt, ipoptsize));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_ipopt_rr(lcrzo_sock *psock,
		       lcrzo_int8 nbroutesaenregistrer)
{ lcrzo_ipopt opt;
  lcrzo_uint8 ipoptsize;

  /*parameters verification*/
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  lcrzo_er(lcrzo_ipopt_init_rr(nbroutesaenregistrer, opt, &ipoptsize));
  lcrzo_er(lcrzo_sock_ipopt_set(psock, opt, ipoptsize));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_ipopt_time(lcrzo_sock *psock,
			 lcrzo_int8 nbtimestampsaenregistrer)
{ lcrzo_ipopt opt;
  lcrzo_uint8 ipoptsize;

  /*parameters verification*/
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  lcrzo_er(lcrzo_ipopt_init_time(nbtimestampsaenregistrer, 
				 opt, &ipoptsize));
  lcrzo_er(lcrzo_sock_ipopt_set(psock, opt, ipoptsize));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_ipopt_timeip(lcrzo_sock *psock,
			   lcrzo_int8 nbtimestampsaenregistrer)
{ lcrzo_ipopt opt;
  lcrzo_uint8 ipoptsize;

  /*parameters verification*/
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  lcrzo_er(lcrzo_ipopt_init_timeip(nbtimestampsaenregistrer, 
				   opt, &ipoptsize));
  lcrzo_er(lcrzo_sock_ipopt_set(psock, opt, ipoptsize));
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_sock_ipopt_timeipp(lcrzo_sock *psock,
			    lcrzo_ipl ipl1,
			    lcrzo_ipl ipl2,
			    lcrzo_ipl ipl3,
			    lcrzo_ipl ipl4,
			    lcrzo_int8 nbiplaffectes)
{ lcrzo_ipopt opt;
  lcrzo_uint8 ipoptsize;

  /*parameters verification*/
  if (psock==NULL) return(LCRZO_ERR_PANULLPTR);

  lcrzo_er(lcrzo_ipopt_init_timeipp(ipl1, ipl2, ipl3, ipl4, nbiplaffectes,
				    opt, &ipoptsize));
  lcrzo_er(lcrzo_sock_ipopt_set(psock, opt, ipoptsize));
  return(LCRZO_ERR_OK);
}


/*-------------------------------------------------------------*/
int lcrzo_virtual_answer_arp(lcrzo_constdata packet,
			    lcrzo_uint32 nboct,
			    const lcrzo_device device,
			    const lcrzo_etha etha,
			    lcrzo_ipl ipl)
{ int retour;
  lcrzo_hdrleth hdrlethrecu, hdrlethenvoye;
  lcrzo_hdrlarp hdrlarprecu, hdrlarpenvoye;
  lcrzo_ipa ipa;
  lcrzo_spoof spoof;

  /*parameters verification*/
  if (packet==NULL && nboct) return(LCRZO_ERR_PANULLPTRSIZE);
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  if (etha==NULL) return(LCRZO_ERR_PANULLPTR);

  /*tentative de decodage ARP*/
  retour=lcrzo_packet_decode_etharp(packet, nboct,
				   &hdrlethrecu, &hdrlarprecu);
  if (retour!=LCRZO_ERR_OK) return(LCRZO_ERR_OK);

  /*on n'est interesse que par les requetes arp*/
  if ( hdrlethrecu.type != LCRZO_HDRLETH_TYPE_ARP ) 
    return(LCRZO_ERR_OK);
  if ( hdrlarprecu.op != 1 ) return(LCRZO_ERR_OK);
  
  /*on n'est interesse que par les requetes de ipa*/
  lcrzo_er(lcrzo_ipa_init_ipl(ipl, ipa));
  if (!lcrzo_ipa_equal(hdrlarprecu.prot_dst, ipa)) return(LCRZO_ERR_OK);

  /*cree le nouveau entete ethernet*/
  lcrzo_er(lcrzo_hdrleth_initdefault(&hdrlethenvoye));
  memcpy(hdrlethenvoye.src, etha, LCRZO_ETHA_MAXBYTES);
  memcpy(hdrlethenvoye.dst, hdrlarprecu.hw_src, LCRZO_ETHA_MAXBYTES);
  hdrlethenvoye.type=LCRZO_HDRLETH_TYPE_ARP;
    
  /*cree le nouveau entete arp*/
  lcrzo_er(lcrzo_hdrlarp_initdefault(&hdrlarpenvoye));
  hdrlarpenvoye.op=2;
  memcpy(hdrlarpenvoye.hw_src, etha, LCRZO_ETHA_MAXBYTES);
  memcpy(hdrlarpenvoye.hw_dst, hdrlarprecu.hw_src, LCRZO_ETHA_MAXBYTES);
  memcpy(hdrlarpenvoye.prot_src, hdrlarprecu.prot_dst, LCRZO_IPA_MAXBYTES);
  memcpy(hdrlarpenvoye.prot_dst, hdrlarprecu.prot_src, LCRZO_IPA_MAXBYTES);
    
  /*envoi du packet*/
  lcrzo_er(lcrzo_spoof_init(&spoof));
  lcrzo_er(lcrzo_spoof_etharp(&spoof, device, hdrlethenvoye,
			      hdrlarpenvoye));
  lcrzo_er(lcrzo_spoof_close(&spoof));
  
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_virtual_answer_rarp(lcrzo_constdata packet,
			     lcrzo_uint32 nboct,
			     const lcrzo_device device,
			     const lcrzo_etha etha,
			     lcrzo_ipl ipl)
{ int retour;
  lcrzo_hdrleth hdrlethrecu, hdrlethenvoye;
  lcrzo_hdrlarp hdrlarprecu, hdrlarpenvoye;
  lcrzo_ipa ipa;
  lcrzo_spoof spoof;

  /*parameters verification*/
  if (packet==NULL && nboct) return(LCRZO_ERR_PANULLPTRSIZE);
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  if (etha==NULL) return(LCRZO_ERR_PANULLPTR);

  /*tentative de decodage ARP*/
  retour=lcrzo_packet_decode_etharp(packet, nboct,
				   &hdrlethrecu, &hdrlarprecu);
  if (retour!=LCRZO_ERR_OK) return(LCRZO_ERR_OK);

  /*on n'est interesse que par les requetes rarp*/
  if ( hdrlethrecu.type != LCRZO_HDRLETH_TYPE_RARP )
    return(LCRZO_ERR_OK);
  if ( hdrlarprecu.op != 3 ) return(LCRZO_ERR_OK);
  
  /*on n'est interesse que par les requetes de etha*/
  if (!lcrzo_etha_equal(hdrlarprecu.hw_dst, etha)) return(LCRZO_ERR_OK);

  /*cree le nouveau entete ethernet*/
  lcrzo_er(lcrzo_hdrleth_initdefault(&hdrlethenvoye));
  memcpy(hdrlethenvoye.src, etha, LCRZO_ETHA_MAXBYTES);
  memcpy(hdrlethenvoye.dst, hdrlarprecu.hw_src, LCRZO_ETHA_MAXBYTES);
  hdrlethenvoye.type=LCRZO_HDRLETH_TYPE_RARP;
    
  /*cree le nouveau entete arp*/
  lcrzo_er(lcrzo_ipa_init_ipl(ipl, ipa));
  lcrzo_er(lcrzo_hdrlarp_initdefault(&hdrlarpenvoye));
  hdrlarpenvoye.op=4;
  memcpy(hdrlarpenvoye.hw_src, etha, LCRZO_ETHA_MAXBYTES);
  memcpy(hdrlarpenvoye.hw_dst, etha, LCRZO_ETHA_MAXBYTES);
  memcpy(hdrlarpenvoye.prot_src, ipa, LCRZO_IPA_MAXBYTES);
  memcpy(hdrlarpenvoye.prot_dst, ipa, LCRZO_IPA_MAXBYTES);
    
  /*envoi du packet*/
  lcrzo_er(lcrzo_spoof_init(&spoof));
  lcrzo_er(lcrzo_spoof_etharp(&spoof, device, hdrlethenvoye, hdrlarpenvoye));
  lcrzo_er(lcrzo_spoof_close(&spoof));

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_virtual_answer_ping(lcrzo_constdata packet,
			     lcrzo_uint32 nboct,
			     const lcrzo_device device,
			     const lcrzo_etha etha,
			     lcrzo_ipl ipl)
{ lcrzo_hdrleth hdrlethrecu, hdrlethenvoye;
  lcrzo_hdrlip  hdrliprecu, hdrlipenvoye;
  lcrzo_hdrlicmp hdrlicmprecu, hdrlicmpenvoye;
  lcrzo_data data;
  lcrzo_ipopt opt;
  lcrzo_uint16 nboctdata;
  lcrzo_uint8 nboctopt;
  lcrzo_spoof spoof;
  int retour;

  /*parameters verification*/
  if (packet==NULL && nboct) return(LCRZO_ERR_PANULLPTRSIZE);
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  if (etha==NULL) return(LCRZO_ERR_PANULLPTR);

  /*tentative de decodage ICMP*/
  retour=lcrzo_packet_decodem_ethipopticmpdata(packet, nboct,
					       &hdrlethrecu, &hdrliprecu,
					       opt, &nboctopt, &hdrlicmprecu,
					       &data, &nboctdata);
  if (retour!=LCRZO_ERR_OK) return(LCRZO_ERR_OK);

  /*on n'est interesse que par les echo request*/
  if ( hdrlicmprecu.type != 8 ) 
  { lcrzo_data_free(data);
    return(LCRZO_ERR_OK); 
  }
  
  /*on n'est interesse que par les requetes de etha et ipa*/
  if (!lcrzo_etha_equal(hdrlethrecu.dst, etha)) 
  { lcrzo_data_free(data);
    return(LCRZO_ERR_OK);
  }
  if (!lcrzo_ipl_equal(hdrliprecu.daddr, ipl))
  { lcrzo_data_free(data); 
    return(LCRZO_ERR_OK);
  }

  /*cree le nouveau entete ethernet*/
  lcrzo_efr(lcrzo_hdrleth_initdefault(&hdrlethenvoye), lcrzo_data_free(data));
  memcpy(hdrlethenvoye.src, etha, LCRZO_ETHA_MAXBYTES);
  memcpy(hdrlethenvoye.dst, hdrlethrecu.src, LCRZO_ETHA_MAXBYTES);
    
  /*cree le nouveau entete ip*/
  lcrzo_efr(lcrzo_hdrlip_initdefault(&hdrlipenvoye), lcrzo_data_free(data));
  hdrlipenvoye.saddr=hdrliprecu.daddr;
  hdrlipenvoye.daddr=hdrliprecu.saddr;

  /*cree le nouveau entete icmp*/
  lcrzo_efr(lcrzo_hdrlicmp_initdefault(&hdrlicmpenvoye),
	    lcrzo_data_free(data));
  hdrlicmpenvoye.type=0;

  /*envoi du packet*/
  lcrzo_efr(lcrzo_spoof_init(&spoof), lcrzo_data_free(data));
  lcrzo_efr(lcrzo_spoof_ethipopticmpdata(&spoof, device,
					 hdrlethenvoye,
					 hdrlipenvoye, opt, nboctopt,
					 hdrlicmpenvoye, 
					 data, nboctdata), 
	    lcrzo_data_free(data));
  lcrzo_data_free(data);
  lcrzo_er(lcrzo_spoof_close(&spoof));

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
int lcrzo_virtual_answer_alive(lcrzo_constdata packet,
				lcrzo_uint32 nboct,
				const lcrzo_device device,
				const lcrzo_etha etha,
				lcrzo_ipl ipl)
{ 
  /*parameters verification*/
  if (packet==NULL && nboct) return(LCRZO_ERR_PANULLPTRSIZE);
  if (device==NULL) return(LCRZO_ERR_PANULLPTR);
  if (etha==NULL) return(LCRZO_ERR_PANULLPTR);

  lcrzo_er(lcrzo_virtual_answer_arp(packet, nboct,
				    device, etha, ipl));
  lcrzo_er(lcrzo_virtual_answer_rarp(packet, nboct,
				     device, etha, ipl));
  lcrzo_er(lcrzo_virtual_answer_ping(packet, nboct,
				     device, etha, ipl));
  return(LCRZO_ERR_OK);
}
