/*
** pkt.c for  in 
** 
** Made by 
** Login   <vianney@epita.fr>
** 
** Started on  Wed Sep  1 06:40:10 1999 
** Last update Thu Oct 28 20:19:13 1999 
*/
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/stat.h> 
#include <netinet/in.h>
#include <ctype.h>
#include <fcntl.h>
#include <time.h>
#include "pkt.h"
#include "pat_pat.h"
#include "pat_inaddr.h"
#include "pat_subnet.h"

t_pkt			*pkt_new(len,pat,alloc_proc,free_proc,status)
int			len;		
t_pat			*pat;
t_alloc_proc		alloc_proc;	
t_free_proc		free_proc;	
t_status		*status;	
{ 
  t_pkt			*npkt; 

  if ((npkt = alloc_proc(sizeof (t_pkt), 
			 "pkt", 
 			 "pkt_new:pkt", 
 			 status)) == NULL) 
    return (NULL); 
  if ((npkt->buf = alloc_proc(len * sizeof (char), 
 			      "pkt", 
 			      "pkt_new:buf", 
 			      status)) == NULL) 
    { 
      free_proc(npkt, 
 		"pkt", 
 		"pkt_new:pkt"); 
      return (NULL); 
    } 
  npkt->len = npkt->netlen = len; 
  npkt->pat = pat; 
  npkt->timestamp.tv_sec = 0; 
  npkt->timestamp.tv_usec = 0; 
  bzero(npkt->buf,npkt->len); 
  return (npkt); 
} 

t_status		pkt_scratch(src,dst,alloc_proc,free_proc) 
t_pkt			*src;		
t_pkt			*dst;		
t_alloc_proc		alloc_proc;	 
t_free_proc		free_proc;	 
{ 
  char			*nbuf;
  t_status		status;

  if ((nbuf = alloc_proc(src->len * sizeof (char), 
			 "pkt", 
			 "pkt_scratch:buf", 
			 &status)) == NULL) 
    return (status);  
  free_proc(dst->buf,
	    "pkt",
	    "*:buf");
  PKT_COPY(src,dst); 
  dst->buf = nbuf;
  bcopy(src->buf,dst->buf,src->len * sizeof (char)); 
  return (0);
}

t_pkt			*pkt_dup(pkt,alloc_proc,free_proc,status) 
t_pkt			*pkt;		
t_alloc_proc		alloc_proc;	 
t_free_proc		free_proc;	 
t_status		*status;	 
{ 
  t_pkt			*npkt; 

  if ((npkt = alloc_proc(sizeof (t_pkt), 
 			 "pkt", 
 			 "pkt_dup:pkt", 
 			 status)) == NULL) 
    return (NULL); 
  PKT_COPY(pkt,npkt); 
  if ((npkt->buf = alloc_proc(npkt->len * sizeof (char), 
 			      "pkt", 
 			      "pkt_dup:buf", 
 			      status)) == NULL) 
    { 
      free_proc(npkt, 
 		"pkt", 
 		"pkt_dup:pkt"); 
      return (NULL); 
    } 
  bcopy(pkt->buf,npkt->buf,npkt->len * sizeof (char)); 
  return (npkt); 
}

VOID_FUNC		pkt_delete(pkt,free_proc) 
t_pkt			*pkt;		 
t_free_proc		free_proc;	 
{ 
  free_proc(pkt->buf, 
 	    "pkt", 
 	    "*:buf"); 
  free_proc(pkt, 
 	    "pkt", 
 	    "*:pkt"); 
} 

t_status		pkt_save_raw(pkt,fname) 
t_pkt			*pkt; 
char			*fname;		 
{ 
  int			fd; 
  int			cc; 

  if ((fd = open(fname,O_WRONLY|O_CREAT,0600)) < 0) 
    return (ERR_PAT_OPEN); 
  if ((cc = write(fd,pkt->buf,pkt->len)) != pkt->len) 
    return (ERR_PAT_WRITE); 
  close(fd); 
  return (0); 
}

t_status		pkt_load_raw(pkt,fname,alloc_proc,free_proc) 
t_pkt			*pkt;		 
char			*fname;		 
t_alloc_proc		alloc_proc;	 
t_free_proc		free_proc;	 
{ 
  struct stat		st; 
  int			fd; 
  int			cc; 
  t_status		status; 
  char			*buf; 

  if (stat(fname,&st) < 0) 
    return (ERR_PAT_SYSCALL); 
  if ((fd = open(fname,O_RDONLY)) < 0)
    return (ERR_PAT_OPEN);
  if ((buf = alloc_proc(st.st_size,
			"pkt",
			"pkt_load_raw:buf",
			&status)) == NULL)
    {
      close(fd);
      return (status);
    }
  if ((cc = read(fd,buf,st.st_size)) < 0)
    {
      status = ERR_PAT_READ;
      goto bad;
    }
  if (cc != st.st_size)
    {
      status = ERR_PAT_TRUNC_FILE;
      goto bad;
    }
  free_proc(pkt->buf,
	    "pkt",
	    "*:buf");
  pkt->buf = buf;
  pkt->len = pkt->netlen = st.st_size;
  status = 0;
  goto end;
bad:
  free_proc(buf,
	    "pkt",
	    "pkt_load_raw:buf");
end:
  close(fd);
  return (status);
}

t_status		pkt_sub(pkt,sub_pkt,id)
t_pkt			*pkt;		
t_pkt			*sub_pkt;	
t_id			*id;		
{
  t_status		status;
  t_pat			*sub_pat;
  t_off			off;
  
  if (!pkt->pat->sub_proc)
    {
      return (ERR_PAT_NO_METHOD_SUB);
    }
  if ((status = pkt->pat->sub_proc(NULL,
				   id,
				   pkt->buf,
				   pkt->len,
				   &sub_pat)) != 0)
    {
      return (status);
    }
  sub_pkt->pat = sub_pat;
  if (!pkt->pat->off_proc)
    {
      return (ERR_PAT_NO_METHOD_OFF);
    }
  if ((status = pkt->pat->off_proc(NULL,
				   pkt->buf,
				   pkt->len,
				   &off)) != 0)
    return (status);
  if (pkt->len == off)
    {
      status = ERR_PAT_EXACT_MATCH;
      goto dosub;
    }
  if (off == 0)
    {
      return (ERR_PAT_LOOP);
    }
  if (pkt->len < off)
    {
      return (ERR_PAT_TRUNC_PAT);
    }
  status = 0;
dosub:
  sub_pkt->buf = pkt->buf + off;
  sub_pkt->len = pkt->len - off;
  sub_pkt->netlen = pkt->netlen;
  sub_pkt->timestamp = pkt->timestamp;
  return (status);
}

t_status		pkt_walk_i(pkt,
				   do_opts,
				   level,
				   id,
				   proc,
				   data)
t_pkt			*pkt;		
t_boolean		do_opts;		
int			level;
t_id			*id;
t_pkt_walk_proc		proc;
VOID_PTR		data;
{
  t_status		status;
  t_status		status2;
  t_pkt			sub_pkt;
  
#ifdef DEBUG
  if (PAT_VERB(VERB_PAT_PKTWALK))
    {
      char		buf[STR_BUFSIZ];
      t_status		lstatus;
      int		i;	

      buf[0] = 0;
      lstatus = pat_to_str(pkt->pat,
			   buf,
			   sizeof (buf));
      fprintf(stderr,"+pktwalk+ ");
      i = 0;
      while (i < level)
	{
	  fprintf(stderr," ");
	  i++;
	}
      fprintf(stderr,"%s\n",buf);
    }
#endif
  if ((status = proc(pkt,
		     level,
		     id,
		     data)) != 0)
    return (status);
  if (do_opts)
    {
      t_boolean		has_opt;
      t_off		opt_off;
      int		opt_len;
      t_pat		*opt_pat;
      VOID_PTR		opt_data;

      if (pkt->pat->has_opt_proc)
	{
	  if ((status = pkt->pat->has_opt_proc(NULL,
					       pkt->buf,
					       pkt->len,
					       &has_opt,
					       &opt_off,
					       &opt_len,
					       &opt_pat,
					       &opt_data)) != 0)
	    return (status);
	  if (has_opt)
	    {
	      t_pkt	opt_pkt;
	      
	      if ((opt_off + opt_len) > pkt->len)
		return (ERR_PAT_OPT_BAD_MATCH);
	      PKT_COPY(pkt,&opt_pkt);
	      opt_pkt.pat = opt_pat;
	      opt_pkt.buf = pkt->buf + opt_off;
	      opt_pkt.len = opt_len;
	      if ((status = pkt_walk_i(&opt_pkt,
				       do_opts,
				       level + 1,
				       id,
				       proc,
				       data)) != 0)
		{
		  switch (status)
		    {
		    case ERR_PAT_TRUNC_PAT:
		      break ;
		    default:
		      return (status);
		    }
		}
	    }
	}
    }
  if ((status = pkt_sub(pkt,
			&sub_pkt,
			id)) != 0)
    {
      switch (status)
	{
	case ERR_PAT_NO_METHOD_SUB:
	case ERR_PAT_EXACT_MATCH:
	  return (0);
	default:
	  return (status);
	}
    }
  status = pkt_walk_i(&sub_pkt,
		      do_opts,
		      level,
		      id,
		      proc,
		      data);
  return (status);
}

t_status			pkt_walk(pkt,do_opts,level,proc,data)
t_pkt				*pkt;		
t_boolean			do_opts;		
int				level;
t_pkt_walk_proc			proc;
VOID_PTR			data;
{
  t_id				*id;
  t_status			status;

  if ((id = PAT_TINY_ID_NEW(&status)) == NULL)
    return (status);
  status = pkt_walk_i(pkt,
		      do_opts,
		      level,
		      id,
		      proc,
		      data);
  id_delete(id);
  return (status);
}

t_status			pkt_on_i_walk(pkt,level,id,pod)
t_pkt				*pkt;
int				level;
t_id				*id;
t_pkt_on_data			*pod;
{
  t_status			status;
  char				buf[STR_BUFSIZ];

  buf[0] = 0;
  if (!pkt->pat->name_proc)
    return (ERR_PAT_NO_METHOD_NAME);
  if ((status = pkt->pat->name_proc(NULL,
				    id,
				    buf,
				    sizeof (buf))) != 0)
    return (status);
  if (!strncmp(pod->patname,buf,pod->patlen))
    {
      return (pod->proc(pkt,
			level,
			id,
			pod->data));
    }
  return (0);
}

t_status			pkt_on_i(pkt,
					 do_opts,
					 patname,
					 patlen,
					 id,
					 proc,
					 data)
t_pkt				*pkt;
t_boolean			do_opts;
char				*patname;
int				patlen;
t_id				*id;
t_pkt_walk_proc			proc;
VOID_PTR			data;
{
  t_pkt_on_data			pod;

  pod.proc = proc;
  pod.data = data;
  pod.patname = patname;
  pod.patlen = patlen;
  return (pkt_walk_i(pkt,
		     do_opts,
		     0,
		     id,
		     (t_pkt_walk_proc)pkt_on_i_walk,
		     &pod));
}

t_status			pkt_on(pkt,do_opts,patname,patlen,proc,data)
t_pkt				*pkt;
t_boolean			do_opts;
char				*patname;
int				patlen;
t_pkt_walk_proc			proc;
VOID_PTR			data;
{
  t_pkt_on_data			pod;

  pod.proc = proc;
  pod.data = data;
  pod.patname = patname;
  pod.patlen = patlen;
  return (pkt_walk(pkt,
		   do_opts,
		   0,
		   (t_pkt_walk_proc)pkt_on_i_walk,
		   &pod));
}

t_status			pkt_sum(pkt,up_pkt) 
t_pkt				*pkt;
t_pkt				*up_pkt;
{ 
  t_pkt				sub_pkt; 
  t_status			status; 
  t_pkt				tmp_pkt; 

  if ((status = pkt_sub(pkt,
			&sub_pkt,
			NULL)) != 0)
    { 
      switch (status)
	{
	case ERR_PAT_EXACT_MATCH:
	case ERR_PAT_NO_METHOD_SUB:
	  return (0);
	default:
	  return (status);
	}
    }
  PKT_COPY(pkt,&tmp_pkt); 
  if ((status = pkt_sum(&sub_pkt,
			&tmp_pkt)) != 0) 
     	    return (status); 
  if (pkt->pat->sum_proc)
    {
      if ((status = pkt->pat->sum_proc(NULL,
				       pkt->buf,
				       pkt->len,
				       up_pkt?up_pkt->buf:NULL,
				       up_pkt?up_pkt->len:0)) != 0)
	return (status);
    }
  return (0); 
} 

t_status			pkt_adapt_len(pkt)
t_pkt				*pkt;
{
  t_pkt				sub_pkt;
  t_status			status;

  if ((status = pkt_sub(pkt,
			&sub_pkt,
			NULL)) != 0)
    {
      switch (status)
	{
	case ERR_PAT_EXACT_MATCH:
	case ERR_PAT_NO_METHOD_SUB:
	  return (0);
	default:
	  return (status);
	}
    }
  if ((status = pkt_adapt_len(&sub_pkt)) != 0)
    return (status);
  if (pkt->pat->adapt_len_proc)
    {
      if ((status = pkt->pat->adapt_len_proc(NULL,
					     pkt->buf,
					     pkt->len)) != 0)
	return (status);
    }
  return (0);
}

t_status			pkt_get_field_to_str_i_on(pkt,
							  level,
							  id,
							  pgftsd)
t_pkt				*pkt;
int				level;
t_id				*id;
t_pkt_get_field_to_str_data	*pgftsd;
{
  t_status			status;
  
  if (!pkt->pat->get_field_proc)
    return (ERR_PAT_NO_METHOD_GET_FIELD);
  if ((status = pkt->pat->get_field_proc(NULL,
					 id,
					 pkt->buf,
					 pkt->len,
					 pgftsd->field,
					 pgftsd->str,
					 pgftsd->max_len)) != 0)
    return (status);
  else
    return (ERR_PAT_BREAK);
}

t_status			pkt_get_field_to_str_i(pkt,
						       fq_field,
						       str,
						       max_len,
						       id)
t_pkt				*pkt;		
char				*fq_field;	
char				*str;		
int				max_len;	
t_id				*id;		
{
  t_status			status;
  char				*field;
  int				patlen;
  t_pkt_get_field_to_str_data	pgftsd;
  
#ifdef DEBUG
  if (PAT_VERB(VERB_PAT_PKT))
    fprintf(stderr,"+pkt+ getting field %s\n",fq_field);
#endif
  if (!strcmp(fq_field,"Pat"))
    {
      return (pat_to_str(pkt->pat,
			 str,
			 max_len));
    }
  if (!strcmp(fq_field,"Len"))
    {
      return (long_to_str((signed long)(pkt->len),
			  pat_base,
			  str,
			  max_len));
    }
  if (!strcmp(fq_field,"Netlen"))
    {
      return (long_to_str((signed long)(pkt->netlen),
			  pat_base,
			  str,
			  max_len));
    }
  if (!strcmp(fq_field,"timestamp") || !strcmp(fq_field,"Timestamp"))
    {
      return (timestamp_to_str(&pkt->timestamp,
			       (int)pat_zone,
			       str,
			       max_len,
			       fq_field[0] == 'T'));
    }
  if ((field = index(fq_field,'.')) == NULL)
    return (ERR_PAT_NO_SUCH_FIELD);
  patlen = field - fq_field;
  field++;
  pgftsd.str = str;
  pgftsd.max_len = max_len;
  pgftsd.field = field;
  if ((status = pkt_on_i(pkt,
			 TRUE,
			 fq_field,
			 patlen,
			 id,
			 (t_pkt_walk_proc)pkt_get_field_to_str_i_on,
			 &pgftsd)) != 0)
    {
      switch (status)
	{
	case ERR_PAT_BREAK:
	  return (0);
	default:
	  return (status);
	}
    }
  else
    return (0);
}

t_status			pkt_get_field_to_str(pkt,field,str,max_len)
t_pkt				*pkt;		
char				*field;		
char				*str;		
int				max_len;	
{
  t_id				*id;
  t_status			status;
  
  if ((id = PAT_TINY_ID_NEW(&status)) == NULL)
    return (status);
  status = pkt_get_field_to_str_i(pkt,
				  field,
				  str,
				  max_len,
				  id);
  id_delete(id);
  return (status);
}

t_status			pkt_set_field_from_str_i_on(pkt,
							    level,
							    id,
							    psffsd)
t_pkt				*pkt;
int				level;
t_id				*id;
t_pkt_set_field_from_str_data	*psffsd;
{
  t_status			status;

  if (!pkt->pat->set_field_proc)
    return (ERR_PAT_NO_METHOD_SET_FIELD);
  if ((status = pkt->pat->set_field_proc(NULL,
					 id,
					 pkt->buf,
					 pkt->len,
					 psffsd->field,
					 psffsd->value)) != 0)
    return (status);
  else
    return (ERR_PAT_BREAK);
}

t_status			pkt_set_field_from_str_i(pkt,fq_field,value,id)
t_pkt				*pkt;		
char				*fq_field;	
char				*value;		
t_id				*id;		
{
  t_status			status;
  char				*field;
  int				patlen;
  t_pkt_set_field_from_str_data	psffsd;
  
#ifdef DEBUG
  if (PAT_VERB(VERB_PAT_PKT))
    fprintf(stderr,"+pkt+ setting field %s to %s\n",fq_field,value);
#endif
  if (!strcmp(fq_field,"Pat"))
    {
      t_pat		*pat;

      if ((pat = pat_from_str(value,
			      &status)) == NULL)
	return (status);
      pkt->pat = pat;
      return (0);
    }
  if ((field = index(fq_field,'.')) == NULL)
    return (ERR_PAT_NO_SUCH_FIELD);
  patlen = field - fq_field;
  field++;
  psffsd.value = value;
  psffsd.field = field;
  if ((status = pkt_on_i(pkt,
			 TRUE,
			 fq_field,
			 patlen,
			 id,
			 (t_pkt_walk_proc)pkt_set_field_from_str_i_on,
			 &psffsd)) != 0)
    {
      switch (status)
	{
	case ERR_PAT_BREAK:
	  return (0);
	default:
	  return (status);
	}
    }
  else
    return (0);
}

t_status			pkt_set_field_from_str(pkt,field,value)
t_pkt				*pkt;		
char				*field;		
char				*value;		
{
  t_id				*id;
  t_status			status;

  if ((id = PAT_TINY_ID_NEW(&status)) == NULL)
    return (status);
  status = pkt_set_field_from_str_i(pkt,
				    field,
				    value,
				    id);
  id_delete(id);
  return (status);
}

t_status			pkt_get_choices_i_on(pkt,level,id,pgcd)
t_pkt				*pkt;
int				level;
t_id				*id;
t_pkt_get_choices_data		*pgcd;
{
  t_pat				*field_pat;
  VOID_PTR			field_data;
  t_status			status;

  if (!pkt->pat->get_field_pat_proc)
    return (ERR_PAT_NO_METHOD_GET_FIELD_PAT);
  if ((status = pkt->pat->get_field_pat_proc(NULL,
					     id,
					     pkt->buf,
					     pkt->len,
					     pgcd->field,
					     &field_pat,
					     &field_data)) != 0)
    return (status);
  if (!field_pat->get_choices_proc)
    return (ERR_PAT_NO_METHOD_GET_CHOICES);
  if ((status = field_pat->get_choices_proc(field_data,
					    pkt->buf,
					    pkt->len,
					    pgcd->vec_str,
					    pgcd->do_sort_return)) == 0)
    return (ERR_PAT_BREAK);
  return (0);
}

t_status			pkt_get_choices_i(pkt,
						  fq_field,
						  vec_str,
						  do_sort_return,
						  id)
t_pkt				*pkt;		
char				*fq_field;	
t_vec				*vec_str;	
t_boolean			*do_sort_return;
t_id				*id;		
{
  t_status			status;
  char				*field;
  int				patlen;
  t_pkt_get_choices_data	pgcd;
    
#ifdef DEBUG
  if (PAT_VERB(VERB_PAT_PKT))
    fprintf(stderr,"+pkt+ getting choices of %s\n",fq_field);
#endif
  if (!strcmp(fq_field,"Pat"))
    {
      return (pat_displayable_get_choices(vec_str));
    }
  if ((field = index(fq_field,'.')) == NULL)
    return (ERR_PAT_NO_SUCH_FIELD);
  patlen = field - fq_field;
  field++;
  pgcd.field = field;
  pgcd.vec_str = vec_str;
  pgcd.do_sort_return = do_sort_return;
  if ((status = pkt_on_i(pkt,
			 TRUE,
			 fq_field,
			 patlen,
			 id,
			 (t_pkt_walk_proc)pkt_get_choices_i_on,
			 &pgcd)) != 0)
    {
      switch (status)
	{
	case ERR_PAT_BREAK:
	  return (0);
	default:
	  return (status);
	}
    }
  else
    return (0);
}

t_status		pkt_get_choices(pkt,field,vec_str,do_sort_return)
t_pkt			*pkt;		
char			*field;		
t_vec			*vec_str;	
t_boolean		*do_sort_return;
{
  t_id			*id;
  t_status		status;

  if ((id = PAT_TINY_ID_NEW(&status)) == NULL)
    return (status);
  status = pkt_get_choices_i(pkt,
			     field,
			     vec_str,
			     do_sort_return,
			     id);
  id_delete(id);
  return (status);
}

t_status			pkt_get_tmpl_to_str_walk(pkt,
							 level,
							 id,
							 pgttsd)
t_pkt				*pkt;
int				level;
t_id				*id;
t_pkt_get_tmpl_to_str_data	*pgttsd;
{
  t_status			status;

  if (!pkt->pat->get_tmpl_proc)
    return (ERR_PAT_NO_METHOD_GET_TMPL);
  if ((status = pkt->pat->get_tmpl_proc(NULL,
					id,
					pkt->buf,
					pkt->len,
					pgttsd->str,
					pgttsd->max_len)) != 0)
    return (status);
  return (0);
}

t_status			pkt_get_tmpl_to_str(pkt,str,max_len,do_opts)
t_pkt				*pkt;		
char				*str;		
int				max_len;		
t_boolean			do_opts;	
{
  t_id				*id;
  t_status			status;
  t_pkt_get_tmpl_to_str_data	pgttsd;

  if ((id = PAT_TINY_ID_NEW(&status)) == NULL)
    return (status);
  pgttsd.str = str;
  pgttsd.max_len = max_len;
  status = pkt_walk_i(pkt,
		      do_opts,
		      0,
		      id,
		      (t_pkt_walk_proc)pkt_get_tmpl_to_str_walk,
		      &pgttsd);
  id_delete(id);
  return (status);
}

VOID_FUNC		timestamp_from_str(str,tv)
char			*str;	
struct timeval		*tv;	
{
  char			*ptr;

  if (ptr = index(str,'.'))
    *ptr++ = 0;
  else
    ptr = "0";
  tv->tv_sec = atoi(str);
  tv->tv_usec = atoi(ptr);
}

t_status		timestamp_to_str(tv,zone,str,max_len,resolve)
struct timeval		*tv;		
int			zone;		
char			*str;		
int			max_len;	
t_boolean		resolve;	
{
  t_status		status;
  
  if (resolve)
    {
      unsigned int	s;
      
      s = (tv->tv_sec + zone) % 86400;
      return (str_cat_fmt_va(str,
			     max_len,
			     "%02u:%02u:%02u.%06u",
			     s / 3600,
			     (s % 3600) / 60,
			     s % 60,
			     (unsigned int)(tv->tv_usec)));
    }
  else
    return (str_cat_fmt_va(str,
			   max_len,
			   "%u.%06u",
			   (unsigned int)(tv->tv_sec),
			   (unsigned int)(tv->tv_usec)));
}

t_status		local_to_str(pkt,
				     vec_prefixes,
				     str,
				     max_len,
				     remote,
				     resolve,
				     get_field_to_str_proc)
t_pkt			*pkt;			
t_vec			*vec_prefixes;	
char			*str;		
int			max_len;	
t_boolean		remote;		
t_boolean		resolve;	
t_pkt_get_field_to_str_proc	get_field_to_str_proc;
{
  char			src[STR_BUFSIZ];
  char			dst[STR_BUFSIZ];
  struct in_addr	inaddrsrc;
  struct in_addr	inaddrdst;
  t_boolean		srcislocal;
  t_boolean		dstislocal;
  struct in_addr	*wantedinaddr;
  t_status		status;

  src[0] = 0;
  if ((status = get_field_to_str_proc(pkt,
				      "ip.src",
				      src,
				      sizeof (src))) != 0)
      return (status);
  dst[0] = 0;
  if ((status = get_field_to_str_proc(pkt,
				      "ip.dst",
				      dst,
				      sizeof (dst))) != 0)
      return (status);
  if ((status = inaddr_from_str(src,&inaddrsrc,FALSE)) != 0)
    return (status);
  if ((status = inaddr_from_str(dst,&inaddrdst,FALSE)) != 0)
    return (status);
  srcislocal = FALSE;
  VEC_FOR(vec_prefixes,t_subnet *subnet)
    {
      if (is_subnet_member(&inaddrsrc,subnet))
	{
	  srcislocal = TRUE;
	  break ;
	}
    }
  VEC_ENDFOR;
  dstislocal = FALSE;
  VEC_FOR(vec_prefixes,t_subnet *subnet)
    {
      if (is_subnet_member(&inaddrdst,subnet))
	{
	  dstislocal = TRUE;
	  break ;
	}
    }
  VEC_ENDFOR;
#define REMOTESRC	(remote?&inaddrdst:&inaddrsrc)
#define REMOTEDST	(remote?&inaddrsrc:&inaddrdst)
  if (srcislocal != dstislocal)
    wantedinaddr = srcislocal?REMOTESRC:REMOTEDST;
  else
    wantedinaddr = (inaddr_cmp(&inaddrsrc,&inaddrdst) < 0)?REMOTESRC:REMOTEDST;
  return (inaddr_to_str(wantedinaddr,
			str,
			max_len,
			resolve));
}

t_status		pkt_format_do(bs,var,pfdd)
t_bridled_str		*bs;		
char			*var;		
t_pkt_format_do_data	*pfdd;	       
{
  int			varlen;
  long			signedvalue;
  t_status		status;

  varlen = strlen(var);
  if (varlen == 0)
    return (str_cat_char(bs->str,bs->max_len,'%'));
  else
    if (isdigit(var[0]))
      {
	if (var[0] == '0')
	  {
	    if (var[1] == 'x' || var[1] == 'X')
	      signedvalue = strtol(var,NULL,16);
	    else
	      signedvalue = strtol(var,NULL,8);
	  }
	else
	  signedvalue = strtol(var,NULL,10);
	return (str_cat_char(bs->str,bs->max_len,(int)signedvalue));
      }
    else
      {
	time_t		t;
	struct tm	*tmp;

	if (!strcmp(var,"Do_opts"))
	  {
	    return (str_cat_str(bs->str,
				bs->max_len,
				(pfdd->do_opts)?"@+":"@-"));
	  }
	if (pfdd->vec_prefixes)
	  {
	    if (!strcmp(var,"local") || !strcmp(var,"Local"))
	      return (local_to_str(pfdd->pkt,
				   pfdd->vec_prefixes,
				   bs->str,
				   bs->max_len,
				   FALSE,
				   var[0] == 'L',
				   pfdd->get_field_to_str_proc));
	    if (!strcmp(var,"remote") || !strcmp(var,"Remote"))
	      return (local_to_str(pfdd->pkt,
				   pfdd->vec_prefixes,
				   bs->str,
				   bs->max_len,
				   TRUE,
				   var[0] == 'R',
				   pfdd->get_field_to_str_proc));
	  }
	if ((status = pfdd->get_field_to_str_proc(pfdd->pkt,
						  var,
						  bs->str,
						  bs->max_len)) != 0)
	  {
	    switch (status)
	      {
	      case ERR_PAT_TRUNC_FIELD:
		return (str_cat_str(bs->str,
				    bs->max_len,
				    "|"));
	      case ERR_PAT_CANT_HANDLE:
		return (str_cat_str(bs->str,
				    bs->max_len,
				    "?"));
	      case ERR_PAT_NO_SUCH_FIELD:
		break ;
	      default:
		return (status);
	      }
	  }
	if (pfdd->extra_vars)
	  {
	    t_hash_elt	*he;

	    if (he = dict_get(pfdd->extra_vars,var))
	      return (str_cat_str(bs->str,
				  bs->max_len,
				  he->value));
	  }
	time(&t);
	tmp = localtime(&t);
	if (!strcmp(var,"wday"))
	  return (long_to_str((signed long)(tmp->tm_wday),
			      10,
			      bs->str,
			      bs->max_len));
	if (!strcmp(var,"mon"))
	  return (long_to_str((signed long)(tmp->tm_mon),
			      10,
			      bs->str,
			      bs->max_len));
	if (!strcmp(var,"mday"))
	  return (long_to_str((signed long)(tmp->tm_mday),
			      10,
			      bs->str,
			      bs->max_len));
	if (!strcmp(var,"hour"))
	  return (long_to_str((signed long)(tmp->tm_hour),
			      10,
			      bs->str,
			      bs->max_len));
	if (!strcmp(var,"min"))
	  return (long_to_str((signed long)(tmp->tm_min),
			      10,
			      bs->str,
			      bs->max_len));
	if (!strcmp(var,"sec"))
	  return (long_to_str((signed long)(tmp->tm_sec),
			      10,
			      bs->str,
			      bs->max_len));
	if (!strcmp(var,"year"))
	  return (long_to_str((signed long)(tmp->tm_year),
			      10,
			      bs->str,
			      bs->max_len));
      }
  return (0);
}

t_status		pkt_format_htmlize_do(bs,var,pfdd)
t_bridled_str		*bs;		
char			*var;		
t_pkt_format_do_data	*pfdd;
{
  t_status		status;
  t_bridled_str		htmlize_bs;
  char			*ptr;

  pfdd->htmlize_str[0] = 0;
  htmlize_bs.str = pfdd->htmlize_str;
  htmlize_bs.max_len = pfdd->htmlize_max_len;
  if ((status = pfdd->tmpl_do_proc(&htmlize_bs,var,pfdd)) != 0)
    return (status);
  ptr = htmlize_bs.str;
  while (*ptr)
    {
      switch (*ptr)
	{
	case '\n':
	  if ((status = str_cat_str(bs->str,
				    bs->max_len,
				    "<br>\n")) != 0)
	    return (status);
	  break ;
	case '<':
	  if ((status = str_cat_str(bs->str,
				    bs->max_len,
				    "&lt;")) != 0)
	    return (status);
	  break ;
	case '&':
	  if ((status = str_cat_str(bs->str,
				    bs->max_len,
				    "&amp;")) != 0)
	    return (status);
	  break ;
	default:
	  if ((!isprint(*ptr)) || ((t_u8)(*ptr) > 127)) 
	    {
	      if ((status = str_cat_char(bs->str,
					 bs->max_len,
					 '.')) != 0)
		return (status);
	    }
	  else
	    {
	      if ((status = str_cat_char(bs->str,
					 bs->max_len,
					 *ptr)) != 0)
		return (status);
	    }
	}
      ptr++;
    }
  return (0);
}

t_status		pkt_format_html(pkt,
					do_opts,
					tmpl_str,
					vec_prefixes,
					extra_vars,
					str,
					max_len,
					htmlize_str,
					htmlize_max_len)
t_pkt			*pkt;		
t_boolean		do_opts;
char			*tmpl_str;	
t_vec			*vec_prefixes;	
t_dict			*extra_vars;	
char			*str;		
int			max_len;	
char			*htmlize_str;	
int			htmlize_max_len;
{
  t_pkt_format_do_data	pfdd;

  pfdd.pkt = pkt;
  pfdd.do_opts = do_opts;
  pfdd.vec_prefixes = vec_prefixes;
  pfdd.get_field_to_str_proc = pkt_get_field_to_str;
  pfdd.extra_vars = extra_vars;
  pfdd.tmpl_do_proc = (t_tmpl_do_proc)pkt_format_do;
  pfdd.htmlize_str = htmlize_str;
  pfdd.htmlize_max_len = htmlize_max_len;
  return (tmpl_str_to_str(tmpl_str,
			  (t_tmpl_do_proc)pkt_format_htmlize_do,
			  &pfdd,
			  str,
			  max_len));
}

t_status		pkt_trunc_on(pkt,level,id,ptd)
t_pkt			*pkt;
int			level;
t_id			*id;
t_pkt_trunc_data	*ptd;
{
  t_status		status;
  char			*nbuf;
  int			nlen;
  t_pkt			sub_pkt;

  if ((status = pkt_sub(pkt,
			&sub_pkt,
			id)) != 0)
    return (status);
  nlen = ptd->ref_pkt->len - sub_pkt.len;
  if ((nbuf = ptd->alloc_proc(nlen * sizeof (char),
			      "pkt",
			      "pkt_trunc:buf",
			      &status)) == NULL)
    return (status);
  bcopy(ptd->ref_pkt->buf,nbuf,nlen);
  ptd->free_proc(ptd->ref_pkt->buf,
		 "pkt",
		 "*:buf");
  ptd->ref_pkt->buf = nbuf;
  ptd->ref_pkt->len = nlen;
  return (ERR_PAT_BREAK);
}

t_status		pkt_trunc(pkt,patname,alloc_proc,free_proc)
t_pkt			*pkt;		
char			*patname;		
t_alloc_proc		alloc_proc;
t_free_proc		free_proc;
{
  t_status		status;
  t_pkt_trunc_data	ptd;
  
  ptd.patname = patname;
  ptd.ref_pkt = pkt;
  ptd.alloc_proc = alloc_proc;
  ptd.free_proc = free_proc;
  if ((status = pkt_on(pkt,
		       TRUE,
		       patname,
		       strlen(patname),
		       (t_pkt_walk_proc)pkt_trunc_on,
		       &ptd)) != 0)
    {
      switch (status)
	{
	case ERR_PAT_BREAK:
	  return (0);
	default:
	  return (status);
	}
    }
  else
    return (0);
}

t_status		pkt_extract_on(pkt,level,id,ptd)
t_pkt			*pkt;
int			level;
t_id			*id;
t_pkt_trunc_data	*ptd;
{
  t_status		status;
  char			*nbuf;
  int			nlen;
  t_pkt			sub_pkt;

  if ((nbuf = ptd->alloc_proc(pkt->len * sizeof (char),
			      "pkt",
			      "pkt_extract:buf",
			      &status)) == NULL)
    return (status);
  ptd->free_proc(ptd->ref_pkt->buf,
		 "pkt",
		 "*:buf");
  bcopy(pkt->buf,nbuf,pkt->len);
  ptd->ref_pkt->buf = nbuf;
  ptd->ref_pkt->len = pkt->len;
  ptd->ref_pkt->pat = pkt->pat;
  return (ERR_PAT_BREAK);
}

t_status		pkt_extract(pkt,patname,alloc_proc,free_proc)
t_pkt			*pkt;		
char			*patname;		
t_alloc_proc		alloc_proc;
t_free_proc		free_proc;
{
  t_status		status;
  t_pkt_trunc_data	ptd;

  ptd.patname = patname;
  ptd.ref_pkt = pkt;
  ptd.alloc_proc = alloc_proc;
  ptd.free_proc = free_proc;
  if ((status = pkt_on(pkt,
		       TRUE,
		       patname,
		       strlen(patname),
		       (t_pkt_walk_proc)pkt_extract_on,
		       &ptd)) != 0)
    {
      switch (status)
	{
	case ERR_PAT_BREAK:
	  return (0);
	default:
	  return (status);
	}
    }
  else
    return (0);
}

t_status		pkt_pat_length_on(pkt,level,id,length_return)
t_pkt			*pkt;
int			level;
t_id			*id;
t_off			*length_return;
{
  t_status		status;

  if (!pkt->pat->off_proc)
    return (ERR_PAT_NO_METHOD_OFF);
  if ((status = pkt->pat->off_proc(NULL,
				   pkt->buf,
				   pkt->len,
				   length_return)) != 0)
    return (status);
  return (ERR_PAT_BREAK);
}

t_status		pkt_pat_length(pkt,patname,length_return)
t_pkt			*pkt;
char			*patname;
t_off			*length_return;
{
  t_status		status;
  
  if ((status = pkt_on(pkt,
		       TRUE,
		       patname,
		       strlen(patname),
		       (t_pkt_walk_proc)pkt_pat_length_on,
		       length_return)) != 0)
    {
      switch (status)
	{
	case ERR_PAT_BREAK:
	  return (0);
	default:
	  return (status);
	}
    }
  else
    return (0);
}

t_status		pkt_subn(pkt,
				 subn_pkt,
				 wanted_name,
				 wanted_name_len,
				 do_opts)
t_pkt			*pkt;
t_pkt			*subn_pkt;
char			*wanted_name;
int			wanted_name_len;
t_boolean		do_opts;
{
  return (ERR_PAT_NI);
}

t_status		pkt_cut_internal(pkt,
					 fine,
					 cutbufpkt,
					 alloc_proc,
					 free_proc)
t_pkt			*pkt;
t_boolean		fine;
t_pkt			*cutbufpkt;
t_alloc_proc		alloc_proc;
t_free_proc		free_proc;
{
  char			*nbuf;
  t_status		status;

  if (fine)
    {
      t_off		off;
      
      if (!pkt->pat->off_proc)
	return (ERR_PAT_NO_METHOD_OFF);
      if ((status = pkt->pat->off_proc(NULL,
					  pkt->buf,
					  pkt->len,
					  &off)) != 0)
	return (status);
      pkt->len = MY_MIN(pkt->len,off);
    }
  if ((nbuf = alloc_proc(pkt->len * sizeof (char),
			 "pkt",
			 "pkt_cut:buf",
			 &status)) == NULL)
    return (status);
  bcopy(pkt->buf,nbuf,pkt->len);
  free_proc(cutbufpkt->buf,
	    "pkt",
	    "*:buf");
  cutbufpkt->pat = pkt->pat;
  cutbufpkt->buf = nbuf;
  cutbufpkt->len = pkt->len;
  cutbufpkt->netlen = pkt->netlen;
  return (0);
}

t_status		pkt_cut_on(pkt,level,id,pcd)
t_pkt			*pkt;
int			level;
t_id			*id;
t_pkt_cut_data		*pcd;
{
  return (pkt_cut_internal(pkt,
			   pcd->fine,
			   pcd->cutbufpkt,
			   pcd->alloc_proc,
			   pcd->free_proc));
}

t_status		pkt_cut(pkt,
				patname,
				cutbufpkt,
				fine,
				alloc_proc,
				free_proc)
t_pkt			*pkt;		
char			*patname;	   
t_pkt			*cutbufpkt;	
t_boolean		fine;		
t_alloc_proc		alloc_proc;	
t_free_proc		free_proc;
{
  t_pkt			sub_pkt;
  t_status		status;

  if (patname)
    {
      t_pkt_cut_data	pcd;

      pcd.cutbufpkt = cutbufpkt;
      pcd.fine = fine;
      pcd.alloc_proc = alloc_proc;
      pcd.free_proc = free_proc;
      return (pkt_on(pkt,
		     TRUE,
		     patname,
		     strlen(patname),
		     (t_pkt_walk_proc)pkt_cut_on,
		     &pcd));
    }
  else
    {
      return (pkt_cut_internal(pkt,
			       fine,
			       cutbufpkt,
			       alloc_proc,
			       free_proc));
		     
    }
}

t_status		pkt_paste_internal(pkt,
					   ref_pkt,
					   fine,
					   cutbufpkt,
					   alloc_proc,
					   free_proc)
t_pkt			*pkt;
t_pkt			*ref_pkt;
t_boolean		fine;
t_pkt			*cutbufpkt;
t_alloc_proc		alloc_proc;
t_free_proc		free_proc;
{
  char			*nbuf;
  int			nlen;
  t_status		status;

  if (fine)
    {
      t_off		off;
      
      if (!pkt->pat->off_proc)
	return (ERR_PAT_NO_METHOD_OFF);
      if ((status = pkt->pat->off_proc(NULL,
					  pkt->buf,
					  pkt->len,
					  &off)) != 0)
	return (status);
      pkt->len = MY_MIN(pkt->len,off);
      if (cutbufpkt->len > pkt->len)
	{
	  return (ERR_PAT_ROOM);
	}
    }
  if (cutbufpkt->len > pkt->len)
    {
      nlen = ref_pkt->len - pkt->len + cutbufpkt->len;
      if ((nbuf = alloc_proc(nlen * sizeof (char),
			     "pkt",
			     "pkt_paste:buf",
			     &status)) == NULL)
	return (status);
      bzero(nbuf,nlen);
      bcopy(ref_pkt->buf,nbuf,ref_pkt->len);
      free_proc(ref_pkt->buf,
		"pkt",
		"*:buf");
      ref_pkt->buf = nbuf;
      ref_pkt->len = nlen;
    }
  bcopy(cutbufpkt->buf,pkt->buf,cutbufpkt->len);
  return (0);
}

t_status		pkt_paste_on(pkt,level,id,ppd)
t_pkt			*pkt;
int			level;
t_id			*id;
t_pkt_paste_data	*ppd;
{
  return (pkt_paste_internal(pkt,
			     ppd->ref_pkt,
			     ppd->fine,
			     ppd->cutbufpkt,
			     ppd->alloc_proc,
			     ppd->free_proc));
}

t_status		pkt_paste(pkt,
				  patname,
				  cutbufpkt,
				  fine,
				  alloc_proc,
				  free_proc)
t_pkt			*pkt;		
char			*patname;	   
t_pkt			*cutbufpkt;	
t_boolean		fine;		
t_alloc_proc		alloc_proc;
t_free_proc		free_proc;
{
  t_status		status;
  char			*nbuf;
  int			nlen;

  if (patname)
    {
      t_pkt_paste_data	ppd;

      ppd.ref_pkt = pkt;
      ppd.cutbufpkt = cutbufpkt;
      ppd.fine = fine;
      ppd.alloc_proc = alloc_proc;
      ppd.free_proc = free_proc;
      return (pkt_on(pkt,
		     TRUE,
		     patname,
		     strlen(patname),
		     (t_pkt_walk_proc)pkt_paste_on,
		     &ppd));
    }
  else
    {
      char		*nbuf;
      int		nlen;

      nlen = pkt->len + cutbufpkt->len;
      if ((nbuf = alloc_proc(nlen * sizeof (char),
			     "pkt",
			     "pkt_paste:buf",
			     &status)) == NULL)
	return (status);
      bcopy(pkt->buf,nbuf,pkt->len);
      free_proc(pkt->buf,
		"pkt",
		"*:buf");
      pkt->buf = nbuf;
      bcopy(cutbufpkt->buf,pkt->buf + pkt->len,cutbufpkt->len);
      pkt->len = nlen;
    }
  return (0);
}


