/*
** pat_udp.c for  in 
** 
** Made by 
** Login   <vianney@epita.fr>
** 
** Started on  Wed Sep  1 06:40:34 1999 
** Last update Thu Oct 28 20:19:24 1999 
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "pat_16.h"
#include "pat_port.h"
#include "pat_ip.h"
#include "pat_udp.h"
#include "pat_data.h"
#include "pat_rip.h"
#include "pat_dns.h"
#include "pat_tftp.h"
#include "pat_rpc.h"
#include "pat_bootp.h"

t_boolean		pat_udp_perform_sum = TRUE;
t_boolean		pat_udp_guess_mode = TRUE;
t_id			*pat_udp_sub_id = NULL;

/* initializes pat_udp_sub_id (see udp_sub(3)).
   Returns 0 if OK. Might return various errors. */
t_status		pat_udp_init(VOID_DECL)
{
  t_status		status;

  if ((pat_udp_sub_id = PAT_TINY_ID_NEW(&status)) == NULL)
    return (status);
  return (0);
}

/* deletes pat_udp_sub_id */
VOID_FUNC		pat_udp_destroy(VOID_DECL)
{
  id_delete(pat_udp_sub_id);
}

/* computes an udp checksum.
   It is portable and deals with alignment.
   Returns the checksum. */
t_u16			udp_cksum(ip)
struct s_ip		*ip;
{
   t_udp		*udp;
   t_u16		*sh;
   unsigned long	sum;
   int			len;
   int			i;
   
   udp = (t_udp *)(((char *)ip) + ip_get_hl(ip) * 4);
   len = safe_ntohs(&udp->ulen);
   sum = 0;
   sh = (t_u16 *)(&(ip->src));
   i = 0;
   while (i < UDP_ALEN)
     {
       t_u16		u16;

       FBCOPY(sh,&u16,sizeof (u16)); /* alignment */
       sum += UNSAFE_HTONS(u16);
       sh++;
       i++;
     }
   sh = (t_u16 *)udp;
   sum += ip->p + len;
   if (len & 0x1)
   {
      ((char *)udp)[len] = 0;
      len += 1;
   }
   len /= 2;
   i = 0;
   while (i < len)
     {
       t_u16		u16;

       FBCOPY(sh,&u16,sizeof (u16)); /* alignment */
       sum += UNSAFE_HTONS(u16);
       sh++;
       i++;
     }
   sum = (sum >> 16) + (sum & 0xffff);
   sum += (sum >> 16);
   return ((t_u16)(~sum & 0xffff));
}

/* computes udp checksum. 
   Note: It deals with alignment. 
   It calls udp_cksum(3). */
VOID_FUNC		udp_compute_sum(ip)
struct s_ip		*ip;
{
   t_udp		*udp;
   t_u16		zero;

   udp = (t_udp *)((char *)ip + ip_get_hl(ip) * 4);
   zero = 0;
   FBCOPY(&zero,&udp->sum,sizeof (zero)); /* alignment */
   safe_htons(udp_cksum(ip),&udp->sum);
}

/* sets udp protocol to a ip header */
VOID_FUNC		udp_set_proto(ip)
t_ip			*ip;
{
  ip_set_p(ip,IP_PROTO_UDP);
}

/* sets udp source port.
   Note: deals with alignment. */
VOID_FUNC		udp_set_sport(udp,sport)
t_udp			*udp;
int			sport;
{
  safe_htons((t_u16)(sport),&udp->sport);
}

/* gets udp source port.
   Note: deals with alignment. */
int			udp_get_sport(udp)
t_udp			*udp;
{
  return (safe_ntohs(&udp->sport));
}

/* sets udp destination port.
   Note: deals with alignment. */
VOID_FUNC		udp_set_dport(udp,dport)
t_udp			*udp;
int			dport;
{
  safe_htons((t_u16)(dport),&udp->dport);
}

/* gets udp destination port.
   Note: deals with alignment. */
int			udp_get_dport(udp)
t_udp			*udp;
{
  return (safe_ntohs(&udp->dport));
}

/* sets udp length.
   Note: deals with alignment. */
VOID_FUNC		udp_set_ulen(udp,ulen)
t_udp			*udp;
{
  safe_htons(ulen,&udp->ulen);
}

/* gets udp length.
   Note: deals with alignment. */
int			udp_get_ulen(udp)
t_udp			*udp;
{
  return (safe_ntohs(&udp->ulen));
}

/* sets udp checksum.
   Note: deals with alignment. */
VOID_FUNC		udp_set_sum(udp,sum)
t_udp			*udp;
int			sum;
{
  safe_htons(sum,&udp->sum);
}

/* gets udp checksum.
   Note: deals with alignment. */
int			udp_get_sum(udp)
t_udp			*udp;
{
  return (safe_ntohs(&udp->sum));
}	

t_field				udp_fields[] = 
{
  {"sport",	OFFSET(t_udp *,sport),	&nu16_pat,		NULL},
  {"Sport",	OFFSET(t_udp *,sport),	&port_resolved_pat,	"udp"},
  {"dport",	OFFSET(t_udp *,dport),	&nu16_pat,		NULL},
  {"Dport",	OFFSET(t_udp *,dport),	&port_resolved_pat,	"udp"},
  {"ulen",	OFFSET(t_udp *,ulen),	&nu16_pat,		NULL},
  {"sum",	OFFSET(t_udp *,sum),	&nu16_pat,		NULL},
  NULL_FIELD
};

char				*udp_itmpl = "\n\
<table _name=udp[%i%] width=100%%%% bgcolor=\"%%udpColor%%\">\n\
<tr>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(udp[%i%].Sport)\">%%udp[%i%].Sport%%</a>\n\
(<a href=\"set(udp[%i%].sport)\">%%udp[%i%].sport%%</a>)\n\
</td>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(udp[%i%].Dport)\">%%udp[%i%].Dport%%</a>\n\
(<a href=\"set(udp[%i%].dport)\">%%udp[%i%].dport%%</a>)\n\
</td>\n\
</tr>\n\
<tr>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(udp[%i%].ulen)\">%%udp[%i%].ulen%%</a>\n\
</td>\n\
<td align=center width=50%%%%>\n\
<a href=\"set(udp[%i%].sum)\">%%udp[%i%].sum%%</a>\n\
</td>\n\
</tr>\n\
</table>\n\
<br>\n\
";

int	udp_chan;

PAT_NAME_GENERIC(udp_pat_name,
		 (VOID_PTR)&udp_chan,
		 "udp")

PAT_GET_FIELD_GENERIC(udp_pat_get_field,
		      udp_fields)

PAT_SET_FIELD_GENERIC(udp_pat_set_field,
		      udp_fields)

PAT_GET_FIELDS_GENERIC(udp_pat_get_fields,
		       udp_fields)

PAT_GET_FIELD_PAT_GENERIC(udp_pat_get_field_pat,
			  udp_fields)

PAT_GET_TMPL_GENERIC(udp_pat_get_tmpl,
		     (VOID_PTR)&udp_chan,
		     udp_itmpl)

PAT_OFF_GENERIC(udp_pat_off,
		UDP_HLEN)

/* performs an udp checksum.
   This procedure is called by pat_udp_pat(3) and corresponds to the 
   meta-checksum-mechanism of pkt_sum(3).
   It checks the standard header len (UDP_HLEN) and calls udp_compute_sum(3).
   Returns 0 if OK. Returns ERR_PAT_BADLEN if the given length is different
   from the one retrieved by udp_get_ulen(3). It also check for
   ip header (given by up_buf and up_len) in the same way as ip_sum(3).
   Might of course return ERR_PAT_TRUNC_PAT. */
t_status		udp_sum(buf,len,up_buf,up_len)
char			*buf;
int			len;
char			*up_buf;
int			up_len;
{
  t_udp			*udp;
  t_ip			*ip;

  PAT_UDP_CHECK(udp,buf,len);
  if (udp_get_ulen(udp) != len)
    return (ERR_PAT_BAD_LEN);
  PAT_IP_CHECK(ip,up_buf,up_len);
  if (ip_get_len(ip) != up_len)
    return (ERR_PAT_BAD_LEN);
  if (ip_get_hl(ip) * 4 < IP_MINHLEN)
    return (ERR_PAT_BAD_HEADER);
  udp_compute_sum(ip);
  return (0);
}

/* sets zero to udp checksum.
   It is used by pat_udp_pat(3) if pat_udp_perform_sum is FALSE.
   Performs the same checks as in udp_sum(3)
   Returns 0 if OK. */
t_status		udp_zero_sum(buf,len,up_buf,up_len)
char			*buf;
int			len;
char			*up_buf;
int			up_len;
{
  t_udp			*udp;
  t_ip			*ip;

  PAT_UDP_CHECK(udp,buf,len);
  PAT_IP_CHECK(ip,up_buf,up_len);
  udp->sum = 0;
  return (0);
}

PAT_SUM_DECL(udp_pat_sum)
{
  if (pat_udp_perform_sum)
    return (udp_sum(buf,
		    len,
		    up_buf,
		    up_len));
  else
    return (udp_zero_sum(buf,
			 len,
			 up_buf,
			 up_len));
}

PAT_SUB_DECL(udp_pat_sub)
{
  t_udp			*udp;

  PAT_UDP_CHECK(udp,buf,len);
  if (pat_udp_guess_mode)
    {
      t_u16		dport;
      t_u16		sport;

      dport = udp_get_dport(udp);
      switch (dport)
	{
	case RIP_PORT:
	  (*pat_return) = &rip_pat;
	  return (0);
	case DNS_PORT:
	  (*pat_return) = &dns_pat;
	  return (0);
	case TFTP_PORT:
	  (*pat_return) = &tftp_pat;
	  return (0);
	case RPC_PORT:
	  (*pat_return) = &rpc_pat;
	  return (0);
	case BOOTPS_PORT:
	case BOOTPC_PORT:
	  (*pat_return) = &bootp_pat;
	  return (0);
	}
      sport = udp_get_sport(udp);
      switch (sport)
	{
	case RIP_PORT:
	  (*pat_return) = &rip_pat;
	  return (0);
	case DNS_PORT:
	  (*pat_return) = &dns_pat;
	  return (0);
	case TFTP_PORT:
	  (*pat_return) = &tftp_pat;
	  return (0);
	case RPC_PORT:
	  (*pat_return) = &rpc_pat;
	  return (0);
	case BOOTPS_PORT:
	case BOOTPC_PORT:
	  (*pat_return) = &bootp_pat;
	  return (0);
	}
      if (pat_udp_sub_id)
	{
	  t_hash_elt	*he;

	  if (he = id_get(pat_udp_sub_id,
			  (VOID_PTR)(t_u32)dport))
	    {
	      (*pat_return) = (t_pat *)(he->value);
	      return (0);
	    }
	  else
	    if (he = id_get(pat_udp_sub_id,
			    (VOID_PTR)(t_u32)sport))
	      {
		return (0);
		(*pat_return) = (t_pat *)(he->value);
	      }
	}
    }
  (*pat_return) = &data_pat;
  return (0);
}

PAT_ADAPT_LEN_DECL(udp_pat_adapt_len)
{
  t_udp			*udp;
  
  PAT_UDP_CHECK(udp,buf,len);
  udp_set_ulen(udp,len);
  return (0);
}

t_pat				udp_pat = 
{
  udp_pat_name,			/* t_pat_name_proc		*/
  udp_pat_off,			/* t_pat_off_proc		*/
  udp_pat_sub,			/* t_pat_sub_proc		*/
  udp_pat_sum,			/* t_pat_sum_proc		*/
  udp_pat_get_field,		/* t_pat_get_field_proc		*/
  udp_pat_set_field,		/* t_pat_set_field_proc		*/
  udp_pat_get_fields,		/* t_pat_get_fields_proc	*/
  udp_pat_get_tmpl,		/* t_pat_get_tmpl_proc		*/
  NULL,				/* t_pat_get_tmpl2_proc		*/
  NULL,				/* t_pat_has_opt_proc		*/
  udp_pat_adapt_len,		/* t_pat_adapt_len_proc		*/
  udp_pat_get_field_pat,	/* t_pat_get_field_pat_proc	*/
  NULL,				/* t_pat_extract_proc		*/
  NULL,				/* t_pat_insert_proc		*/
  NULL,				/* t_pat_get_choices_proc	*/
};
