/*
** xiprepl.c for  in 
** 
** Made by 
** Login   <vianney@epita.fr>
** 
** Started on  Wed Sep  1 08:26:45 1999 
** Last update Thu Oct 28 20:24:01 1999 
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "pktio.h"
#include "pat_ether.h"
#include "pat_arp.h"
#include "pat_ip.h"
#include "pat_icmp.h"
#include "pat_udp.h"
#include "pat_tcp.h"
#include "pat_data.h"
#include "xiperr.h"
#include "xipvar.h"

/* converts a signed integer to a static string */
char			*my_itoa(signed_integer)
signed int		signed_integer;
{
  static char		str[STR_BUFSIZ];
  
  str[0] = 0;
  long_to_str((signed long)signed_integer,
	      10,
	      str,
	      sizeof (str));
  return (str);
}

/* swaps field values.
   Returns 0 if OK. Might return various errors. */
t_status		xip_pkt_swap_fields(pkt,field1,field2)
t_pkt			*pkt;
char			*field1;	/* E.g "ip[0].src" */
char			*field2;	/* E.g "ip[0].dst" */
{
  char			buf1[STR_BUFSIZ];
  char			buf2[STR_BUFSIZ];
  t_status		status;
  
  buf1[0] = 0;
  if ((status = pkt_get_field_to_str(pkt,
				     field1,
				     buf1,
				     sizeof (buf1))) != 0)
    return (status);
  buf2[0] = 0;
  if ((status = pkt_get_field_to_str(pkt,
				     field2,
				     buf2,
				     sizeof (buf2))) != 0)
    return (status);
  if ((status = pkt_set_field_from_str(pkt,
				       field1,
				       buf2)) != 0)
    return (status);
  if ((status = pkt_set_field_from_str(pkt,
				       field2,
				       buf1)) != 0)
    return (status);
  return (0);
}

/* swaps "ether.shost" and "ether.dhost" 
   Return 0 if OK. Might return various errors */
t_status		xip_pkt_reply_ether(pkt)
t_pkt			*pkt;
{
  return (xip_pkt_swap_fields(pkt,
			      "ether.shost",
			      "ether.dhost"));
}

/* replies to an arp request.
   if "arp.Op" is "request" then it sets "reply" instead.
   Returns 0 if OK. ERR_XIP_CANTREPLY if arp packet is already a reply.
   Might return various other errors. */
t_status		xip_pkt_reply_arp(pkt)
t_pkt			*pkt;
{
  char			buf[STR_BUFSIZ];
  t_status		status;

  buf[0] = 0;
  if ((status = pkt_get_field_to_str(pkt,
				     "arp.Op",
				     buf,
				     sizeof (buf))) != 0)
    return (status);
  if (!strcmp(buf,"reply"))
    return (ERR_XIP_CANT_REPLY);
  return (pkt_set_field_from_str(pkt,
				 "arp.Op",
				 "reply"));
}

/* replies to an arp ether-ip request.
   Swaps "arpethip.spa" and "arpethip.tpa" fields, put my_ether into
   "arpethip.sha" and puts gost_ip to "arpethip.spa", sets null ethernet
   address to "arpethip.tha".
   Returns 0 if OK. Might return various errors. */
t_status		xip_pkt_reply_arpethip(pkt)
t_pkt			*pkt;
{
  t_status		status;
  char			buf[STR_BUFSIZ];

  if ((status = xip_pkt_swap_fields(pkt,
				    "arpethip.spa",
				    "arpethip.tpa")) != 0)
    return (status);
  buf[0] = 0;
  if ((status = etheraddr_to_str(&my_ether,
				 buf,
				 sizeof (buf))) != 0)
    return (status);
  if ((status = pkt_set_field_from_str(pkt,
				       "arpethip.sha",
				       buf)) != 0)
    return (status);
  buf[0] = 0;
  if ((status = inaddr_to_str(&my_ip,
			      buf,
			      sizeof (buf),
			      FALSE)) != 0)
    return (status);
  if ((status = pkt_set_field_from_str(pkt,
				       "arpethip.spa",
				       buf)) != 0)
    return (status);
  if ((status = pkt_set_field_from_str(pkt,
				       "arpethip.tha",
				       "00:00:00:00:00:00")) != 0)
    return (status);
  return (0);
}

/* replies to an ip packet.
   Swaps "ip.src" and "ip.dst" and increments "ip.id".
   Returns 0 if OK. Might return various errors. */
t_status		xip_pkt_reply_ip(pkt)
t_pkt			*pkt;
{
  t_status		status;

  if ((status = xip_pkt_swap_fields(pkt,
				    "ip.src",
				    "ip.dst")) != 0)
    return (status);
  if ((status = pkt_set_field_from_str(pkt,
				       "ip.id",
				       my_itoa(ip_id++))) != 0)
    return (status);
  return (0);
}

/* replies to an icmp packet.
   If "icmp.type" is echo request, it replies with echo_reply.
   Note: It is quite simplist!
   Returns 0 if OK. Might return various errors */
t_status		xip_pkt_reply_icmp(pkt)
t_pkt			*pkt;
{
  char			buf[STR_BUFSIZ];
  t_status		status;

  buf[0] = 0;
  if ((status = pkt_get_field_to_str(pkt,
				     "icmp.Type",
				     buf,
				     sizeof (buf))) != 0)
    return (status);
  if (!strcmp(buf,"echo_request"))
    {
      if ((status = pkt_set_field_from_str(pkt,
					   "icmp.Type",
					   "echo_reply")) != 0)
	return (status);
    }
  return (0);
}

/* replies to an udp packet.
   Swaps "udp.sport" and "udp.dport" fields.
   Returns 0 if OK. Might return various errors */
t_status		xip_pkt_reply_udp(pkt)
t_pkt			*pkt;
{
  t_status		status;

  if ((status = xip_pkt_swap_fields(pkt,
				    "udp.sport",
				    "udp.dport")) != 0)
    return (status);
  return (0);
}

/* replies to a tcp packet.
   Swaps "tcp.sport" and "tcp.dport" fields.
   Sets "tcp.ack" to "tcp.seq" + 1.
   Returns 0 if OK. Might return various errors */
t_status		xip_pkt_reply_tcp(pkt)
t_pkt			*pkt;
{
  char			buf[STR_BUFSIZ];
  t_tcp_seq		seq;
  t_status		status;

  if ((status = xip_pkt_swap_fields(pkt,
				    "tcp.sport",
				    "tcp.dport")) != 0)
    return (status);
  buf[0] = 0;
  if ((status = pkt_get_field_to_str(pkt,
				     "tcp.seq",
				     buf,
				     sizeof (buf))) != 0)
    return (status);
  seq = strtol(buf,NULL,10);
  if ((status = pkt_set_field_from_str(pkt,
				       "tcp.ack",
				       my_itoa(seq + 1))) != 0)
    return (status);
  buf[0] = 0;
  if ((status = pkt_get_field_to_str(pkt,
				     "tcp.Flags",
				     buf,
				     sizeof (buf))) != 0)
    return (status);
  if (!strcmp(buf,"syn"))
    {
      if ((status = pkt_set_field_from_str(pkt,
					   "tcp.Flags",
					   "syn|ack")) != 0)
	return (status);
    }
  return (0);
}

/* replies to a packet.
   It makes a common reply according to packet protocol.
   It is quite simplist.
   Returns 0 if OK. Might return various errors. ERR_XIP_NOMETHOD
   if no pat was guessed. */
t_status		xip_pkt_reply(pkt)
t_pkt			*pkt;
{
  t_status		status;
  
  xip_pkt_reply_ether(pkt);
  xip_pkt_reply_arp(pkt);
  xip_pkt_reply_arpethip(pkt);
  xip_pkt_reply_ip(pkt);
  xip_pkt_reply_icmp(pkt);
  xip_pkt_reply_udp(pkt);
  xip_pkt_reply_tcp(pkt);
  if ((status = pkt_adapt_len(pkt)) != 0)
    {
      err_print(status,"pkt_adapt_len");
      return (0);
    }
  if ((status = pkt_sum(pkt,NULL)) != 0)
    {
      err_print(status,"pkt_sum");
      return (0);
    }
  return (0);
}
