/**************************************************************************** 
** File: packet_manip.c
**
** Author: Mike Borella
**
** Comments: Packet manipulation routines
**
** $Id: packet_manip.c,v 1.16 2001/10/09 23:20:42 mborella Exp $
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program 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 Library General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

#include "packet_manip.h"
#include "hexbuffer.h"

extern struct arg_t *my_args;

/*----------------------------------------------------------------------------
**
** get_packet_bytesgeneral()
**
** Non API function, this is a generalization of get_packet_bytes() and
** get_packet_bytestoend().
**
** Mode 0 is get_packet_bytes()
** Mode 1 is get_packet_bytestoend()
**
** NOTE: This is NOT a safe function. The caller MUST allocate enough memory 
** in the destination array.
**
** Returns 0 if operation fails and no write occurs.  Returns 1 otherwise.
**----------------------------------------------------------------------------
*/

inline int get_packet_bytesgeneral(u_int8_t *dst, packet_t *p, unsigned int n, 
			    int mode)
{
  /* 
   * First make sure that we can read the bytes without running off the end 
   * of the packet.
   */

  switch(mode)
    {
    case 0:
      if (p->current + n > p->apparent_end + 1)
	return 0;
      break;

    case 1:
      if (p->current + n > p->end + 1)
	return 0;
      break;

    default:
      error_message("unknown mode for get_packet_bytesgeneral()");
      return 0;
    }

  /* 
   * Do the copy
   */

  memcpy(dst, p->current, n);
  hexbuffer_add(p->current, n);

  /* 
   * Increment the current pointer
   */

  p->current += n;

  /* 
   * Return successfully
   */

  return 1;
}

/*----------------------------------------------------------------------------
**
** get_packet_bytes()
**
** Grab a specified number of bytes from a packet and deposit them in an array.
**
** NOTE: This is NOT a safe function. The caller MUST allocate enough memory 
** in the destination array.
**
** Returns 0 if operation fails and no write occurs.  Returns 1 otherwise.
**----------------------------------------------------------------------------
*/

inline int get_packet_bytes(u_int8_t *dst, packet_t *p, unsigned int n)
{
  return get_packet_bytesgeneral(dst, p, n, 0);
}


/*----------------------------------------------------------------------------
**
** get_packet_bytestoend()
**
** Grab a specified number of bytes from a packet and deposit them in an array.
** Get bytes all the way to the real end of the packet, not the apparent end.
**
** NOTE: This is NOT a safe function. The caller MUST allocate enough memory 
** in the destination array.
**
** Returns 0 if operation fails and no write occurs.  Returns 1 otherwise.
**----------------------------------------------------------------------------
*/

inline int get_packet_bytestoend(u_int8_t *dst, packet_t *p, unsigned int n)
{
  return get_packet_bytesgeneral(dst, p, n, 1);
}

/*----------------------------------------------------------------------------
**
** get_packet_line()
**
** Get a \n terminated line of text from the packet.
**
** NOTE: This is NOT a safe function. The caller MUST allocate enough memory 
** in the destination array.
**
** Returns 0 if operation fails and no write occurs.
** Returns number of bytes read otherwise.
**----------------------------------------------------------------------------
*/

inline int get_packet_line(u_int8_t *dst, u_int32_t max, packet_t *p)
{
  u_int8_t * ptr;
  int        ret;

  /*
   * Find the next \n
   */
  
  ptr = p->current;
  while (ptr < p->apparent_end && (ptr - p->current) < max - 1)
    {
      if (*ptr == '\n')
	break;
      ptr++;
    }

  /*
   * If we reached the end, then there's no line to read, fail.
   */

  if (ptr >= p->apparent_end)
    return 0;

  /* 
   * Do the copy (NOTE, we do not copy the \n)
   * We add a \0 in the appropriate place.
   */

  memcpy(dst, p->current, ptr - p->current);
  dst[ptr - p->current] = '\0';
  hexbuffer_add(p->current, ptr - p->current);

  /* 
   * Increment the current pointer past the \n
   */

  ret = ptr - p->current;
  p->current += ret + 1;

  /* 
   * Return successfully the length of the text, not including the \n
   */

  return ret;
}

/*----------------------------------------------------------------------------
**
** get_packet_string()
**
** Get a \0 terminated string of text from the packet.
**
** NOTE: This is NOT a safe function. The caller MUST allocate enough memory 
** in the destination array.
**
** Returns 0 if operation fails and no write occurs.
** Returns number of bytes read otherwise.
**----------------------------------------------------------------------------
*/

inline int get_packet_string(u_int8_t *dst, u_int32_t max, packet_t *p)
{
  u_int8_t * ptr;
  int        ret;

  /*
   * Find the next \0
   */
  
  ptr = p->current;
  while (ptr < p->apparent_end && (ptr - p->current) < max - 1)
    {
      if (*ptr == '\0')
	break;
      ptr++;
    }

  /*
   * If we reached the end, then there's no line to read, fail.
   */

  if (ptr >= p->apparent_end)
    return 0;

  /* 
   * Do the copy (NOTE, we do not copy the \0)
   * We add a \0 in the appropriate place.
   */

  memcpy(dst, p->current, ptr - p->current);
  dst[ptr - p->current] = '\0';
  hexbuffer_add(p->current, ptr - p->current);

  /* 
   * Increment the current pointer past the \0
   */

  ret = ptr - p->current;
  p->current += ret + 1;

  /* 
   * Return successfully the length of the text
   */

  return ret;
}

/*----------------------------------------------------------------------------
**
** look_packet_bytes()
**
** Copy specified number of bytes from a packet and deposit them in an array.
** Unlike get_packet_bytes(), we will not increment the pointer.
**
** NOTE: This is NOT a safe function. The caller MUST allocate enough memory 
** in the destination array.
**
** Returns 0 if operation fails and no write occurs.  Returns 1 otherwise.
**----------------------------------------------------------------------------
*/

inline int look_packet_bytes(u_int8_t *dst, packet_t *p, unsigned int n)
{
  /* 
   * First make sure that we can read the bytes without running off the end 
   * of the packet.
   */

  if (p->current + n > p->apparent_end + 1)
    return 0;
   
  /* 
   * Do the copy
   */

  memcpy(dst, p->current, n);

  /* 
   * Return successfully
   */

  return 1;
}

/*----------------------------------------------------------------------------
**
** skip_packet_bytes()
**
** Jump the pointer ahead the specified number of bytes
**
** Returns 0 if operation fails and no write occurs.  Returns 1 otherwise.
**----------------------------------------------------------------------------
*/

inline int skip_packet_bytes(packet_t *p, unsigned int n)
{
  /* 
   * First make sure that we can read the bytes without running off the end 
   * of the packet.
   */

  if (p->current + n > p->apparent_end + 1)
    return 0;
   
  /* 
   * Increment the current pointer
   */

  p->current += n;
  hexbuffer_add(p->current, n);

  /* 
   * Return successfully
   */

  return 1;
}

/*----------------------------------------------------------------------------
**
** skip_packet_toapparentend()
**
** Jump the pointer ahead the apparent end of the packet.  Useful for 
** jumping a padding section, if one exists.
**
** Returns 0 if operation fails and no write occurs.  Returns 1 otherwise.
**----------------------------------------------------------------------------
*/

inline int skip_packet_toapparentend(packet_t *p)
{
  if (p->current > p->apparent_end)
    return 0;

  /*
   * Set the current pointer
   */

  p->current = p->apparent_end;

  /* 
   * Return successfully
   */

  return 1;
}

/*----------------------------------------------------------------------------
**
** get_packet_apparentbytesleft()
**
** Returns the # of apparent bytes left to read off of the packet.
**
**----------------------------------------------------------------------------
*/

inline u_int32_t get_packet_apparentbytesleft(packet_t *pkt)
{
  u_int32_t len;

  len = pkt->apparent_end - pkt->current;
  if (len < 0) 
    return 0;
  else 
    return len;
}

/*----------------------------------------------------------------------------
**
** get_packet_bytesleft()
**
** Returns the # of bytes left to read off of the packet.
**
**----------------------------------------------------------------------------
*/

inline u_int32_t get_packet_bytesleft(packet_t *pkt)
{
  u_int32_t len;

  len = pkt->end - pkt->current;
  if (len < 0) 
    return 0;
  else 
    return len;
}

/*----------------------------------------------------------------------------
**
** packet_haspadding()
**
** Returns 1 if there are bytes between the apparent end and the real end, 
** 0 otherwise
**
**----------------------------------------------------------------------------
*/

inline int packet_haspadding(packet_t *pkt)
{
  u_int32_t len;

  len = pkt->end - pkt->apparent_end;
  if (len <= 0) 
    return 0;
  else 
    return 1;
}

/*----------------------------------------------------------------------------
**
** set_packet_mark()
**
** Mark the current byte of the packet
**
**----------------------------------------------------------------------------
*/

inline void set_packet_mark(packet_t *pkt)
{
  pkt->mark = pkt->current;
}

/*----------------------------------------------------------------------------
**
** get_packet_markdistance()
**
** Get the distance between the current pointer and the mark
**
**----------------------------------------------------------------------------
*/

inline int32_t get_packet_markdistance(packet_t *pkt)
{
  /*
   * Sanity check the mark first
   */

  if (pkt->mark < pkt->contents || pkt->mark > pkt->apparent_end)
    return -1;

  return pkt->current - pkt->mark;
}

/*----------------------------------------------------------------------------
**
** set_packet_apparentend()
**
** Set the apparent end of the packet to be the given number of bytes beyond 
** the current pointer.
**
**----------------------------------------------------------------------------
*/

inline void set_packet_apparentend(packet_t * pkt, int n)
{
  /* make sure we're not running off the packet */
  if (pkt->current + n >= pkt->apparent_end)
    return;

  pkt->apparent_end = pkt->current + n;
}
