/* This file is part of Lcrzo.
*/


/*-------------------------------------------------------------*/
#if LCRZODEF_LIBNETINSTALLED==1
int lcrzo_priv_spoof_init_ip(lcrzo_spoof *pspoof)
{
  /*ouverture*/
  pspoof->fd=libnet_open_raw_sock(IPPROTO_RAW);
  if ( pspoof->fd == -1 )
  { return(LCRZO_ERR_FULIBNETOPENRAWSOCK);
  }
  return(LCRZO_ERR_OK);
}
int lcrzo_priv_spoof_ip(lcrzo_spoof *pspoof,
			lcrzo_constdata packet, 
			lcrzo_int32 nboctpacket)
{ int retour;
  lcrzo_data paq;

  /*parameters verification*/
  if (packet==NULL && nboctpacket) return(LCRZO_ERR_PANULLPTRSIZE);
  if (nboctpacket==0) return(LCRZO_ERR_OK);

  /*ecriture*/
  lcrzo_er(lcrzo_data_initm_data(packet, nboctpacket, &paq, NULL));
  retour=libnet_write_ip(pspoof->fd, paq, nboctpacket);
  lcrzo_data_free(paq);
  if ( retour == -1 )
  { return(LCRZO_ERR_FULIBNETWRITEIP);
  }
  return(LCRZO_ERR_OK);
}
int lcrzo_priv_spoof_close_ip(lcrzo_spoof *pspoof)
{ int retour;
  
  /*fermeture*/
  retour=libnet_close_raw_sock(pspoof->fd);
  if ( retour == -1 )
  { return(LCRZO_ERR_FULIBNETCLOSERAWSOCK);
  }

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
#elif defined LCRZODEF_SYSTEM_Linux
int lcrzo_priv_spoof_init_ip(lcrzo_spoof *pspoof)
{ int i;

  /*ouverture de la socket*/ 
  pspoof->fd=socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  i=0;
  while( pspoof->fd<0 && i<500 )
  { if (errno!=ENOBUFS) return(LCRZO_ERR_FUSOCKET);
    /*sinon, attend 500ms. En effet, sur des machines puissantes, le
      programme sature le systeme*/
    i++;
    lcrzo_er(lcrzo_time_sleep_usec(1000));
    pspoof->fd=socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  }
  /*on est toujours sature*/
  if(pspoof->fd<0) return(LCRZO_ERR_FUSOCKET);

  return(LCRZO_ERR_OK);
}
int lcrzo_priv_spoof_ip(lcrzo_spoof *pspoof,
			lcrzo_constdata packet, 
			lcrzo_int32 nboctpacket)
{ int retour, un;
  struct sockaddr_in soin;
  lcrzo_ipl ipl;

  /*parameters verification*/
  if (packet==NULL && nboctpacket) return(LCRZO_ERR_PANULLPTRSIZE);
  if (nboctpacket==0) return(LCRZO_ERR_OK);

  /*uniquement pour faire plaisir a sendto (send() ne marche pas)*/
  memset(&soin, 0, sizeof(struct sockaddr_in));
  soin.sin_family = AF_INET;
  if ( nboctpacket>19 ) /*sinon essaie sans initialiser, mais ca peut ne pas
                       marcher. Selon mes tests, ca depend des machines ...*/
  { lcrzo_ipl_init(packet[16], packet[17], packet[18], packet[19], &ipl);
    soin.sin_addr.s_addr = lcrzo_htonl(ipl);
  }

  /*l'entete IP est inclus*/
  un=1;
  retour = LAURENT_setsockopt(pspoof->fd, IPPROTO_IP, IP_HDRINCL,
			      &un, sizeof(un));
  if (retour) return(LCRZO_ERR_FUSETSOCKOPT);

  /*autorise a envoyer des packets broadcast*/
  un=1;
  retour = LAURENT_setsockopt(pspoof->fd, SOL_SOCKET, SO_BROADCAST,
			      &un, sizeof(un));
  if (retour) return(LCRZO_ERR_FUSETSOCKOPT);

  /*envoi*/
  retour=LAURENT_sendto(pspoof->fd, packet, nboctpacket, 0,
			(struct sockaddr*)&soin, sizeof(struct sockaddr));
  if(retour<0) return(LCRZO_ERR_FUSENDTO);

  return(LCRZO_ERR_OK);
}
int lcrzo_priv_spoof_close_ip(lcrzo_spoof *pspoof)
{ int retour;
  
  /* fermeture */
  retour=close(pspoof->fd);
  if (retour==-1) return(LCRZO_ERR_FUCLOSE);

  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
#elif defined LCRZODEF_SYSTEM_FreeBSD || defined LCRZODEF_SYSTEM_OpenBSD
int lcrzo_priv_spoof_init_ip(lcrzo_spoof *pspoof)
{
  /*ouverture de la socket*/ 
  pspoof->fd=socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  if(pspoof->fd<0) return(LCRZO_ERR_FUSOCKET);

  return(LCRZO_ERR_OK);
}
int lcrzo_priv_spoof_ip(lcrzo_spoof *pspoof,
			lcrzo_constdata packet, 
			lcrzo_int32 nboctpacket)
{ int retour, un;
  struct sockaddr_in soin;
  lcrzo_ipl ipl;

  /*parameters verification*/
  if (packet==NULL && nboctpacket) return(LCRZO_ERR_PANULLPTRSIZE);
  if (nboctpacket==0) return(LCRZO_ERR_OK);


  /*uniquement pour faire plaisir a sendto (send() ne marche pas)*/
  memset(&soin, 0, sizeof(struct sockaddr_in));
  soin.sin_family = AF_INET;
  if ( nboctpacket>19 ) /*sinon essaie sans initialiser, mais ca peut ne pas
                       marcher. Selon mes tests, ca depend des machines ...*/
  { lcrzo_ipl_init(packet[16], packet[17], packet[18], packet[19], &ipl);
    soin.sin_addr.s_addr = lcrzo_htonl(ipl);
  }

  /*l'entete IP est inclus*/
  un=1;
  retour = LAURENT_setsockopt(pspoof->fd, IPPROTO_IP, IP_HDRINCL, 
			      &un, sizeof(un));
  if (retour) return(LCRZO_ERR_FUSETSOCKOPT);

  /*autorise a envoyer des packets broadcast*/
  un=1;
  retour = LAURENT_setsockopt(pspoof->fd, SOL_SOCKET, SO_BROADCAST,
			      &un, sizeof(un));
  if (retour) return(LCRZO_ERR_FUSETSOCKOPT);

#if defined LCRZODEF_SYSTEM_FreeBSD 
  { lcrzo_data paq;
    /*a partir de maintenant, on travaille sur la copie*/
    lcrzo_er(lcrzo_data_initm_data(packet, nboctpacket, &paq, NULL));
    /* because of a FreeBSD bug, we have to revert iplen and offset*/
    if (nboctpacket>=4)
    { paq[2]=packet[3];
      paq[3]=packet[2];
    }
    if (nboctpacket>=8)
    { paq[6]=packet[7];
      paq[7]=packet[6];
    }
    /*send*/
    retour=LAURENT_sendto(pspoof->fd, paq, nboctpacket, 0,
			  (struct sockaddr*)&soin, sizeof(struct sockaddr));
    lcrzo_data_free(paq);
  }
#elif defined LCRZODEF_SYSTEM_OpenBSD
  /*send*/
  retour=LAURENT_sendto(pspoof->fd, packet, nboctpacket, 0,
			(struct sockaddr*)&soin, sizeof(struct sockaddr));
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif
  if(retour<0) return(LCRZO_ERR_FUSENDTO);

  return(LCRZO_ERR_OK);
}
int lcrzo_priv_spoof_close_ip(lcrzo_spoof *pspoof)
{ int retour;
  
  /* fermeture */
  retour=close(pspoof->fd);
  if (retour==-1) return(LCRZO_ERR_FUCLOSE);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
#elif defined LCRZODEF_SYSTEM_Solaris
int lcrzo_priv_spoof_init_ip(lcrzo_spoof *pspoof)
{
  /*ouverture de la socket*/ 
  pspoof->fd=socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  if(pspoof->fd<0) return(LCRZO_ERR_FUSOCKET);

  return(LCRZO_ERR_OK);
}
int lcrzo_priv_spoof_ip(lcrzo_spoof *pspoof,
			lcrzo_constdata packet, 
			lcrzo_int32 nboctpacket)
{ int retour;
  struct sockaddr_in soin;
  lcrzo_ipl ipl;
  lcrzo_data paq;

  /*parameters verification*/
  if (packet==NULL && nboctpacket) return(LCRZO_ERR_PANULLPTRSIZE);
  if (nboctpacket==0) return(LCRZO_ERR_OK);

  /*uniquement pour faire plaisir a sendto (send() ne marche pas)*/
  memset(&soin, 0, sizeof(struct sockaddr_in));
  soin.sin_family = AF_INET;
  if ( nboctpacket>19 ) /*sinon essaie sans initialiser, mais ca peut ne pas
                       marcher. Selon mes tests, ca depend des machines ...*/
  { lcrzo_ipl_init(packet[16], packet[17], packet[18], packet[19], &ipl);
    soin.sin_addr.s_addr = lcrzo_htonl(ipl);
  }

  /*a partir de maintenant, on travaille sur la copie*/
  lcrzo_er(lcrzo_data_initm_data(packet, nboctpacket, &paq, NULL));

  /*on n'utilise pas lcrzo_packet_decode_... qui est trop "gros" pour
    ce que l'on veut faire*/
  if ( nboctpacket>(lcrzo_int32)sizeof(lcrzo_hdrpip) )
  { lcrzo_uint8 ip_protocol;
    lcrzo_uint8 ip_ihl, ip_ihl4;

    ip_ihl=(lcrzo_uint8)(0x0F&paq[0]);
    ip_ihl4=(lcrzo_uint8)(ip_ihl*4);
    if ( ip_ihl>=5 ) /*sinon packet incorrect : touche pas*/
    { ip_protocol=paq[9];
#if defined LCRZODEF_SYSVER_Solaris_24 || defined LCRZODEF_SYSVER_Solaris_25
      /*a cause d'une erreur du noyau Solaris, il faut mettre :
        hdrtcp.check=lcrzo_htons(hdrtcp.doff<<2);
        hdrudp.check=hdrudp.len;
	Si on met une valeur differente de celles calculees ci-dessus, le
	noyau Solaris envoie un checksum incorrect sur le reseau.
	Par contre, en lui envoyant la valeur ci-dessus, il calcule 
	correctement le checksum.
	C'est un bug assez ennuyeux car il nous empeche d'envoyer un
	checksum falsifie : il faut utiliser pour cela lcrzo_spoof_eth.
      */
      /*correction bug checksum tcp*/
      if ( ip_protocol==LCRZO_HDRLIP_PROTOCOL_TCP )
      { if ( nboctpacket>=(lcrzo_int32)(ip_ihl4+sizeof(lcrzo_hdrptcp)) )
	{ lcrzo_uint8 tcp_dataoff;
	  lcrzo_uint16 check;
	  tcp_dataoff=(lcrzo_uint8)((paq[ip_ihl4+12])>>4);
          /*affecte hdrtcp.check*/
          check=lcrzo_htons(tcp_dataoff<<2);
          paq[ip_ihl4+16]=(lcrzo_uint8)(check>>8);
          paq[ip_ihl4+17]=(lcrzo_uint8)(check&0xFF);
	}
      }
      /*correction bug checksum udp*/
      if ( ip_protocol==LCRZO_HDRLIP_PROTOCOL_UDP )
      { if ( nboctpacket>=(lcrzo_int32)(ip_ihl4+sizeof(lcrzo_hdrpudp)) )
	{ /*affecte hdrudp.check*/
          paq[ip_ihl4+6]=paq[ip_ihl4+4];
          paq[ip_ihl4+7]=paq[ip_ihl4+5];
	}
      }
#elif defined LCRZODEF_SYSVER_Solaris_26 || defined LCRZODEF_SYSVER_Solaris_27 || defined LCRZODEF_SYSVER_Solaris_28
      /* checksum doesn't have to be corrected */
#else
 #error "Unknown value for LCRZODEF_SYSVER"
#endif

      /*changement de place des options : les options comprises dans
        le packet ne sont pas reconnues, il faut les affecter comme cela*/
      if ( ip_ihl>5 )
      { if ( nboctpacket>=ip_ihl4 ) /*toutes les opt sont presentes*/
	{ lcrzo_uint16 nboctopt;
	  nboctopt=(lcrzo_uint8)((ip_ihl-5)*4);
          /*affecte les options*/
          retour = LAURENT_setsockopt(pspoof->fd, IPPROTO_IP, IP_OPTIONS,
			      paq+sizeof(lcrzo_hdrpip), nboctopt);
          if (retour)
          { lcrzo_data_free(paq); 
	    return(LCRZO_ERR_FUSETSOCKOPT); 
	  }
          /*les supprime du packet a envoyer*/  
          memcpy(paq+sizeof(lcrzo_hdrpip), paq+ip_ihl4, nboctpacket-ip_ihl4);
	  nboctpacket -= nboctopt;
	  /*modifie ihl*/
	  paq[0]= (lcrzo_uint8)( (paq[0]&0xF0) | 5 );
#if defined LCRZODEF_SYSVER_Solaris_27 || defined LCRZODEF_SYSVER_Solaris_28
	  { lcrzo_uint16 ip_totlen;
	    /*also change totlen*/
	    ip_totlen = (lcrzo_uint8)( (paq[2]<<8) | paq[3] );
	    if ( ip_totlen > nboctopt )
	    { ip_totlen = (lcrzo_uint16)( ip_totlen - nboctopt );
	      paq[2] = (lcrzo_uint8)( ip_totlen << 8 );
	      paq[3] = (lcrzo_uint8)( ip_totlen & 0xFF );
	    }
	  }
#elif defined LCRZODEF_SYSVER_Solaris_24 || defined LCRZODEF_SYSVER_Solaris_25 || defined LCRZODEF_SYSVER_Solaris_26
	  /* doesn't need to change totlen */
	  /* Note : I didn't verify on Solaris 2.6, and I suppose it has
	     to be treated like 2.4 or 2.5, and not 2.7 or 2.8 */
#else
 #error "Unknown value for LCRZODEF_SYSVER"
#endif
          /*solaris recalcule le checksum IP pour nous*/
	}
      }
    }
  }

  /*envoi*/
  retour=LAURENT_sendto(pspoof->fd, paq, nboctpacket, 0,
			(struct sockaddr*)&soin, sizeof(struct sockaddr));
  lcrzo_data_free(paq);
  if(retour<0) return(LCRZO_ERR_FUSENDTO);

  return(LCRZO_ERR_OK);
}
int lcrzo_priv_spoof_close_ip(lcrzo_spoof *pspoof)
{ int retour;
  
  /* fermeture */
  retour=close(pspoof->fd);
  if (retour==-1) return(LCRZO_ERR_FUCLOSE);
  return(LCRZO_ERR_OK);
}

/*-------------------------------------------------------------*/
#elif defined LCRZODEF_SYSTEM_Windows
int lcrzo_priv_spoof_init_ip(lcrzo_spoof *pspoof)
{ return(LCRZO_ERR_IEUNIMPLEMENTED);
  /* just for the compiler warning */
  pspoof=pspoof;
}
int lcrzo_priv_spoof_ip(lcrzo_spoof *pspoof,
			lcrzo_constdata packet, 
			lcrzo_int32 nboctpacket)
{ return(LCRZO_ERR_IEUNIMPLEMENTED);
  /* just for the compiler warning */
  pspoof=pspoof;
  packet=packet;
  nboctpacket=nboctpacket;
}
int lcrzo_priv_spoof_close_ip(lcrzo_spoof *pspoof)
{ return(LCRZO_ERR_IEUNIMPLEMENTED);
  /* just for the compiler warning */
  pspoof=pspoof;
}
#if 0
/* seems to work only with Win2k.
   This should be included in next versions of lcrzo  */
#include <lcrzo_incl.h>
#include "stdio.h"
#include "stdlib.h"
#include <winsock2.h>
#include <ws2tcpip.h>
//#define	IP_HDRINCL  2
void f(void)
{ unsigned int iTrue=1;
  SOCKET s;
  struct sockaddr_in m_TargetAddress;
  s=socket(AF_INET, SOCK_RAW, IPPROTO_IP);
  if (s==INVALID_SOCKET) puts("socket");
  if(LAURENT_setsockopt(s,IPPROTO_IP,IP_HDRINCL,&iTrue,sizeof(iTrue))==SOCKET_ERROR)puts("sockopt");
  memset(&m_TargetAddress,0,sizeof(m_TargetAddress));
  m_TargetAddress.sin_family=AF_INET;
  m_TargetAddress.sin_addr.s_addr=inet_addr("192.168.201.10");
  m_TargetAddress.sin_port=htons(122);
  LAURENT_sendto(s,"\105\000\000\056\004\270\100\000\200\006\270\025\300\250\311\021\300\250\311\012\141\142\012\143\144\145\146\147\150\151\152\153\154\155\156\157\160\161\162\163\164\165\166\167\170\171", 46,0,
			   (struct sockaddr*)&m_TargetAddress,sizeof(m_TargetAddress));
  closesocket(s);
}
int main(void)
{
  WORD wVersionRequested;
  WSADATA wsaData;
  int err;
  wVersionRequested = MAKEWORD( 2, 2 );
  err = WSAStartup( wVersionRequested, &wsaData );
  if (err!=0) return FALSE;
  if (LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
  { WSACleanup();
    return FALSE;
  }
  f();
  WSACleanup();
  return 0;
}
#endif

/*-------------------------------------------------------------*/
#else
 #error "Unknown value for LCRZODEF_SYSTEM"
#endif

