/*

Copyright (C) 2000 - 2006 Christian Kreibich <christian@whoop.org>.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies of the Software and its documentation and acknowledgment shall be
given in the documentation and software packages that this Software was
used.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
#ifndef __libnd_packet_h
#define __libnd_packet_h

#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#include <libnd_types.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/* Netdude's view of a packet: */

struct lnd_packet
{
  /* The pcap header */
  struct pcap_pkthdr    ph;

  guchar               *data;

  /* The allocated size of the data chunk. The packet may actually
   * be using less and can never use more; see ph.caplen for the
   * actually used size.
   */
  guint                 data_size;

  LND_TracePart        *part;

  /* Protocols at various layers into the packet */
  GList                *pd; /* GList<LND_ProtoData*>*/
  
  gint64                protocols;

  /* Pointers to the next/previous selected
     packets, for quicker iteration. */
  struct lnd_packet    *sel_next;
  struct lnd_packet    *sel_prev;

  /* Linked list pointers. */
  struct lnd_packet    *next;
  struct lnd_packet    *prev;

  void                 *user_data;

  gboolean              filtered;

  /* Remember to update nd_packet_duplicate() when adding
   * stuff here !!!
   */
};


/* These are the various codes for packet modifications
 * that can be reported to observers using libnd_packet_set_observer().
 */
typedef enum {
  LND_PACKET_INITIALIZED  = (1 << 0),    /* Packet got initialized */
  LND_PACKET_MODIFIED     = (1 << 1),    /* Packet got modified */
  LND_PACKET_DELETE_PRE   = (1 << 2),    /* Packet gets deleted (pre-action) */
  LND_PACKET_DELETE_POST  = (1 << 3),    /* Packet got deleted (post-action) */
  LND_PACKET_INSERT_PRE   = (1 << 4),    /* Packet gets inserted (pre-action) */
  LND_PACKET_INSERT_POST  = (1 << 5),    /* Packet got inserted (post-action) */
  LND_PACKET_DUPLICATED   = (1 << 6),    /* Packet got duplicated */
  LND_PACKET_VISIBILITY   = (1 << 7),    /* Packet visibility changed */
  LND_PACKET_UPDATED      = (1 << 8),    /* Packet got updated (partial initialization) */
  LND_PACKET_FIXED        = (1 << 9),    /* Packet checksums got fixed */
  LND_PACKET_LEN_CHANGED  = (1 << 10),   /* Packet's wire size changed */
  LND_PACKET_CAPLEN_CHANGED = (1 << 11), /* Packet's caplen changed */
  
  /* If you add something here, also add a callback invocation in
   * libnd_packet_tell_observers().
   */
} LND_PacketObserverOp;


typedef struct lnd_packet_observer
{
  void  (*packet_initialized) (LND_Packet *packet);
  void  (*packet_modified) (LND_Packet *packet);
  void  (*packet_delete_pre) (LND_Packet *packet);
  void  (*packet_delete_post) (LND_Packet *packet);
  void  (*packet_insert_pre) (LND_Packet *packet);
  void  (*packet_insert_post) (LND_Packet *packet);
  void  (*packet_duplicated) (LND_Packet *packet);
  void  (*packet_visibility) (LND_Packet *packet);
  void  (*packet_updated) (LND_Packet *packet);
  void  (*packet_fixed) (LND_Packet *packet);
  void  (*packet_len_changed) (LND_Packet *packet);
  void  (*packet_caplen_changed) (LND_Packet *packet);

} LND_PacketObserver;

/**
 * LND_PacketFunc - callback prototype for iterating over protocols.
 * @packet: the packet over whose protocol data is iterated.
 * @pd: iterated protocol data.
 * @user_data: arbitrary data passed through to this callback.
 *
 * Functions of this signature are used to iterate over the
 * protocols that are contained in packtes. See
 * libnd_packet_foreach_proto() and libnd_packet_foreach_proto_backward().
 *
 * Returns: %TRUE if the iteration is to continue, %FALSE on abort.
 */
typedef gboolean (*LND_PacketFunc)(LND_Packet     *packet,
				   LND_ProtoData  *pd,
				   void           *user_data);


/**
 * libnd_packet_new - allocates a packet.
 * @tp: trace part the new packet belongs to.
 * @data_size: amount of payload the new packet holds.
 *
 * The function creates a packet and returns it. Each packet must
 * belong to a trace. The function does not register the packet
 * in the trace, because a packet could be inserted anywhere.
 *
 * Returns: fresh packet.
 */
LND_Packet     *libnd_packet_new(LND_TracePart *tp, guint data_size);

/**
 * libnd_packet_from_pcap - reads a new packet from a pcap handle.
 * @pcap: pcap handle to read from.
 *
 * The function reads a single packet from @pcap using pcap_next().
 *
 * Returns: a pointer to a filled-in LND_Packet, or %NULL if no
 * packet could be read from @pcap.
 */
LND_Packet     *libnd_packet_from_pcap(pcap_t *pcap);


/**
 * libnd_packet_free - cleans up a packet
 * @packet: packet to delete.
 *
 * This function removes @packet from its trace's
 * packet lists, then frees @packet.
 */
void            libnd_packet_free(LND_Packet *packet);

void            libnd_packet_remove(LND_Packet *packet);


/**
 * libnd_packet_cmp - compares two packets by timestamp.
 * @packet1: first packet.
 * @packet2: second packet.
 *
 * Returns: -1, 0, 1 depending on whether the packet1's timestamp
 * is smaller than, equal, or larger than packet2's. You can use
 * this function as GCompareFunc.
 */
int             libnd_packet_cmp(const LND_Packet *packet1,
				 const LND_Packet *packet2);

/**
 * libnd_packet_get_trace - returns the trace that a packet belongs to.
 * @packet: packet whose trace to return.
 *
 * Returns: the trace that @packet belongs to, or %NULL if @packet
 * currently belongs to no trace.
 */
LND_Trace      *libnd_packet_get_trace(const LND_Packet *packet);

/**
 * libnd_packet_dump - writes the packet to a pcap dumper.
 * @packet: packet to write.
 * @dumper: dumper to write to.
 *
 * The function writes @packet to @dumper. Always use this function
 * when you want to save some packet's content and don't use the data
 * and ph fields yourself, as the resulting saved packet would contain
 * bogus data.
 */
void            libnd_packet_dump(const LND_Packet *packet, pcap_dumper_t *dumper);


/**
 * libnd_packet_set_data - sets packet content.
 * @packet: packet to set content of.
 * @hdr: pcap header structure.
 * @data: packet data copied into packet.
 *
 * The function copies the amount of bytes specified by @hdr into @packet,
 * starting at @data. Less is copied if @packet was created for fewer bytes.
 */
void            libnd_packet_set_data(LND_Packet *packet,
				      const struct pcap_pkthdr *hdr,
				      const guchar *data);


/**
 * libnd_packet_duplicate - deep-copy packet
 * @packet: packet to copy.
 *
 * Returns a deep copy of a packet, but with the
 * list pointers set to NULL (i.e. the copy does not
 * belong to any packet list). It also doesn't belong
 * to any trace part (i.e., packet->part == NULL).
 *
 * Returns: packet copy or NULL when error occured.
 */
LND_Packet     *libnd_packet_duplicate(LND_Packet *packet);


/**
 * libnd_packet_set_filtered - marks a packet as filtered or unfiltered.
 * @packet: packet to (un)filter.
 * @filtered: filtering status.
 *
 * The function marks the given packet as filtered (if @filtered
 * is %TRUE) or unfiltered (if @filtered is %FALSE).
 */
void            libnd_packet_set_filtered(LND_Packet *packet, gboolean filtered);

/**
 * libnd_packet_is_filtered - whether or not a packet is currently filtered.
 * @packet: packet to query.
 *
 * Returns: %TRUE when the packet is currently filtered, %FALSE
 * otherwise.
 */
gboolean        libnd_packet_is_filtered(LND_Packet *packet);


/**
 * libnd_packet_init - initializes packet internally.
 * @packet: packet to initialize.
 *
 * The function initializes a packet, its data offset pointers,
 * and the protocol types for the protcol stack. It cleans
 * up before adjusting internal settings, so you can call this
 * repeatedly without any cleanups etc.
 */
void            libnd_packet_init(LND_Packet *packet);


/**
 * libnd_packet_cleanup - releases all data contained in a packet.
 * @packet: packet to clean up.
 *
 * The function is equivalent to libnd_packet_free() but does
 * not release the packet structure pointed to by @packet itself.
 * In that sense it's the opposite of libnd_packet_init(). Note
 * that libnd_packet_init() does call libnd_packet_cleanup() on
 * a given packet anyway, so it is okay to just call libnd_packet_init()
 * on the same packet repeatedly.
 */
void            libnd_packet_cleanup(LND_Packet *packet);


/**
 * libnd_packet_init_from_pcap - initializes packet internally.
 * @packet: packet to initialize.
 * @pcap: pcap handle.
 *
 * The function initializes a packet using information provided
 * in the given pcap handle. Use this if you want to initialize
 * a standalone packet that is not part of an LND_Trace. Other
 * than that, it's just like libnd_packet_init().
 */
void            libnd_packet_init_from_pcap(LND_Packet *packet, pcap_t *pcap);


/**
 * libnd_packet_is_complete - was packet completely captured?
 * @packet: packet to check.
 *
 * The predicate returns %TRUE if the packet was captured
 * completely (i.e. pcap's caplen == len) and %FALSE otherwise.
 *
 * Returns: completeness result.
 */
gboolean        libnd_packet_is_complete(const LND_Packet *packet);


/**
 * libnd_packet_update - updates partial packet state
 * @packet: packet to update.
 * @proto: protocol where to start update.
 * @nesting: at what nesting of @proto to update.
 *
 * This is libnd_packet_init()'s little brother. It doesn't initialize
 * the whole packet, but only the part starting at @proto with
 * nesting level @nesting.
 */
void            libnd_packet_update(LND_Packet *packet, LND_Protocol *proto, guint nesting);

/**
 * libnd_packet_get_num_protos - returns number of protocols in packet.
 * @packet: packet to query.
 *
 * Returns: the number of protocols found in @packet.
 */
guint           libnd_packet_get_num_protos(LND_Packet *packet);

/**
 * libnd_packet_get_data - data accessing function
 * @packet: packet to query.
 * @proto: protocol to look up.
 * @nesting: nesting level of @proto to use.
 *
 * The function returns a pointer to the packet data containing
 * the start of @proto's data at the given nesting level. If the
 * packet doesn't contain any such data, %NULL is returned.
 *
 * Returns: data pointer.
 */
guchar         *libnd_packet_get_data(const LND_Packet *packet,
				      const LND_Protocol *proto,
				      guint nesting);

/**
 * libnd_packet_get_data_end - data accessing function
 * @packet: packet to query.
 * @proto: protocol to look up.
 * @nesting: nesting level of @proto to use.
 *
 * The function returns a pointer to the first byte after the
 * data containing the start of @proto's data at the given nesting
 * level. If the packet doesn't contain any such data, %NULL
 * is returned.
 *
 * Returns: data pointer.
 */
guchar         *libnd_packet_get_data_end(const LND_Packet *packet,
					  const LND_Protocol *proto,
					  guint nesting);


/**
 * libnd_packet_add_proto_data - adds internal protocol information.
 * @packet: packet to update.
 * @proto: protocol type of data chunk
 * @data: start of protocol data.
 * @data_end: first byte of data after protocol data.
 *
 * Each packet internally maintains a list of data offsets which
 * store info about the protocol type, nesting etc. This function
 * appends a new chunk information to that list. It is called from
 * libnd_packet_init() and libnd_packet_update(), chances are you want
 * these functions instead.
 */
void            libnd_packet_add_proto_data(LND_Packet *packet,
					    LND_Protocol *proto,
					    guchar *data,
					    guchar *data_end);

/**
 * libnd_packet_get_proto_data - protocol information accessor.
 * @packet: packet to query.
 * @proto: protocol to look up.
 * @nesting: nesting level for @proto.
 *
 * This function is like libnd_packet_get_data, but does not only
 * return the data pointer but the full #LND_ProtoData structure,
 * which yields nesting info, data start end end pointers etc.
 *
 * Returns: protocol data or %NULL if n/a.
 */
LND_ProtoData  *libnd_packet_get_proto_data(const LND_Packet *packet,
					    const LND_Protocol *proto,
					    guint nesting);

/**
 * libnd_packet_get_end -  returns pointer to end of packet data.
 * @packet: packet to query.
 *
 * The function returns a ponter to the byte following the last
 * byte of the captured data.
 *
 * Returns: data pointer.
 */
guchar         *libnd_packet_get_end(const LND_Packet *packet);


/**
 * libnd_packet_has_complete_header - whether a given protocol's header is present.
 * @packet: packet to query.
 * @proto: protocol to check for.
 * @nesting: nesting level for @proto.
 *
 * The predicate returns %TRUE when a packet contains a complete header
 * of the requested protocol, %FALSE otherwise. The implementation of
 * the check itself is up to the implementation of @proto's plug-in.
 * If you only need to know whether a protocol is present in the packet
 * at all, use libnd_packet_has_proto(), which is faster.
 *
 * Returns: check result.
 */
gboolean        libnd_packet_has_complete_header(const LND_Packet *packet,
						 const LND_Protocol *proto,
						 guint nesting);

/**
 * libnd_packet_has_proto - whether a given protocol is present in packet.
 * @packet: packet to query.
 * @proto: protocol to check for.
 *
 * The predicate returns %TRUE when a packet contains data of the
 * given protocol. It may contain multiple instances of a protocol,
 * but the function only checks if a protocol is present at all.
 * If you need to find out whether a protocol is present at a given
 * nesting level (e.g. whether a packet contains IP in IP), use
 * libnd_packet_has_complete_header() or libnd_packet_has_proto_nested().
 *
 * Returns: check result.
 */
gboolean        libnd_packet_has_proto(const LND_Packet *packet,
				       const LND_Protocol *proto);


/**
 * libnd_packet_has_proto_nested - whether a protocol is present in packet at given nesting level.
 * @packet: packet to query.
 * @proto: protocol to check for.
 * @nesting: nesting level of @proto.
 *
 * The predicate returns %TRUE when a packet contains data of the
 * given protocol at the requested nesting level.
 * If you only need to find out whether a protocol is present at all,
 * use libnd_packet_has_proto() instead, which is faster.
 *
 * Returns: check result.
 */
gboolean        libnd_packet_has_proto_nested(const LND_Packet *packet,
					      const LND_Protocol *proto,
					      guint nesting);


/**
 * libnd_packet_get_last_nonraw - returns the last protocol info for non-raw data.
 * @packet: packet to query.
 *
 * The function tries to find the innermost protocol in the packet coming
 * before any uninterpreted raw data. 
 *
 * Returns: the protocol data before raw data, if there is both
 * raw data interpreted data coming before it, or %NULL if either is
 * missing.
 */
LND_ProtoData  *libnd_packet_get_last_nonraw(const LND_Packet *packet);


/**
 * libnd_packet_update_proo_state - updates the state of a packet.
 * @packet: packet to update.
 * @index: index of packet.
 *
 * The function iterates over the protocols contained in its data,
 * and calls the update_state() callback of the stateful protocols.
 * It's faster when the index of the packet is already known, so
 * pass it if you happen to know it. Otherwise, pass -1 for @index.
 */
void            libnd_packet_update_proto_state(LND_Packet *packet,
						int index);

/**
 * libnd_packet_foreach_proto - protocol iterator.
 * @packet: packet whose protocols to iterate.
 * @cb: callback to call.
 * @user_data: arbitrary data passed to @cb.
 *
 * The function iterates over the protocols in the packet, from
 * outer- to the innermost, and calls @cb with that the corresponding
 * packet, protocol data and @user_data.
 */
void            libnd_packet_foreach_proto(LND_Packet *packet,
					   LND_PacketFunc cb,
					   void *user_data);

/**
 * libnd_packet_foreach_proto_backward - backward protocol iterator.
 * @packet: packet whose protocols to iterate.
 * @cb: callback to call.
 * @user_data: arbitrary data passed to @cb.
 *
 * The function iterates over the protocols in the packet, from
 * inner- to the outermost, and calls @cb with that the corresponding
 * packet, protocol data and @user_data.
 */
void            libnd_packet_foreach_proto_backward(LND_Packet *packet,
						    LND_PacketFunc cb,
						    void *user_data);

/**
 * libnd_packet_modified - marks a packet as modified.
 * @packet: packet to mark.
 *
 * The function marks a packet as modified and updates the GUI
 * accordingly.
 */
void            libnd_packet_modified(LND_Packet *packet);


/**
 * libnd_packet_get_index -- returns index of packet.
 * @packet: packet to query.
 * 
 * The function returns the index of the packet in it's trace,
 * the first packet has index 0.
 *
 * Returns: index, or -1 if error occurred.
 */
int             libnd_packet_get_index(const LND_Packet *packet);


/**
 * libnd_packet_get_proto_nesting - returns nesting level of protocol at given data offset.
 * @packet: packet to query.
 * @proto: protocol whose nesting level to find.
 * @data: data pointer.
 *
 * The function returns the nesting level of @proto at the given
 * data offset. The first occurrence of a protocol has nesting level 0.
 * So, if you call this for IP and the data pointer points to somewhere
 * in or after the IP header in an ICMP error message, the nesting level
 * will be 1.
 *
 * Returns: nesting level, or -1 when an error occurred.
 */
int             libnd_packet_get_proto_nesting(const LND_Packet *packet,
					       const LND_Protocol *proto,
					       guchar *data);


/**
 * libnd_packet_get_proto_index - index of a protocol in the packet.
 * @packet: packet to query.
 *
 * Returns: the index of the given protocol instance in the packet,
 * starting from zero. If the instance cannot be located in the packet,
 * a negative value is returned. For example, if a packet contains
 * Ethernet, IP, and TCP, then those protocols would be at indices
 * 0, 1, and 2, respectively. This is an O(n) operation.
 */
int             libnd_packet_get_proto_index(const LND_Packet *packet,
					     const LND_ProtoInst *pi);


/**
 * libnd_packet_get_nth_proto - returns nth protocol in packet.
 * @packet: packet to query.
 *
 * Returns the LND_ProtoData structure corresponding to the nth
 * protocol in the packet, or %NULL if no nth protocol exists.
 * Indexing starts at 0. This is an O(n) operation.
 */
LND_ProtoData  *libnd_packet_get_nth_proto(LND_Packet *packet, int n);


/**
 * libnd_packet_fix - calls fix_packet() callback for each protocol.
 * @packet: packet to fix.
 *
 * The function calls the fix_packet callback of every protocol that
 * is contained in @packet, iterating from the inner- to the outermost
 * protocol.
 *
 * Returns: %TRUE if @packet got modified, %FALSE otherwise.
 */
gboolean        libnd_packet_fix(LND_Packet *packet);

/**
 * libnd_packet_get_last_fixable_proto - offsert of last fixable protocol in packet.
 * @packet: packet to query.
 *
 * Returns: the index (starting from 0) of the last protocol in the
 * given packet at which libnd_packet_fix() will do anything.
 */
int             libnd_packet_get_last_fixable_proto(const LND_Packet *packet);

/**
 * libnd_packet_adjust_len - changes a packet's wire size.
 * @packet: packet to adjust.
 * @delta: amount of size change, positive or negative.
 *
 * The function adjusts the packet's wire size and gives the protocols
 * in the packet a chance to accomodate the size change. Note: the size
 * change refers to the size of the packet as seen on the wire, not the
 * snaplen used by pcap: a size change of -10 on a packet of 1500 bytes
 * of which you've captured only 68 will leave 68 unmodified, but change
 * the packet's contents as captures as if the original size had been
 * 1490 bytes.
 * 
 * If you want to change the caplen, use libnd_packet_adjust_caplen(). 
 *
 * Returns: %TRUE if the change succeded, %FALSE if not.
 */
gboolean         libnd_packet_adjust_len(LND_Packet *packet, int delta);

/**
 * libnd_packet_adjust_caplen - changes a packet's wire size.
 * @packet: packet to adjust.
 * @delta: amount of size change, positive or negative.
 *
 * The function adjusts the size of the part of the packet that has been
 * captured. With positive delta's, the new part of the packet is
 * initialized to zero. If the caplen would grow beyond the wire length,
 * the latter is updated accordingly.
 * 
 * If you want to change (only) the wire length, use
 * libnd_packet_adjust_len().
 *
 * Returns: %TRUE if the change succeded, %FALSE if not.
 */
gboolean            libnd_packet_adjust_caplen(LND_Packet *packet, int delta);

/**
 * libnd_packet_observer_new - creates new packet observer.
 *
 * The function allocates a new, empty packet observer. You should
 * then fill in callbacks for the events you're interested in,
 * and register the thing using libnd_packet_add_observer().
 *
 * Returns: new packet observer with dummy callbacks.
 */
LND_PacketObserver *libnd_packet_observer_new(void);

/**
 * libnd_packet_observer_free - deleted packet observer.
 * @ob: observer to delete.
 * 
 * The function releases all memory associated with @ob.
 */
void                libnd_packet_observer_free(LND_PacketObserver *ob);

/**
 * libnd_packet_add_observer - registers a new packet observer.
 * @ob: new observer to register.
 *
 * The function registers the new observer for notifications about
 * future changes to packets.
 */
void                libnd_packet_add_observer(LND_PacketObserver *ob);

/**
 * libnd_packet_del_observer - deletes a packet observer.
 * @ob: observer to drop.
 *
 * The function stops packet operations from being reported to
 * @ob. It does not release @ob's memory, use libnd_packet_observer_free()
 * for that. 
 */
void                libnd_packet_del_observer(LND_PacketObserver *ob);

/**
 * libnd_packet_tell_observer - push operations to registered observers.
 * @packet: packet to report operation on.
 * @op: operation type.
 * @data: operation-dependant data passed to observer.
 *
 * The function reports the given operation on the given packet to all
 * registered observers.
 */
void                libnd_packet_tell_observers(LND_Packet *packet,
						LND_PacketObserverOp op,
						void *data);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif
