/*
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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <libnd.h>
static GList *observers; /* GList<LND_PacketIteratorObserver> */
static void
pit_observers_init(int num_total)
{
GList *l;
LND_PacketIteratorObserver *ob;
for (l = observers; l; l = g_list_next(l))
{
ob = (LND_PacketIteratorObserver *) l->data;
if (ob->pit_init)
ob->pit_init(num_total);
}
}
static void
pit_observers_progress(int num_progress)
{
GList *l;
LND_PacketIteratorObserver *ob;
for (l = observers; l; l = g_list_next(l))
{
ob = (LND_PacketIteratorObserver *) l->data;
if (ob->pit_progress)
ob->pit_progress(num_progress);
}
}
static void
pit_observers_clear(void)
{
GList *l;
LND_PacketIteratorObserver *ob;
for (l = observers; l; l = g_list_next(l))
{
ob = (LND_PacketIteratorObserver *) l->data;
if (ob->pit_clear)
ob->pit_clear();
}
}
gboolean
libnd_pit_init(LND_PacketIterator *pit,
LND_Trace *trace)
{
if (!pit || !trace)
return FALSE;
return libnd_pit_init_mode(pit, trace, trace->iterator_mode);
}
gboolean
libnd_pit_init_mode(LND_PacketIterator *pit,
LND_Trace *trace,
LND_PacketIteratorMode mode)
{
D_ENTER;
if (!pit || !trace)
D_RETURN_(FALSE);
memset(pit, 0, sizeof(LND_PacketIterator));
pit->trace = trace;
pit->mode = mode;
pit->skip_filtered = TRUE;
libnd_filter_list_init(trace->filters, trace);
switch (pit->mode)
{
case LND_PACKET_IT_AREA_RW:
/* There's no difference between read and read/write mode
* in initialization. Only in iteration, read/write mode
* requires writing out the iterated packets.
*
* We break for nobody. Straight over your helmet.
*/
case LND_PACKET_IT_AREA_R:
{
LND_TraceLoc start_loc;
LND_TracePart *tp = trace->tpm->current;
/* If the current trace part got modified, we need to first
* add it to the pile so that the offset calculation sees it.
*/
if (tp->dirty)
libnd_tpm_clear_current_part(trace->tpm, FALSE);
pit->offset_orig = libnd_tpm_map_loc_to_offset(trace->tpm, &tp->start);
/* Find the end condition of the iteration, and go to
* the beginning of the iteration area that is currently
* configured for the trace:
*/
if (! libnd_tpm_find_locs(trace->tpm, &trace->area,
&start_loc, &pit->stop_loc))
{
D(("Couldn't get iteration boundaries, aborting\n"));
pit->current = NULL;
D_RETURN_(FALSE);
}
/* We jump to the beginning of the iteration area only for the
* purpose of the iteration. Do not fire a JUMPED event, as we
* will jump back to the original location after the iteration
* anyway.
*
* Note that this jump will create a new current trace part
* in trace->tpm->current, so there is a current part from now
* on no matter if we removed it above in libnd_tpm_clear_current_part().
*/
libnd_trace_block_trace_observer_op(trace, LND_TRACE_JUMPED);
if (! libnd_tpm_goto_loc(trace->tpm, &start_loc))
{
/* FIXME error handling here */
}
libnd_trace_unblock_trace_observer_op(trace, LND_TRACE_JUMPED);
D(("Stop condition: part %p, offset %lu\n",
pit->stop_loc.tp, (long unsigned) pit->stop_loc.offset));
if (! libnd_tpm_read_packet(trace->tpm, &pit->packet))
{
D(("Couldn't read first packet, aborting\n"));
D_RETURN_(FALSE);
}
pit->current = &pit->packet;
pit->offset = pcapnav_get_offset(trace->tpm->current->pcn);
/* Interpret the packet, and determine filtering status: */
libnd_packet_init(pit->current);
/* We initialize the progress observers to the size of the
* iteration area, in bytes.
*/
{
off_t start_off, end_off;
start_off = libnd_tpm_map_loc_to_offset(trace->tpm, &start_loc);
end_off = libnd_tpm_map_loc_to_offset(trace->tpm, &pit->stop_loc);
pit_observers_init(end_off - start_off);
}
}
break;
case LND_PACKET_IT_PART_R:
case LND_PACKET_IT_PART_RW:
/* We want to iterate over everything in the currently
* loaded trace part. Initialize iterator to packet list
* of current part, and progress bar to number of packets
* in current trace part.
*/
pit->current = libnd_tpm_get_packets(trace->tpm);
/* We initialize to the number of in-mem packets. */
pit_observers_init(trace->tpm->current->num_packets);
break;
case LND_PACKET_IT_SEL_R:
case LND_PACKET_IT_SEL_RW:
default:
/* We want to iterate over selection in current trace
* part. Initialize iterator to first selected packet
* in current trace part, and progress bar to number
* of selected packets in the trace part.
*/
pit->current = libnd_tpm_get_sel(trace->tpm);
/* We initialize to the number of selected packets. */
pit_observers_init(trace->tpm->current->sel.size);
}
/* Filter the initial packet in any case. */
libnd_filter_list_apply(trace->filters, pit->current, trace->filter_mode);
D_RETURN_(TRUE);
}
void
libnd_pit_cleanup(LND_PacketIterator *pit)
{
LND_TraceLoc loc;
D_ENTER;
if (!pit)
D_RETURN;
if (pit->trace)
libnd_filter_list_cleanup(pit->trace->filters);
if (pit->mode == LND_PACKET_IT_AREA_RW)
libnd_tp_sync(pit->trace->tpm->current);
/* Return to old location if necessary */
if (pit->mode == LND_PACKET_IT_AREA_R ||
pit->mode == LND_PACKET_IT_AREA_RW)
{
libnd_tpm_map_offset_to_loc(pit->trace->tpm, pit->offset_orig, &loc);
libnd_tpm_goto_loc(pit->trace->tpm, &loc);
}
/* Tell observers that we're done. */
pit_observers_clear();
D_RETURN;
}
LND_Packet *
libnd_pit_get(LND_PacketIterator *pit)
{
if (!pit)
return NULL;
/* If it's filtered, proceed to the first unfiltered one. This is particularly
* for the case when libnd_pit_init() initializes to a filtered packet and we
* don't want to iterate at all in libnd_pit_init() (for example, to adjust the
* output file name before the iteration starts). In all other cases, the current
* packet won't be filtered and this will just return the current packet.
*/
if (libnd_packet_is_filtered(pit->current))
{
D(("Skipping current packet\n"));
libnd_pit_next(pit);
}
return pit->current;
}
LND_Packet *
libnd_pit_next(LND_PacketIterator *pit)
{
LND_Trace *trace;
D_ENTER;
if (!pit || !pit->current)
D_RETURN_(NULL);
do {
pit->packet_count++;
switch (pit->mode)
{
case LND_PACKET_IT_AREA_RW:
/* First write out old packet ... unless not wanted by user,
* or packet is filtered:
*/
if (! (pit->drop_current ||
(pit->skip_filtered && libnd_packet_is_filtered(pit->current))))
{
libnd_tp_write_packet(pit->trace->tpm->current, pit->current);
D(("Iterator wrote packet\n"));
}
pit->drop_current = FALSE;
/* NO BREAK: proceed as in read-only mode. */
case LND_PACKET_IT_AREA_R:
if (pit->stopped)
{
pit->current = NULL;
break;
}
D(("Test: %p %lu -- %p %lu\n",
pit->trace->tpm->current->end.tp, (long unsigned) pit->offset,
pit->stop_loc.tp, (long unsigned) pit->stop_loc.offset));
if (pit->trace->tpm->current->end.tp == pit->stop_loc.tp &&
pit->offset >= pit->stop_loc.offset)
{
/* We've read the last packet for this iterator. We still
* need to deliver it to make sure we call libnd_pit_next()
* one more time. However, we mark the iteration to stop
* right now.
*/
D(("Stop condition hit -- stopping.\n"));
pit->stopped = TRUE;
}
/* Obtain the next packet using the trace part layering. */
if (! libnd_tpm_read_packet(pit->trace->tpm, &pit->packet))
{
pit->current = NULL;
break;
}
pit->offset = pcapnav_get_offset(pit->trace->tpm->current->pcn);
/* Interpret the packet, and determine filtering status: */
libnd_packet_init(pit->current);
/* Tell observers to proceed by the size of this packet plus the
* pcap packet header.
*/
pit_observers_progress(pcapnav_get_pkthdr_size(pit->packet.part->pcn) +
pit->packet.ph.caplen);
break;
case LND_PACKET_IT_PART_RW:
case LND_PACKET_IT_PART_R:
/* Iterate to next packet in currently loaded part, and
* increment packet counter.
*/
pit->current = pit->current->next;
if (pit->current)
{
if ( (trace = libnd_packet_get_trace(pit->current)))
libnd_filter_list_apply(trace->filters, pit->current, trace->filter_mode);
}
/* Tell observers to proceed by one packet */
pit_observers_progress(1);
break;
case LND_PACKET_IT_SEL_RW:
case LND_PACKET_IT_SEL_R:
default:
/* Iterate to next selected packet in currently loaded part ...
*/
pit->current = pit->current->sel_next;
if (pit->current)
{
if ( (trace = libnd_packet_get_trace(pit->current)))
libnd_filter_list_apply(trace->filters, pit->current, trace->filter_mode);
}
/* Tell observers to proceed by one packet */
pit_observers_progress(1);
}
/* If the iterator must skip filtered packets, make sure we
* actually skip them!
*/
} while (pit->skip_filtered && libnd_packet_is_filtered(pit->current));
/* If there is no current packet now, we have iterated over
* all packets and automatically close the dumper. The user
* never sees any of this.
*/
if (!pit->current)
libnd_pit_cleanup(pit);
D_RETURN_(pit->current);
}
guint64
libnd_pit_get_count(LND_PacketIterator *pit)
{
if (!pit)
return 0;
return pit->packet_count;
}
void
libnd_pit_skip_filtered(LND_PacketIterator *pit, gboolean skip_filtered)
{
if (!pit)
return;
pit->skip_filtered = skip_filtered;
}
void
libnd_pit_drop_current(LND_PacketIterator *pit)
{
if (!pit)
return;
pit->drop_current = TRUE;
}
LND_PacketIteratorObserver *
libnd_pit_observer_new(void)
{
return g_new0(LND_PacketIteratorObserver, 1);
}
void
libnd_pit_observer_free(LND_PacketIteratorObserver *ob)
{
g_free(ob);
}
void
libnd_pit_add_observer(LND_PacketIteratorObserver *ob)
{
if (!ob)
return;
observers = g_list_prepend(observers, ob);
}
void
libnd_pit_del_observer(LND_PacketIteratorObserver *ob)
{
if (!ob)
return;
observers = g_list_remove(observers, ob);
}
syntax highlighted by Code2HTML, v. 0.9.1