Ethereal-dev: [ethereal-dev] Dissector Exceptions

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Gilbert Ramirez <gram@xxxxxxxxxx>
Date: Fri, 28 Apr 2000 08:48:32 -0500
I did some work last night and this morning on exceptions for
Ethereal (targetted only at the dissectors right now). I also
started outlining the API for "tvbuff"'s --- "Testy, Virtual Buffers".

I'll post now just to get some early suggestions.

I'm using excp.[ch] from kazlib (search Freshmeat). I can compile
them w/o modification. The attached file, "exceptions.h", defines
the macros for use within Ethereal.

"tvbuff.h" shows the API for tvbuff.

Here is the struct for tvbuff_t so far:

/* (helper-struct) */

typedef struct {
        /* The backing tvbuff_t */
        tvbuff_t        *tvb;

        /* The offset/length of 'tvb' to which I'm privy */
        guint           offset;
        guint           length;

        /* Used for quick testing to see if this
         * is the tvb_backing that a COMPOSITE is
         * interested in. */
        guint           parent_ending_offset;
} tvb_backing_t;


struct tvbuff {
        /* Record-keeping */
        tvbuff_type             type;
        gboolean                initialized;
        guint                   usage_count;

        /* Our guint8 data */
        union {
                guint8          *real;
                tvb_backing_t   subset;
                GPtrArray       *composite;
        } data;

        /* Length of virtual buffer. */
        guint                   length;

        /* COMPOSITE's might need to create a flattened
         * version of their data in tvb_get_ptr() */
        guint8                  *flattened_data;

        /* Func to call when actually freed */
        tvbuff_free_cb_t        free_cb;
};

--gilbert
#ifndef __EXCEPTIONS_H__
#define __EXCEPTIONS_H__

#ifndef XCEPT_H
#include "except.h"
#endif

/* Ethereal has only one exception group, to make these macros simple */
#define XCEPT_GROUP_ETHEREAL 1

/* Ethereal's exceptions */
#define BoundsError		1	/* Index is out of range */
#define UninitializedError	2	/* Data/object is uninitialized */

/* Usage:
 *
 * TRY {
 * 	code;
 * }
 *
 * CATCH(exception) {
 * 	code;
 * }
 *
 * CATCH_ALL {
 * 	code;
 * }
 *
 * FINALLY {
 * 	code;
 * }
 *
 * ENDTRY;
 *
 * This is really something like:
 *
 * {
 * 	x = setjmp()
 * 	if (x == 0) {
 * 		<TRY code>
 * 	}
 * 	else if (x == 1) {
 * 		<CATCH(1) code>
 * 	}
 * 	else if (x == 2) {
 * 		<CATCH(2) code>
 * 	}
 * 	else {
 * 		<CATCH_ALL code> {
 * 	}
 * 	<FINALLY code>
 * }<ENDTRY tag>
 *
 * You obviously don't want to 'goto' or 'return' inside a
 * CATCH/CATCH_ALL block if you intend to do something in the
 * FINALLY block.
 *
 * All CATCH's must precede a CATCH_ALL.
 * FINALLY must occur after any CATCH or CATCH_ALL.
 * ENDTRY marks the end of the TRY code.
 * TRY and ENDTRY are the mandatory parts of a TRY block.
 * CATCH, CATCH_ALL, and FINALLY are all optional (although
 * you'll probably use at least one, otherwise why "TRY"?)
 *
 * GET_MESSAGE	returns string ptr to exception message
 *
 * To throw/raise an exception.
 *
 * THROW(exception)
 * RETHROW				rethrow the caught exception
 *
 * A cleanup callback is a function called in case an exception occurs
 * and is not caught. It should be used to free any dynamically-allocated data.
 * A pop or call_and_pop should occur at the same statement-nesting level
 * as the push.
 *
 * CLEANUP_CB_PUSH(func, data)
 * CLEANUP_CB_POP
 * CLEANUP_CB_CALL_AND_POP
 */



#define TRY \
{\
	except_t *exc; \
	int caught = 0; \
	static const except_id_t catch_spec[] = { \
		{ XCEPT_GROUP_ETHEREAL, XCEPT_CODE_ANY } }; \
	except_try_push(catch_spec, 1, &exc); \
	if (exc == 0) { \
		/* user's code goes here */

#define ENDTRY \
	} \
	if (exc != 0 && !caught) { \
		g_debug("Exception %lu not caught.", exc->except_id.except_code); \
		g_assert(caught); \
	} \
	except_try_pop();\
}

#define CATCH(x) \
	} \
	else if (exc->except_id.except_code == (x)) { \
		caught = 1;
		/* user's code goes here */


#define CATCH_ALL \
	} \
	else { \
		caught = 1;
		/* user's code goes here */

#define FINALLY \
	} \
	{ \
		/* user's code goes here */

#define THROW(x) \
	except_throw(XCEPT_GROUP_ETHEREAL, (x), "XCEPT_GROUP_ETHEREAL")

#define THROW_MESSAGE(x, y) \
	except_throw(XCEPT_GROUP_ETHEREAL, (x), (y))

#define GET_MESSAGE			except_message(exc)

#define RETHROW				except_rethrow(exc)

#define CLEANUP_CB_PUSH(x,y)		except_cleanup_push((x),(y)
#define CLEANUP_CB_POP			except_cleanup_push(0)
#define CLEANUP_CB_CALL_AND_POP		except_cleanup_push(1)

#endif /* __EXCEPTIONS_H__ */
/* tvbuff.h
 *
 * Testy, Virtual(-izable) Buffer of guint8*'s
 * 
 * "Testy" -- the buffer gets mad when an attempt is made to access data
 * 		beyond the bounds of the buffer. An exception is thrown.
 *
 * "Virtual" -- the buffer can have its own data, can use a subset of
 * 		the data of a backing tvbuff, or can be a composite of
 * 		other tvbuffs.
 *
 * $Id$
 *
 * Copyright (c) 2000 by Gilbert Ramirez <gram@xxxxxxxxxx>
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxx>
 * Copyright 1998 Gerald Combs
 *
 * 
 * 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 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.
 */

#ifndef __TVBUFF_H__
#define __TVBUFF_H__

#ifndef __GLIB_H__
#include <glib.h>
#endif

typedef struct tvbuff tvbuff_t;

typedef void (*tvbuff_free_cb_t)(void*);

/* The different types of tvbuff's */
typedef enum {
	TVBUFF_REAL_DATA,
	TVBUFF_SUBSET,
	TVBUFF_COMPOSITE
} tvbuff_type;

/* TVBUFF_REAL_DATA contains a guint8* that points to real data.
 * The data is allocated and contiguous.
 *
 * TVBUFF_SUBSET has a backing tvbuff. The TVBUFF_SUBSET is a "window"
 * through which the program sees only a portion of the backing tvbuff.
 *
 * TVBUFF_COMPOSITE combines multiple tvbuffs sequentually to produce
 * a larger byte array.
 *
 * tvbuff's of any type can be used as the backing-tvbuff of a
 * TVBUFF_SUBSET or as the member of a TVBUFF_COMPOSITE.
 * TVBUFF_COMPOSITEs can have member-tvbuffs of different types.
 */


/* "class" initialization. Called once during execution of program
 * so that tvbuff.c can initialize its data. */
void tvbuff_init(void);

/* "class" cleanup. Called once during execution of program
 * so that tvbuff.c can clean up its data. */
void tvbuff_cleanup(void);


/* Returns a pointer to a newly initialized tvbuff. Note that
 * tvbuff's of types TVBUFF_SUBSET and TVBUFF_COMPOSITE
 * require further initialization via the appropriate functions */
tvbuff_t* tvb_new(tvbuff_type);

/* Marks a tvbuff for freeing. The guint8* data is *never* freed by
 * the tvbuff routines. The tvbuff is actually freed once its usage
 * count drops to 0. Usage counts exist for any time the tvbuff is
 * used as a member of another tvbuff, i.e., as the backing buffer for
 * a TVBUFF_SUBSET or as a member of a TVBUFF_COMPOSITE.
 *
 * The caller can artificially increment/decrement the usage count
 * with tvbuff_increment_usage_count()/tvbuff_decrement_usage_count().
 */
void tvb_free(tvbuff_t*);

/* Both return the new usage count, after the increment or decrement */
guint tvbuff_increment_usage_count(tvbuff_t*, guint count);
/* If a decrement causes the usage count to drop to 0, a the tvbuff
 * is immediately freed. Be sure you know exactly what you're doing
 * if you decide to use this function, as another tvbuff could
 * still have a pointer to the just-freed tvbuff, causing corrupted data
 * or a segfault in the future */
guint tvbuff_decrement_usage_count(tvbuff_t*, guint count);

/* Set a callback function to call when a tvbuff is actually freed
 * (once the usage count drops to 0). One argument is passed to
 * that callback --- the guint* that points to the real data.
 * Obviously, this only applies to a TVBUFF_REAL_DATA tvbuff. */
void tvb_set_free_cb(tvbuff_t*, tvbuff_free_cb_t);


/* Sets parameters for TVBUFF_REAL_DATA */
void tvb_set_real_data(tvbuff_t*, guint8* data, guint length);

/* Combination of tvb_new() and tvb_set_real_data() */
tvbuff_t* tvb_new_real_data(guint8* data, guint length);


/* Define the subset of the backing buffer to use.
 *
 * 'backing_start_offset' can be negative, to indicate bytes from
 * the end of the backing buffer.
 *
 * 'length' can be 0, although the usefulness of the buffer would
 * be rather limited.
 *
 * 'length' of -1 means "to the end of the backing buffer"
 *
 * Will throw BoundsError if 'backing_start_offset'/'length'
 * is beyond the bounds of the backing tvbuff. */
void tvb_set_subset(tvbuff_t* tvb, tvbuff_t* backing,
		gint backing_start_offset, gint length);

/* Combination of tvb_new() and tvb_set_subset() */
tvbuff_t* tvb_new_subset(tvbuff_t* backing,
		gint backing_start_offset, gint length);


/* Append to the list of tvbuffs that make up this composite tvbuff */
void tvb_composite_append(tvbuff_t* tvb, tvbuff_t* member,
		gint member_start_offset, gint length);

/* Prepend to the list of tvbuffs that make up this composite tvbuff */
void tvb_composite_prepend(tvbuff_t* tvb, tvbuff_t* member,
		gint member_start_offset, gint length);

/* Mark a composite tvbuff as initialized. No further appends or prepends
 * occur, data access can finally happen after this finalization. */
void tvb_composite_finalize(tvbuff_t* tvb);


/* Get total length of buffer */
guint tvb_length(tvbuff_t*);

/* Computes bytes to end of buffer, from offset (which can be negative,
 * to indicate bytes from end of buffer) */
guint tvb_length_remaining(tvbuff_t*, gint offset);

/* Checks (w/o throwing exception) that the bytes referred to by 'offset'/'length'
 * actualy exist in the buffer */
gboolean tvb_bytes_exist(tvbuff_t*, gint offset, gint length);

/* Checks (w/o throwing exception) that offset exists in buffer */
gboolean tvb_offset_exists(tvbuff_t*, gint offset);


/************** START OF ACCESSORS ****************/
/* All accessors will throw UnitializedError or BoundsError if appropriate */

guint8  tvb_get_guint8(tvbuff_t*, gint offset);
guint16 tvb_get_ntohs(tvbuff_t*, gint offset);
guint32 tvb_get_ntohl(tvbuff_t*, gint offset);
guint16 tvb_get_letohs(tvbuff_t*, gint offset);
guint32 tvb_get_letohl(tvbuff_t*, gint offset);

guint8* tvb_memcpy(tvbuff_t*, guint8* target, gint offset, gint length);

/* It is the user's responsibility to g_free() the memory allocated by
 * tvb_memdup() */
guint8* tvb_memdup(tvbuff_t*, gint offset, gint length);

/* WARNING! This function is possibly expensive, temporarily allocating
 * another copy of the packet data */
/* Will return a ptr into our buffer if the data asked for via 'offset'/'length'
 * is contiguous (which might not be the case for TVBUFF_COMPOSITE). If the
 * data is not contiguous, a tvb_memdup() is called for the entire buffer
 * and the pointer to the newly-contiguous data is returned. This dynamically-
 * allocated memory will be freed when the tvbuff is freed, after the
 * tvbuff_free_cb_t() is called, if any. */
guint8* tvb_get_ptr(tvbuff_t*, gint offset, gint length);

/* Find length of string by looking for end of string ('\0'), up to
 * 'max_length' characters'. Returns -1 if 'max_length' reached
 * before finding EOS. */
gint tvb_strnlen(tvbuff_t*, gint offset, gint max_length);

/************** END OF ACCESSORS ****************/

/* Sets pd and offset so that tvbuff's can be used with code
 * that only understands pd/offset and not tvbuffs.
 * This is the "compatibility" function */
void tvb_compat(tvbuff_t*, guint8 **pd, int *offset);

/* For testing purposes, send hexdump of tvbuff data contents
 * to stdout. */
void tvb_dump(tvbuff_t*);

#endif /* __TVBUFF_H__ */