/* tvbparse.c * * Copyright 2005, Luis E. Garcia Ontanon * * $Id$ * * Ethereal - Network traffic analyzer * By Gerald Combs * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include typedef enum _tvbparse_wanted_type_t { TVBPARSE_WANTED_NONE, /* currently unused */ /* simple tokens */ TVBPARSE_WANTED_SIMPLE_CHAR, /* just one matching char */ TVBPARSE_WANTED_SIMPLE_CHARS, /* a sequence of matching chars */ TVBPARSE_WANTED_SIMPLE_NOT_CHAR, /* one non matching char */ TVBPARSE_WANTED_SIMPLE_NOT_CHARS, /* a sequence of non matching chars */ TVBPARSE_WANTED_SIMPLE_STRING, /* a string */ TVBPARSE_WANTED_SIMPLE_CASESTRING, /* a caseless string */ TVBPARSE_WANTED_UNTIL, /* all the characters until the first matching token */ /* composed tokens */ TVBPARSE_WANTED_SET_ONEOF, /* one of the given types */ TVBPARSE_WANTED_SET_SEQ, /* an exact sequence of tokens of the given types */ TVBPARSE_WANTED_CARDINALITY, /* one or more tokens of the given type */ TVBPARSE_WANTED_HANDLE /* a handle to another one */ } tvbparse_type_t; struct _tvbparse_t { tvbuff_t* tvb; int offset; int max_len; void* data; const tvbparse_wanted_t* ignore; guint depth; }; struct _tvbparse_wanted_t { int id; tvbparse_type_t type; union { const gchar* str; guint val; struct _tvbparse_wanted_t** handle; } control; int len; guint min; guint max; const void* data; tvbparse_action_t before; tvbparse_action_t after; GPtrArray* elems; }; tvbparse_wanted_t* tvbparse_char(int id, const gchar* chr, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); w->id = id; w->type = TVBPARSE_WANTED_SIMPLE_CHAR; w->control.str = chr; w->len = 1; w->min = 0; w->max = 0; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); return w; } tvbparse_wanted_t* tvbparse_chars(int id, guint min_len, guint max_len, const gchar* chr, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); w->id = id; w->type = TVBPARSE_WANTED_SIMPLE_CHARS; w->control.str = chr; w->len = 0; w->min = min_len ? min_len : 1; w->max = max_len ? max_len : G_MAXINT; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); return w; } tvbparse_wanted_t* tvbparse_not_char(int id, const gchar* chr, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); w->id = id; w->type = TVBPARSE_WANTED_SIMPLE_NOT_CHAR; w->control.str = chr; w->len = 0; w->min = 0; w->max = 0; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); return w; } tvbparse_wanted_t* tvbparse_not_chars(int id, guint min_len, guint max_len, const gchar* chr, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb){ tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); w->id = id; w->type = TVBPARSE_WANTED_SIMPLE_NOT_CHARS; w->control.str = chr; w->len = 0; w->min = min_len ? min_len : 1; w->max = max_len ? max_len : G_MAXINT; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); return w; } tvbparse_wanted_t* tvbparse_string(int id, const gchar* str, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); w->id = id; w->type = TVBPARSE_WANTED_SIMPLE_STRING; w->control.str = str; w->len = strlen(str); w->min = 0; w->max = 0; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); return w; } tvbparse_wanted_t* tvbparse_casestring(int id, const gchar* str, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); w->id = id; w->type = TVBPARSE_WANTED_SIMPLE_CASESTRING; w->control.str = str; w->len = strlen(str); w->min = 0; w->max = 0; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); return w; } tvbparse_wanted_t* tvbparse_set_oneof(int id, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb, ...) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); tvbparse_t* el; va_list ap; w->id = id; w->type = TVBPARSE_WANTED_SET_ONEOF; w->control.val = 0; w->len = 0; w->min = 0; w->max = 0; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); va_start(ap,after_cb); while(( el = va_arg(ap,tvbparse_t*) )) { g_ptr_array_add(w->elems,el); }; va_end(ap); return w; } tvbparse_wanted_t* tvbparse_set_seq(int id, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb, ...) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); tvbparse_wanted_t* el = NULL; va_list ap; w->id = id; w->type = TVBPARSE_WANTED_SET_SEQ; w->control.val = 0; w->len = 0; w->min = 0; w->max = 0; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); va_start(ap,after_cb); while(( el = va_arg(ap,tvbparse_wanted_t*) )) { g_ptr_array_add(w->elems,el); }; va_end(ap); return w; } tvbparse_wanted_t* tvbparse_some(int id, guint from, guint to, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb, const tvbparse_wanted_t* el) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); g_assert(from <= to); w->id = id; w->type = TVBPARSE_WANTED_CARDINALITY; w->control.val = 0; w->len = 0; w->min = from; w->max = to; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); g_ptr_array_add(w->elems,(gpointer)el); return w; } tvbparse_wanted_t* tvbparse_until(int id, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb, const tvbparse_wanted_t* el, int op_mode) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); w->id = id; w->type = TVBPARSE_WANTED_UNTIL; w->control.val = op_mode; w->len = 0; w->min = 0; w->max = 0; w->data = data; w->before = before_cb; w->after = after_cb; w->elems = g_ptr_array_new(); g_ptr_array_add(w->elems,(gpointer)el); return w; } tvbparse_wanted_t* tvbparse_handle(tvbparse_wanted_t** handle) { tvbparse_wanted_t* w = g_malloc(sizeof(tvbparse_wanted_t)); w->id = 0; w->type = TVBPARSE_WANTED_HANDLE; w->control.handle = handle; w->len = 0; w->min = 0; w->max = 0; w->data = NULL; w->before = NULL; w->after = NULL; w->elems = NULL; return w; } tvbparse_wanted_t* tvbparse_quoted(int id, const void* data, tvbparse_action_t before_cb, tvbparse_action_t after_cb, char quote, char esc) { gchar* esc_quot = g_strdup_printf("%c%c",esc,quote); gchar* quot = g_strdup_printf("%c",quote); tvbparse_wanted_t* want_quot = tvbparse_char(-1,quot,NULL,NULL,NULL); return tvbparse_set_oneof(id, data, before_cb, after_cb, tvbparse_set_seq(-1, NULL, NULL, NULL, want_quot, tvbparse_set_seq(-1,NULL,NULL,NULL, tvbparse_set_oneof(-1, NULL, NULL, NULL, tvbparse_string(-1,esc_quot,NULL,NULL,NULL), tvbparse_not_chars(-1,0,0,quot,NULL,NULL,NULL), NULL), NULL), want_quot, NULL), tvbparse_set_seq(-1, NULL, NULL, NULL, want_quot, want_quot, NULL), NULL); } void tvbparse_shrink_token_cb(void* tvbparse_data _U_, const void* wanted_data _U_, tvbparse_elem_t* tok) { tok->offset += 1; tok->len -= 2; } tvbparse_t* tvbparse_init(tvbuff_t* tvb, int offset, int len, void* data, const tvbparse_wanted_t* ignore) { tvbparse_t* tt = ep_alloc(sizeof(tvbparse_t)); tt->tvb = tvb; tt->offset = offset; tt->max_len = (len == -1) ? (int) tvb_length(tvb) : len; tt->data = data; tt->ignore = ignore; tt->depth = 0; return tt; } gboolean tvbparse_reset(tvbparse_t* tt, int offset, int len) { len = (len == -1) ? (int) tvb_length(tt->tvb) : len; if( tvb_length_remaining(tt->tvb, offset) >= len) { tt->offset = offset; tt->max_len = len; tt->depth = 0; return TRUE; } else { tt->depth = 0; return FALSE; } } static tvbparse_elem_t* new_tok(tvbparse_t* tt, int id, int offset, int len, const tvbparse_wanted_t* wanted) { tvbparse_elem_t* tok = ep_alloc(sizeof(tvbparse_elem_t)); tok->tvb = tt->tvb; tok->id = id; tok->offset = offset; tok->len = len; tok->data = NULL; tok->sub = NULL; tok->next = NULL; tok->wanted = wanted; tok->last = tok; return tok; } guint tvbparse_curr_offset(tvbparse_t* tt) { return tt->offset; } guint tvbparse_len_left(tvbparse_t* tt) { return tt->max_len; } tvbparse_elem_t* tvbparse_get(tvbparse_t* tt, const tvbparse_wanted_t* wanted) { tvbparse_elem_t* tok = NULL; int save_offset = tt->offset; int save_len = tt->max_len; tt->depth++; if (tt->ignore && tt->ignore != wanted) { tvbparse_wanted_t* save = (void*)tt->ignore; tt->ignore = NULL; while ( tvbparse_get(tt,save) ) { ; } tt->ignore = save; } switch(wanted->type) { case TVBPARSE_WANTED_NONE: goto reject; case TVBPARSE_WANTED_SIMPLE_NOT_CHAR: { gchar c, t; guint i; gboolean not_matched = FALSE; if (! tt->max_len ) goto reject; t = (gchar) tvb_get_guint8(tt->tvb,tt->offset); for(i = 0; (c = wanted->control.str[i]) && tt->max_len; i++) { if ( c == t ) { not_matched = TRUE; } } if (not_matched) { goto reject; } else { tt->offset++; tt->max_len--; tok = new_tok(tt,wanted->id,tt->offset-1,1,wanted); goto accept; } } case TVBPARSE_WANTED_SIMPLE_CHAR: { gchar c,t; guint i; if (! tt->max_len ) goto reject; t = (gchar) tvb_get_guint8(tt->tvb,tt->offset); for(i = 0; (c = wanted->control.str[i]) && tt->max_len; i++) { if ( c == t ) { tt->offset++; tt->max_len--; tok = new_tok(tt,wanted->id,tt->offset-1,1,wanted); goto accept; } } goto reject; } case TVBPARSE_WANTED_SIMPLE_NOT_CHARS: { gchar c, t; guint i; guint offset = tt->offset; guint length = 0; while( tt->max_len && length < wanted->max) { gboolean not_matched = FALSE; t = (gchar) tvb_get_guint8(tt->tvb,tt->offset); i = 0; while ( (c = wanted->control.str[i]) && tt->max_len ) { if (c == t) { not_matched = TRUE; } i++; } if ( not_matched ) break; length++; tt->offset++; tt->max_len--; }; if ( length < wanted->min ) { goto reject; } else { tok = new_tok(tt,wanted->id,offset,length,wanted); goto accept; } } case TVBPARSE_WANTED_SIMPLE_CHARS: { gchar c, t; guint i; guint offset = tt->offset; guint length = 0; while( tt->max_len && length < wanted->max) { gboolean matched = FALSE; t = (gchar) tvb_get_guint8(tt->tvb,tt->offset); i = 0; while ( (c = wanted->control.str[i]) && tt->max_len ) { if (c == t) { matched = TRUE; break; } i++; } if (! matched ) break; length++; tt->offset++; tt->max_len--; }; if (length < wanted->min) { goto reject; } else { tok = new_tok(tt,wanted->id,offset,length,wanted); goto accept; } } case TVBPARSE_WANTED_SIMPLE_STRING: { if ( tvb_strneql(tt->tvb, tt->offset, wanted->control.str, wanted->len) == 0 ) { int offset = tt->offset; tt->offset += wanted->len; tt->max_len -= wanted->len; tok = new_tok(tt,wanted->id,offset,wanted->len,wanted); goto accept; } else { goto reject; } } case TVBPARSE_WANTED_SIMPLE_CASESTRING: { if ( tvb_strncaseeql(tt->tvb, tt->offset, wanted->control.str, wanted->len) == 0 ) { int offset = tt->offset; tt->offset += wanted->len; tt->max_len -= wanted->len; tok = new_tok(tt,wanted->id,offset,wanted->len,wanted); goto accept; } else { goto reject; } } case TVBPARSE_WANTED_SET_ONEOF: { guint i; for(i=0; i < wanted->elems->len; i++) { tvbparse_wanted_t* w = g_ptr_array_index(wanted->elems,i); tvbparse_elem_t* new = tvbparse_get(tt, w); if (new) { tok = new_tok(tt, wanted->id, new->offset, new->len, wanted); tok->sub = new; goto accept; } } goto reject; } case TVBPARSE_WANTED_SET_SEQ: { guint i; for(i=0; i < wanted->elems->len; i++) { tvbparse_wanted_t* w = g_ptr_array_index(wanted->elems,i); tvbparse_elem_t* new = tvbparse_get(tt, w); if (new) { if (tok) { tok->len = (new->offset - tok->offset) + new->len; tok->sub->last->next = new; tok->sub->last = new; } else { tok = new_tok(tt, wanted->id, new->offset, new->len, wanted); tok->sub = new; } } else { goto reject; } } goto accept; } case TVBPARSE_WANTED_CARDINALITY: { guint got_so_far = 0; tvbparse_wanted_t* w = g_ptr_array_index(wanted->elems,0); if ( wanted->min == 0 ) { tok = new_tok(tt,wanted->id,tt->offset,0,wanted); } while (got_so_far < wanted->max) { tvbparse_elem_t* new = tvbparse_get(tt, w); if(new) { if (tok) { tok->len = (new->offset - tok->offset) + new->len; if (tok->sub) { tok->sub->last->next = new; tok->sub->last = new; } else { tok->sub = new; } } else { tok = new_tok(tt, wanted->id, new->offset, new->len, wanted); tok->sub = new; } } else { break; } got_so_far++; } if(got_so_far < wanted->min) { goto reject; } goto accept; } case TVBPARSE_WANTED_UNTIL: { int offset = tt->offset; tvbparse_wanted_t* w = g_ptr_array_index(wanted->elems,0); tvbparse_elem_t* new = tvbparse_find(tt, w); if (new) { tok = new; switch (wanted->control.val) { case TP_UNTIL_INCLUDE: tok->len = (tok->offset - offset) + tok->len; break; case TP_UNTIL_LEAVE: tt->offset -= tok->len; tt->max_len += tok->len; /* fall through */ case TP_UNTIL_SPEND: tok->len = (tok->offset - offset); break; default: DISSECTOR_ASSERT_NOT_REACHED(); } tok->offset = offset; tok->id = wanted->id; tok->next = NULL; tok->last = tok; tok->wanted = wanted; goto accept; } else { goto reject; } } case TVBPARSE_WANTED_HANDLE: { tok = tvbparse_get(tt, *(wanted->control.handle)); if (tok) { goto accept; } else { goto reject; } } } DISSECTOR_ASSERT_NOT_REACHED(); return NULL; accept: if (tok) { if( tt->depth == 1 ) { GPtrArray* stack = g_ptr_array_new(); tvbparse_elem_t* curr = tok; while (curr) { if(curr->wanted->before) { curr->wanted->before(tt->data, curr->wanted->data, curr); } if(curr->sub) { g_ptr_array_add(stack,curr); curr = curr->sub; continue; } else { if(curr->wanted->after) curr->wanted->after(tt->data, curr->wanted->data, curr); } curr = curr->next; while( !curr && stack->len ) { curr = g_ptr_array_remove_index_fast(stack,stack->len - 1); if( curr->wanted->after ) curr->wanted->after(tt->data, curr->wanted->data, curr); curr = curr->next; } } g_ptr_array_free(stack,TRUE); } tt->depth--; return tok; } reject: tt->offset = save_offset; tt->max_len = save_len; tt->depth--; return NULL; } tvbparse_elem_t* tvbparse_find(tvbparse_t* tt, const tvbparse_wanted_t* wanted) { int save_offset = tt->offset; int save_len = tt->max_len; tvbparse_elem_t* tok = NULL; while ( tvb_length_remaining(tt->tvb,tt->offset) >= wanted->len ) { if (( tok = tvbparse_get(tt, wanted) )) { return tok; } tt->offset++; tt->max_len--; } tt->offset = save_offset; tt->max_len = save_len; return NULL; } struct _elem_tree_stack_frame { proto_tree* tree; tvbparse_elem_t* elem; }; void tvbparse_tree_add_elem(proto_tree* tree, tvbparse_elem_t* curr) { GPtrArray* stack = g_ptr_array_new(); struct _elem_tree_stack_frame* frame = ep_alloc(sizeof(struct _elem_tree_stack_frame)); proto_item* pi; frame->tree = tree; frame->elem = curr; while (curr) { pi = proto_tree_add_text(frame->tree,curr->tvb,curr->offset,curr->len,"%s",tvb_format_text(curr->tvb,curr->offset,curr->len)); if(curr->sub) { frame->elem = curr; g_ptr_array_add(stack,frame); frame = ep_alloc(sizeof(struct _elem_tree_stack_frame)); frame->tree = proto_item_add_subtree(pi,0); curr = curr->sub; continue; } curr = curr->next; while( !curr && stack->len ) { frame = g_ptr_array_remove_index_fast(stack,stack->len - 1); curr = frame->elem->next; } } g_ptr_array_free(stack,TRUE); }