/*#define DEBUG_BER 1*/ /* TODO: change #.REGISTER signature to new_dissector_t and * update call_ber_oid_callback() accordingly. */ /* packet-ber.c * Helpers for ASN.1/BER dissection * Ronnie Sahlberg (C) 2004 * * $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. */ /* * ITU-T Recommendation X.690 (07/2002), * Information technology ASN.1 encoding rules: * Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER) * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include "packet-ber.h" static gint proto_ber = -1; static gint hf_ber_id_class = -1; static gint hf_ber_id_pc = -1; static gint hf_ber_id_uni_tag = -1; static gint hf_ber_id_tag = -1; static gint hf_ber_length = -1; static gint hf_ber_bitstring_padding = -1; static gint hf_ber_unknown_OID = -1; static gint hf_ber_unknown_OCTETSTRING = -1; static gint hf_ber_unknown_NumericString = -1; static gint hf_ber_unknown_PrintableString = -1; static gint hf_ber_unknown_IA5String = -1; static gint hf_ber_unknown_INTEGER = -1; static gint hf_ber_unknown_ENUMERATED = -1; static gint ett_ber_octet_string = -1; static gint ett_ber_unknown = -1; static gint ett_ber_SEQUENCE = -1; static gboolean show_internal_ber_fields = FALSE; proto_item *ber_last_created_item=NULL; static dissector_table_t ber_oid_dissector_table=NULL; static const value_string ber_class_codes[] = { { BER_CLASS_UNI, "Universal" }, { BER_CLASS_APP, "Application" }, { BER_CLASS_CON, "Context Specific" }, { BER_CLASS_PRI, "Private" }, { 0, NULL } }; static const true_false_string ber_pc_codes = { "Constructed Encoding", "Primitive Encoding" }; static const value_string ber_uni_tag_codes[] = { { BER_UNI_TAG_EOC , "'end-of-content'" }, { BER_UNI_TAG_BOOLEAN , "BOOLEAN" }, { BER_UNI_TAG_INTEGER , "INTEGER" }, { BER_UNI_TAG_BITSTRING , "BIT STRING" }, { BER_UNI_TAG_OCTETSTRING , "OCTET STRING" }, { BER_UNI_TAG_NULL , "NULL" }, { BER_UNI_TAG_OID , "OBJECT IDENTIFIER" }, { BER_UNI_TAG_ObjectDescriptor, "ObjectDescriptor" }, { BER_UNI_TAG_REAL , "REAL" }, { BER_UNI_TAG_ENUMERATED , "ENUMERATED" }, { BER_UNI_TAG_EMBEDDED_PDV , "EMBEDDED PDV" }, { BER_UNI_TAG_UTF8String , "UTF8String" }, { BER_UNI_TAG_RELATIVE_OID , "RELATIVE-OID" }, { BER_UNI_TAG_SEQUENCE , "SEQUENCE, SEQUENCE OF" }, { BER_UNI_TAG_SET , "SET, SET OF" }, { BER_UNI_TAG_NumericString , "NumericString" }, { BER_UNI_TAG_PrintableString , "PrintableString" }, { BER_UNI_TAG_TeletexString , "TeletexString, T61String" }, { BER_UNI_TAG_VideotexString , "VideotexString" }, { BER_UNI_TAG_IA5String , "IA5String" }, { BER_UNI_TAG_UTCTime , "UTCTime" }, { BER_UNI_TAG_GeneralizedTime , "GeneralizedTime" }, { BER_UNI_TAG_GraphicString , "GraphicString" }, { BER_UNI_TAG_VisibleString , "VisibleString, ISO64String" }, { BER_UNI_TAG_GeneralString , "GeneralString" }, { BER_UNI_TAG_UniversalString , "UniversalString" }, { BER_UNI_TAG_CHARACTERSTRING , "CHARACTER STRING" }, { BER_UNI_TAG_BMPString , "BMPString" }, { 0, NULL } }; proto_item *get_ber_last_created_item(void) { return ber_last_created_item; } static GHashTable *oid_table=NULL; void dissect_ber_oid_NULL_callback(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_) { return; } void register_ber_oid_dissector(char *oid, dissector_t dissector, int proto, char *name) { dissector_handle_t dissector_handle; dissector_handle=create_dissector_handle(dissector, proto); dissector_add_string("ber.oid", oid, dissector_handle); g_hash_table_insert(oid_table, oid, name); } /* Register the oid name to get translation in proto dissection */ void register_ber_oid_name(char *oid, char *name) { g_hash_table_insert(oid_table, oid, name); } /* Get oid name fom has table to get translation in proto dissection(packet-per.c) */ char * get_ber_oid_name(char *oid) { return g_hash_table_lookup(oid_table, oid); } /* this function tries to dissect an unknown blob as much as possible. * everytime this function is called it is a failure to implement a proper * dissector in ethereal. * something is missing, so dont become too comfy with this one, * when it is called it is a BAD thing not a good thing. * It can not handle IMPLICIT tags nor indefinite length. */ static int dissect_unknown_ber(packet_info *pinfo, tvbuff_t *tvb, int offset, proto_tree *tree) { int start_offset; guint8 class; gboolean pc, ind; guint32 tag; guint32 len; proto_item *item=NULL; proto_tree *next_tree=NULL; start_offset=offset; offset=dissect_ber_identifier(pinfo, NULL, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, NULL, tvb, offset, &len, &ind); if(len>(guint32)tvb_length_remaining(tvb, offset)){ /* hmm maybe something bad happened or the frame is short, since these are not vital outputs just return instead of throwing en exception. */ proto_tree_add_text(tree, tvb, offset, len, "BER: Error length:%d longer than tvb_length_ramaining:%d",len, tvb_length_remaining(tvb, offset)); return tvb_length(tvb); } switch(class){ case BER_CLASS_UNI: switch(tag){ case BER_UNI_TAG_INTEGER: offset = dissect_ber_integer(FALSE, pinfo, tree, tvb, start_offset, hf_ber_unknown_INTEGER, NULL); break; case BER_UNI_TAG_ENUMERATED: offset = dissect_ber_integer(FALSE, pinfo, tree, tvb, start_offset, hf_ber_unknown_ENUMERATED, NULL); break; case BER_UNI_TAG_OCTETSTRING: offset = dissect_ber_octet_string(FALSE, pinfo, tree, tvb, start_offset, hf_ber_unknown_OCTETSTRING, NULL); break; case BER_UNI_TAG_OID: offset=dissect_ber_object_identifier(FALSE, pinfo, tree, tvb, start_offset, hf_ber_unknown_OID, NULL); break; case BER_UNI_TAG_SEQUENCE: item=proto_tree_add_text(tree, tvb, start_offset, len, "SEQUENCE"); if(item){ next_tree=proto_item_add_subtree(item, ett_ber_SEQUENCE); } offset=dissect_unknown_ber(pinfo, tvb, offset, next_tree); break; case BER_UNI_TAG_NumericString: offset = dissect_ber_octet_string(FALSE, pinfo, tree, tvb, start_offset, hf_ber_unknown_NumericString, NULL); break; case BER_UNI_TAG_PrintableString: offset = dissect_ber_octet_string(FALSE, pinfo, tree, tvb, start_offset, hf_ber_unknown_PrintableString, NULL); break; case BER_UNI_TAG_IA5String: offset = dissect_ber_octet_string(FALSE, pinfo, tree, tvb, start_offset, hf_ber_unknown_IA5String, NULL); break; default: proto_tree_add_text(tree, tvb, offset, len, "BER: Error can not handle universal tag:%d",tag); offset += len; } break; case BER_CLASS_CON: item=proto_tree_add_text(tree, tvb, start_offset, len, "[%d]",tag); if(item){ next_tree=proto_item_add_subtree(item, ett_ber_SEQUENCE); } offset=dissect_unknown_ber(pinfo, tvb, offset, next_tree); break; default: proto_tree_add_text(tree, tvb, offset, len, "BER: Error can not handle class:%d (0x%02x)",class,tvb_get_guint8(tvb, start_offset)); /* some printout here? aborting dissection */ return tvb_length(tvb); } /* were there more data to eat? */ if(offset<(int)tvb_length(tvb)){ offset=dissect_unknown_ber(pinfo, tvb, offset, tree); } return offset; } int call_ber_oid_callback(char *oid, tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree) { tvbuff_t *next_tvb; next_tvb = tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset), tvb_length_remaining(tvb, offset)); if(!dissector_try_string(ber_oid_dissector_table, oid, next_tvb, pinfo, tree)){ proto_item *item=NULL; proto_tree *next_tree=NULL; item=proto_tree_add_text(tree, next_tvb, 0, tvb_length_remaining(tvb, offset), "BER: Dissector for OID:%s not implemented. Contact Ethereal developers if you want this supported", oid); if(item){ next_tree=proto_item_add_subtree(item, ett_ber_unknown); } dissect_unknown_ber(pinfo, next_tvb, offset, next_tree); } /*XXX until we change the #.REGISTER signature for _PDU()s * into new_dissector_t we have to do this kludge with * manually step past the content in the ANY type. */ offset+=tvb_length_remaining(tvb, offset); return offset; } static int dissect_ber_sq_of(gboolean implicit_tag, gint32 type, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_sequence_t *seq, gint hf_id, gint ett_id); /* 8.1 General rules for encoding */ /* 8.1.2 Identifier octets */ int get_ber_identifier(tvbuff_t *tvb, int offset, gint8 *class, gboolean *pc, gint32 *tag) { guint8 id, t; gint8 tmp_class; gboolean tmp_pc; gint32 tmp_tag; id = tvb_get_guint8(tvb, offset); offset += 1; /* 8.1.2.2 */ tmp_class = (id>>6) & 0x03; tmp_pc = (id>>5) & 0x01; tmp_tag = id&0x1F; /* 8.1.2.4 */ if (tmp_tag == 0x1F) { tmp_tag = 0; while (tvb_length_remaining(tvb, offset) > 0) { t = tvb_get_guint8(tvb, offset); offset += 1; tmp_tag <<= 7; tmp_tag |= t & 0x7F; if (t & 0x80) break; } } if (class) *class = tmp_class; if (pc) *pc = tmp_pc; if (tag) *tag = tmp_tag; return offset; } int dissect_ber_identifier(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, gint8 *class, gboolean *pc, gint32 *tag) { int old_offset = offset; gint8 tmp_class; gboolean tmp_pc; gint32 tmp_tag; offset = get_ber_identifier(tvb, offset, &tmp_class, &tmp_pc, &tmp_tag); if(show_internal_ber_fields){ proto_tree_add_uint(tree, hf_ber_id_class, tvb, old_offset, 1, tmp_class<<6); proto_tree_add_boolean(tree, hf_ber_id_pc, tvb, old_offset, 1, (tmp_pc)?0x20:0x00); if(tmp_class==BER_CLASS_UNI){ proto_tree_add_uint(tree, hf_ber_id_uni_tag, tvb, old_offset, offset - old_offset, tmp_tag); } else { proto_tree_add_uint(tree, hf_ber_id_tag, tvb, old_offset, offset - old_offset, tmp_tag); } } if (class) *class = tmp_class; if (pc) *pc = tmp_pc; if (tag) *tag = tmp_tag; return offset; } /* this function gets the length octets of the BER TLV. * We only handle (TAGs and) LENGTHs that fit inside 32 bit integers. */ /* 8.1.3 Length octets */ int get_ber_length(tvbuff_t *tvb, int offset, guint32 *length, gboolean *ind) { guint8 oct, len; guint32 tmp_length; gboolean tmp_ind; tmp_length = 0; tmp_ind = FALSE; oct = tvb_get_guint8(tvb, offset); offset += 1; if (!(oct&0x80)) { /* 8.1.3.4 */ tmp_length = oct; } else { len = oct & 0x7F; if (len) { /* 8.1.3.5 */ while (len--) { oct = tvb_get_guint8(tvb, offset); offset++; tmp_length = (tmp_length<<8) + oct; } } else { /* 8.1.3.6 */ tmp_ind = TRUE; /* TO DO */ } } if (length) *length = tmp_length; if (ind) *ind = tmp_ind; return offset; } /* this function dissects the length octets of the BER TLV. * We only handle (TAGs and) LENGTHs that fit inside 32 bit integers. */ int dissect_ber_length(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, guint32 *length, gboolean *ind) { int old_offset = offset; guint32 tmp_length; gboolean tmp_ind; offset = get_ber_length(tvb, offset, &tmp_length, &tmp_ind); if(show_internal_ber_fields){ if(tmp_ind){ proto_tree_add_text(tree, tvb, old_offset, 1, "Length: Indefinite length"); } else { proto_tree_add_uint(tree, hf_ber_length, tvb, old_offset, offset - old_offset, tmp_length); } } if (length) *length = tmp_length; if (ind) *ind = tmp_ind; return offset; } /* 8.7 Encoding of an octetstring value */ int dissect_ber_octet_string(gboolean implicit_tag, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **out_tvb) { guint8 class; gboolean pc, ind; guint32 tag; guint32 len; int end_offset; proto_item *it; #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } if(tvb_length_remaining(tvb,offset)>3){ printf("OCTET STRING dissect_ber_octet string(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n",name,implicit_tag,offset,tvb_length_remaining(tvb,offset),tvb_get_guint8(tvb,offset),tvb_get_guint8(tvb,offset+1),tvb_get_guint8(tvb,offset+2)); }else{ printf("OCTET STRING dissect_ber_octet_string(%s) entered\n",name); } } #endif if (!implicit_tag) { /* read header and len for the octet string */ offset=dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, tree, tvb, offset, &len, &ind); end_offset=offset+len; /* sanity check: we only handle Constructed Universal Sequences */ if( (class!=BER_CLASS_UNI) ||((tag0)) { if (hf_id != -1) tree = proto_item_add_subtree(ber_last_created_item, ett_ber_octet_string); func(pinfo, tree, out_tvb, 0); } return offset; } int dissect_ber_integer(gboolean implicit_tag, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, guint32 *value) { guint8 class; gboolean pc; guint32 tag; guint32 len; gint32 val; gint64 val64; guint32 i; #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } if(tvb_length_remaining(tvb,offset)>3){ printf("INTEGERnew dissect_ber_integer(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n",name,implicit_tag,offset,tvb_length_remaining(tvb,offset),tvb_get_guint8(tvb,offset),tvb_get_guint8(tvb,offset+1),tvb_get_guint8(tvb,offset+2)); }else{ printf("INTEGERnew dissect_ber_integer(%s) entered implicit_tag:%d len:%d\n",name,implicit_tag,len); } } #endif if(!implicit_tag){ offset=dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); } else { len=tvb_length_remaining(tvb, offset); } /* ok, we cant handle >4 byte integers so lets fake them */ if(len>8){ header_field_info *hfinfo; proto_item *pi; hfinfo = proto_registrar_get_nth(hf_id); pi=proto_tree_add_text(tree, tvb, offset, len, "%s : 0x", hfinfo->name); if(pi){ for(i=0;i4){ header_field_info *hfinfo; val64=0; if (len > 0) { /* extend sign bit */ val64 = (gint8)tvb_get_guint8(tvb, offset); offset++; } for(i=1;iname, val64); return offset; } val=0; if (len > 0) { /* extend sign bit */ val = (gint8)tvb_get_guint8(tvb, offset); offset++; } for(i=1;itype == FT_BOOLEAN) ber_last_created_item=proto_tree_add_boolean(tree, hf_id, tvb, offset-1, 1, val); else ber_last_created_item=proto_tree_add_uint(tree, hf_id, tvb, offset-1, 1, val?1:0); } return offset; } /* this function dissects a BER sequence */ int dissect_ber_sequence(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_sequence_t *seq, gint hf_id, gint ett_id) { gint8 class; gboolean pc, ind, ind_field; gint32 tag; guint32 len; proto_tree *tree = parent_tree; proto_item *item = NULL; int end_offset; tvbuff_t *next_tvb; #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } if(tvb_length_remaining(tvb,offset)>3){ printf("SEQUENCE dissect_ber_sequence(%s) entered offset:%d len:%d %02x:%02x:%02x\n",name,offset,tvb_length_remaining(tvb,offset),tvb_get_guint8(tvb,offset),tvb_get_guint8(tvb,offset+1),tvb_get_guint8(tvb,offset+2)); }else{ printf("SEQUENCE dissect_ber_sequence(%s) entered\n",name); } } #endif if(!implicit_tag){ /* first we must read the sequence header */ offset = dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, &ind); if(ind){ /* if the length is indefinite we dont really know (yet) where the * object ends so assume it spans the rest of the tvb for now. */ end_offset = tvb_length(tvb); } else { end_offset = offset + len; } /* sanity check: we only handle Constructed Universal Sequences */ if ((!pc) ||(!implicit_tag&&((class!=BER_CLASS_UNI) ||(tag!=BER_UNI_TAG_SEQUENCE)))) { proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: Sequence expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return end_offset; } } else { /* was implicit tag so just use the length of the tvb */ len=tvb_length_remaining(tvb,offset); end_offset=offset+len; } /* create subtree */ if (hf_id != -1) { if(parent_tree){ item = proto_tree_add_item(parent_tree, hf_id, tvb, offset, len, FALSE); tree = proto_item_add_subtree(item, ett_id); } } /* loop over all entries until we reach the end of the sequence */ while (offset < end_offset){ gint8 class; gboolean pc; gint32 tag; guint32 len; int hoffset, eoffset, count; if(ind){ /* this sequence was of indefinite length, so check for EOC */ if((tvb_get_guint8(tvb, offset)==0)&&(tvb_get_guint8(tvb, offset+1)==0)){ if(show_internal_ber_fields){ proto_tree_add_text(tree, tvb, offset, 2, "EOC"); } return offset+2; } } hoffset = offset; /* read header and len for next field */ offset = get_ber_identifier(tvb, offset, &class, &pc, &tag); offset = get_ber_length(tvb, offset, &len, &ind_field); eoffset = offset + len; ber_sequence_try_again: /* have we run out of known entries in the sequence ?*/ if (!seq->func) { /* it was not, move to the enxt one and try again */ proto_tree_add_text(tree, tvb, offset, len, "BER Error: This field lies beyond the end of the known sequence definition."); offset = eoffset; continue; } /* Verify that this one is the one we want. * Skip check completely if class==ANY * of if NOCHKTAG is set */ /* XXX Bug in asn2eth, * for scope [7] Scope OPTIONAL, * it generates * { BER_CLASS_CON, 7, BER_FLAGS_OPTIONAL|BER_FLAGS_NOTCHKTAG, dissect_scope }, * and there should not be a NOTCHKTAG here */ if( (seq->class==BER_CLASS_CON) && (!(seq->flags&BER_FLAGS_NOOWNTAG)) ){ if( (seq->class!=BER_CLASS_ANY) && (seq->tag!=-1) &&( (seq->class!=class) ||(seq->tag!=tag) ) ){ /* it was not, move to the enxt one and try again */ if(seq->flags&BER_FLAGS_OPTIONAL){ /* well this one was optional so just skip to the next one and try again. */ seq++; goto ber_sequence_try_again; } proto_tree_add_text(tree, tvb, offset, len, "BER Error: Wrong field in SEQUENCE expected class:%d tag:%d but found class:%d tag:%d",seq->class,seq->tag,class,tag); seq++; offset=eoffset; continue; } } else if (!(seq->flags & BER_FLAGS_NOTCHKTAG)) { if( (seq->class!=BER_CLASS_ANY) && (seq->tag!=-1) &&( (seq->class!=class) ||(seq->tag!=tag) ) ){ /* it was not, move to the enxt one and try again */ if(seq->flags&BER_FLAGS_OPTIONAL){ /* well this one was optional so just skip to the next one and try again. */ seq++; goto ber_sequence_try_again; } proto_tree_add_text(tree, tvb, offset, len, "BER Error: Wrong field in sequence expected class:%d tag:%d but found class:%d tag:%d",seq->class,seq->tag,class,tag); seq++; offset=eoffset; continue; } } if (!(seq->flags & BER_FLAGS_NOOWNTAG) ) { /* dissect header and len for field */ hoffset = dissect_ber_identifier(pinfo, tree, tvb, hoffset, NULL, NULL, NULL); hoffset = dissect_ber_length(pinfo, tree, tvb, hoffset, NULL, NULL); } /* call the dissector for this field */ if(ind_field || ((eoffset-hoffset)>tvb_length_remaining(tvb, hoffset)) ){ /* If the field is indefinite (i.e. we dont know the * length) of if the tvb is short, then just * give it all of the tvb and hope for the best. */ next_tvb = tvb_new_subset(tvb, hoffset, tvb_length_remaining(tvb,hoffset), tvb_length_remaining(tvb,hoffset)); } else { next_tvb = tvb_new_subset(tvb, hoffset, eoffset-hoffset, eoffset-hoffset); } #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } if(tvb_length_remaining(next_tvb,0)>3){ printf("SEQUENCE dissect_ber_sequence(%s) calling subdissector offset:%d len:%d %02x:%02x:%02x\n",name,offset,tvb_length_remaining(next_tvb,0),tvb_get_guint8(next_tvb,0),tvb_get_guint8(next_tvb,1),tvb_get_guint8(next_tvb,2)); }else{ printf("SEQUENCE dissect_ber_sequence(%s) calling subdissector\n",name); } } #endif count=seq->func(pinfo, tree, next_tvb, 0); #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } printf("SEQUENCE dissect_ber_sequence(%s) subdissector ate %d bytes\n",name,count); } #endif seq++; offset = hoffset+count; /* if it was optional and no bytes were eaten, just try again. */ if((count==0)&&(seq->flags&BER_FLAGS_OPTIONAL)){ goto ber_sequence_try_again; } } /* if we didnt end up at exactly offset, then we ate too many bytes */ if (offset != end_offset) { proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: Sequence ate %d too many bytes", offset-end_offset); } return end_offset; } /* this function dissects a BER choice * If we did not find a matching choice, just return offset unchanged * in case it was a CHOICE { } OPTIONAL */ int dissect_ber_choice(packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_choice_t *choice, gint hf_id, gint ett_id) { gint8 class; gboolean pc, ind; gint32 tag; guint32 len; const ber_choice_t *ch; proto_tree *tree=parent_tree; proto_item *item=NULL; int end_offset, start_offset, count; int hoffset = offset; header_field_info *hfinfo; gint reported_length, length; tvbuff_t *next_tvb; #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } if(tvb_length_remaining(tvb,offset)>3){ printf("CHOICE dissect_ber_choice(%s) entered offset:%d len:%d %02x:%02x:%02x\n",name,offset,tvb_length_remaining(tvb,offset),tvb_get_guint8(tvb,offset),tvb_get_guint8(tvb,offset+1),tvb_get_guint8(tvb,offset+2)); }else{ printf("CHOICE dissect_ber_choice(%s) entered len:%d\n",name,tvb_length_remaining(tvb,offset)); } } #endif start_offset=offset; /* read header and len for choice field */ offset=get_ber_identifier(tvb, offset, &class, &pc, &tag); offset=get_ber_length(tvb, offset, &len, &ind); if(ind){ /* if the length is indefinite we dont really know (yet) where the * object ends so assume it spans the rest of the tvb for now. * XXX - what if it runs past the end of the tvb because we * need to do reassembly? */ end_offset = tvb_length(tvb); } else { end_offset = offset + len; } /* Some sanity checks. * The hf field passed to us MUST be an integer type */ if(hf_id!=-1){ hfinfo=proto_registrar_get_nth(hf_id); switch(hfinfo->type) { case FT_UINT8: case FT_UINT16: case FT_UINT24: case FT_UINT32: break; default: proto_tree_add_text(tree, tvb, offset, len,"dissect_ber_choice(): Was passed a HF field that was not integer type : %s",hfinfo->abbrev); fprintf(stderr,"dissect_ber_choice(): frame:%d offset:%d Was passed a HF field that was not integer type : %s\n",pinfo->fd->num,offset,hfinfo->abbrev); return end_offset; } } /* loop over all entries until we find the right choice or run out of entries */ ch = choice; while(ch->func){ choice_try_again: #ifdef DEBUG_BER printf("CHOICE testing potential subdissector class:%d:(expected)%d tag:%d:(expected)%d flags:%d\n",class,ch->class,tag,ch->tag,ch->flags); #endif if( ((ch->class==class)&&(ch->tag==tag)) || ((ch->class==class)&&(ch->tag==-1)&&(ch->flags&BER_FLAGS_NOOWNTAG)) ){ if (!(ch->flags & BER_FLAGS_NOOWNTAG)){ /* dissect header and len for field */ hoffset = dissect_ber_identifier(pinfo, tree, tvb, start_offset, NULL, NULL, NULL); hoffset = dissect_ber_length(pinfo, tree, tvb, hoffset, NULL, NULL); start_offset=hoffset; } /* create subtree */ if(hf_id!=-1){ if(parent_tree){ item = proto_tree_add_uint(parent_tree, hf_id, tvb, hoffset, end_offset - hoffset, ch->value); tree = proto_item_add_subtree(item, ett_id); } } reported_length = end_offset-start_offset; length = tvb_length_remaining(tvb, hoffset); if (length > reported_length) length = reported_length; next_tvb=tvb_new_subset(tvb, hoffset, length, reported_length); #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } if(tvb_length_remaining(next_tvb,0)>3){ printf("CHOICE dissect_ber_choice(%s) calling subdissector start_offset:%d offset:%d len:%d %02x:%02x:%02x\n",name,start_offset,offset,tvb_length_remaining(next_tvb,0),tvb_get_guint8(next_tvb,0),tvb_get_guint8(next_tvb,1),tvb_get_guint8(next_tvb,2)); }else{ printf("CHOICE dissect_ber_choice(%s) calling subdissector len:%d\n",name,tvb_length(next_tvb)); } } #endif count=ch->func(pinfo, tree, next_tvb, 0); #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } printf("CHOICE dissect_ber_choice(%s) subdissector ate %d bytes\n",name,count); } #endif if((count==0)&&(ch->class==class)&&(ch->tag==-1)&&(ch->flags&BER_FLAGS_NOOWNTAG)){ /* wrong one, break and try again */ ch++; goto choice_try_again; } offset=hoffset+count; if(ind){ /* this choice was of indefinite length so we * just have to trust what the subdissector * told us about the length consumed. */ return offset; } else { return end_offset; } break; } ch++; } #ifdef REMOVED /*XXX here we should have another flag to the CHOICE to distinguish * between teh case when we know it is a mandatory or if the CHOICE is optional == no arm matched */ /* oops no more entries and we still havent found * our guy :-( */ proto_tree_add_text(tree, tvb, offset, len, "BER Error: This choice field was not found."); return end_offset; #endif return start_offset; } #if 0 /* this function dissects a BER GeneralString */ int dissect_ber_GeneralString(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, char *name_string, int name_len) { gint8 class; gboolean pc; gint32 tag; guint32 len; int end_offset; char str_arr[256]; guint32 max_len; char *str; str=str_arr; max_len=255; if(name_string){ str=name_string; max_len=name_len; } /* first we must read the GeneralString header */ offset=dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); end_offset=offset+len; /* sanity check: we only handle Universal GeneralString*/ if( (class!=BER_CLASS_UNI) ||(tag!=BER_UNI_TAG_GENSTR) ){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: GeneralString expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return end_offset; } if(len>=(max_len-1)){ len=max_len-1; } tvb_memcpy(tvb, str, offset, len); str[len]=0; if(hf_id!=-1){ proto_tree_add_string(tree, hf_id, tvb, offset, len, str); } return end_offset; } #endif int dissect_ber_restricted_string(gboolean implicit_tag, gint32 type, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **out_tvb) { gint8 class; gboolean pc; gint32 tag; guint32 len; int eoffset; int hoffset = offset; #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } if(tvb_length_remaining(tvb,offset)>3){ printf("RESTRICTED STRING dissect_ber_octet string(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n",name,implicit_tag,offset,tvb_length_remaining(tvb,offset),tvb_get_guint8(tvb,offset),tvb_get_guint8(tvb,offset+1),tvb_get_guint8(tvb,offset+2)); }else{ printf("RESTRICTED STRING dissect_ber_octet_string(%s) entered\n",name); } } #endif if (!implicit_tag) { offset = get_ber_identifier(tvb, offset, &class, &pc, &tag); offset = get_ber_length(tvb, offset, &len, NULL); eoffset = offset + len; /* sanity check */ if( (class!=BER_CLASS_UNI) ||(tag != type) ){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: String with tag=%d expected but Class:%d PC:%d Tag:%d was unexpected", type, class, pc, tag); return eoffset; } } /* 8.21.3 */ return dissect_ber_octet_string(implicit_tag, pinfo, tree, tvb, hoffset, hf_id, out_tvb); } int dissect_ber_GeneralString(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, char *name_string, guint name_len) { tvbuff_t *out_tvb; offset = dissect_ber_restricted_string(FALSE, BER_UNI_TAG_GeneralString, pinfo, tree, tvb, offset, hf_id, (name_string)?&out_tvb:NULL); if (name_string) { if (tvb_length(out_tvb) >= name_len) { tvb_memcpy(out_tvb, name_string, 0, name_len-1); name_string[name_len-1] = '\0'; } else { tvb_memcpy(out_tvb, name_string, 0, -1); name_string[tvb_length(out_tvb)] = '\0'; } } return offset; } /* 8.19 Encoding of an object identifier value */ int dissect_ber_object_identifier(gboolean implicit_tag, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, char *value_string) { gint8 class; gboolean pc; gint32 tag; guint32 i, len; int eoffset; guint8 byte; guint32 value; char str[256],*strp, *name; proto_item *item; #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } if(tvb_length_remaining(tvb,offset)>3){ printf("OBJECT IDENTIFIER dissect_ber_object_identifier(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n",name,implicit_tag,offset,tvb_length_remaining(tvb,offset),tvb_get_guint8(tvb,offset),tvb_get_guint8(tvb,offset+1),tvb_get_guint8(tvb,offset+2)); }else{ printf("OBJECT IDENTIFIER dissect_ber_object_identifier(%s) entered\n",name); } } #endif if (value_string) { value_string[0] = '\0'; } if (!implicit_tag) { /* sanity check */ offset = get_ber_identifier(tvb, offset, &class, &pc, &tag); offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); eoffset = offset + len; if( (class!=BER_CLASS_UNI) ||(tag != BER_UNI_TAG_OID) ){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: Object Identifier expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return eoffset; } } else { len=tvb_length_remaining(tvb,offset); eoffset=offset+len; } value=0; for (i=0,strp=str; i200){ proto_tree_add_text(tree, tvb, offset, eoffset - offset, "BER Error: too long Object Identifier"); return offset; } /* 8.19.4 */ if (i == 0) { strp += sprintf(strp, "%d.%d", byte/40, byte%40); continue; } value = (value << 7) | (byte & 0x7F); if (byte & 0x80) { continue; } strp += sprintf(strp, ".%d", value); value = 0; } *strp = '\0'; if (hf_id != -1) { item=proto_tree_add_string(tree, hf_id, tvb, offset - len, len, str); /* see if we know the name of this oid */ if(item){ name=g_hash_table_lookup(oid_table, str); if(name){ proto_item_append_text(item, " (%s)", name); } } } if (value_string) { strcpy(value_string, str); } return eoffset; } static int dissect_ber_sq_of(gboolean implicit_tag, gint32 type, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_sequence_t *seq, gint hf_id, gint ett_id) { gint8 class; gboolean pc, ind, ind_field; gint32 tag; guint32 len; proto_tree *tree = parent_tree; proto_item *item = NULL; int cnt, hoffset, end_offset; header_field_info *hfi; #ifdef DEBUG_BER { char *name; header_field_info *hfinfo; if(hf_id>0){ hfinfo = proto_registrar_get_nth(hf_id); name=hfinfo->name; } else { name="unnamed"; } if(tvb_length_remaining(tvb,offset)>3){ printf("SQ OF dissect_ber_sq_of(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n",name,implicit_tag,offset,tvb_length_remaining(tvb,offset),tvb_get_guint8(tvb,offset),tvb_get_guint8(tvb,offset+1),tvb_get_guint8(tvb,offset+2)); }else{ printf("SQ OF dissect_ber_sq_of(%s) entered\n",name); } } #endif if(!implicit_tag){ /* first we must read the sequence header */ offset = dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, &ind); if(ind){ /* if the length is indefinite we dont really know (yet) where the * object ends so assume it spans the rest of the tvb for now. */ end_offset = tvb_length(tvb); } else { end_offset = offset + len; } /* sanity check: we only handle Constructed Universal Sequences */ if (!pc ||(!implicit_tag&&((class!=BER_CLASS_UNI) ||(tag!=type)))) { proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: %s Of expected but Class:%d PC:%d Tag:%d was unexpected", (type==BER_UNI_TAG_SEQUENCE)?"Set":"Sequence", class, pc, tag); return end_offset; } } else { len=tvb_length_remaining(tvb,offset); end_offset = offset + len; } /* count number of items */ cnt = 0; hoffset = offset; while (offset < end_offset){ guint32 len; if(ind){ /* this sequence of was of indefinite length, so check for EOC */ if((tvb_get_guint8(tvb, offset)==0)&&(tvb_get_guint8(tvb, offset+1)==0)){ break; } } /* read header and len for next field */ offset = get_ber_identifier(tvb, offset, NULL, NULL, NULL); offset = get_ber_length(tvb, offset, &len, NULL); offset += len; cnt++; } offset = hoffset; /* create subtree */ if (hf_id != -1) { hfi = proto_registrar_get_nth(hf_id); if(parent_tree){ if (hfi->type == FT_NONE) { item = proto_tree_add_item(parent_tree, hf_id, tvb, offset, len, FALSE); proto_item_append_text(item, ":"); } else { item = proto_tree_add_uint(parent_tree, hf_id, tvb, offset, len, cnt); proto_item_append_text(item, (cnt==1)?" item":" items"); } tree = proto_item_add_subtree(item, ett_id); } } /* loop over all entries until we reach the end of the sequence */ while (offset < end_offset){ gint8 class; gboolean pc; gint32 tag; guint32 len; int eoffset; int hoffset, count; if(ind){ /* this sequence of was of indefinite length, so check for EOC */ if((tvb_get_guint8(tvb, offset)==0)&&(tvb_get_guint8(tvb, offset+1)==0)){ if(show_internal_ber_fields){ proto_tree_add_text(tree, tvb, offset, 2, "EOC"); } return offset+2; } } hoffset = offset; /* read header and len for next field */ offset = get_ber_identifier(tvb, offset, &class, &pc, &tag); offset = get_ber_length(tvb, offset, &len, &ind_field); if(ind_field){ /* if the length is indefinite we dont really know (yet) where the * object ends so assume it spans the rest of the tvb for now. */ eoffset = tvb_length(tvb); } else { eoffset = offset + len; } /* verify that this one is the one we want */ if(seq->class!=BER_CLASS_ANY){ if ((seq->class!=class) ||(seq->tag!=tag) ){ if (!(seq->flags & BER_FLAGS_NOTCHKTAG)) { proto_tree_add_text(tree, tvb, offset, len, "BER Error: Wrong field in SQ OF"); offset = eoffset; continue; } } } if (!(seq->flags & BER_FLAGS_NOOWNTAG) && !(seq->flags & BER_FLAGS_IMPLTAG)) { /* dissect header and len for field */ hoffset = dissect_ber_identifier(pinfo, tree, tvb, hoffset, NULL, NULL, NULL); hoffset = dissect_ber_length(pinfo, tree, tvb, hoffset, NULL, NULL); } /* call the dissector for this field */ count=seq->func(pinfo, tree, tvb, hoffset)-hoffset; if(ind_field){ /* previous field was of indefinite length so we have * no choice but use whatever the subdissector told us * as size for the field. */ cnt++; offset = hoffset+count; } else { cnt++; offset = eoffset; } } /* if we didnt end up at exactly offset, then we ate too many bytes */ if (offset != end_offset) { proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: %s Of ate %d too many bytes", (type==BER_UNI_TAG_SEQUENCE)?"Set":"Sequence", offset-end_offset); } return end_offset; } int dissect_ber_sequence_of(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_sequence_t *seq, gint hf_id, gint ett_id) { return dissect_ber_sq_of(implicit_tag, BER_UNI_TAG_SEQUENCE, pinfo, parent_tree, tvb, offset, seq, hf_id, ett_id); } int dissect_ber_set_of(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_sequence_t *seq, gint hf_id, gint ett_id) { return dissect_ber_sq_of(implicit_tag, BER_UNI_TAG_SET, pinfo, parent_tree, tvb, offset, seq, hf_id, ett_id); } int dissect_ber_GeneralizedTime(gboolean implicit_tag, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id) { char str[32]; const guint8 *tmpstr; gint8 class; gboolean pc; gint32 tag; guint32 len; int end_offset; if(!implicit_tag){ offset=dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); end_offset=offset+len; /* sanity check. we only handle universal/generalized time */ if( (class!=BER_CLASS_UNI) ||(tag!=BER_UNI_TAG_GeneralizedTime)){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: GeneralizedTime expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return end_offset; end_offset=offset+len; } } else { len=tvb_length_remaining(tvb,offset); end_offset=offset+len; } tmpstr=tvb_get_ptr(tvb, offset, len); snprintf(str, 31, "%.4s-%.2s-%.2s %.2s:%.2s:%.2s (%.1s)", tmpstr, tmpstr+4, tmpstr+6, tmpstr+8, tmpstr+10, tmpstr+12, tmpstr+14); str[31]=0; /* just in case ... */ if(hf_id!=-1){ proto_tree_add_string(tree, hf_id, tvb, offset, len, str); } offset+=len; return offset; } /* 8.6 Encoding of a bitstring value */ int dissect_ber_bitstring(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const asn_namedbit *named_bits, gint hf_id, gint ett_id, tvbuff_t **out_tvb) { gint8 class; gboolean pc, ind; gint32 tag; guint32 len; guint8 pad=0, b0, b1, val; int end_offset; proto_item *item = NULL; proto_tree *tree = NULL; const asn_namedbit *nb; char *sep; gboolean term; if(!implicit_tag){ /* read header and len for the octet string */ offset = dissect_ber_identifier(pinfo, parent_tree, tvb, offset, &class, &pc, &tag); offset = dissect_ber_length(pinfo, parent_tree, tvb, offset, &len, &ind); end_offset = offset + len; /* sanity check: we only handle Universal BitSrings */ if (!implicit_tag) { if( (class!=BER_CLASS_UNI) ||(tag!=BER_UNI_TAG_BITSTRING) ){ proto_tree_add_text(parent_tree, tvb, offset-2, 2, "BER Error: BitString expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return end_offset; } } } else { pc=0; len=tvb_length_remaining(tvb,offset); end_offset=offset+len; } ber_last_created_item = NULL; if (pc) { /* constructed */ /* TO DO */ } else { /* primitive */ /* padding */ pad = tvb_get_guint8(tvb, offset); proto_tree_add_item(parent_tree, hf_ber_bitstring_padding, tvb, offset, 1, FALSE); offset++; len--; if ( hf_id != -1) { item = proto_tree_add_item(parent_tree, hf_id, tvb, offset, len, FALSE); ber_last_created_item = item; if (ett_id != -1) { tree = proto_item_add_subtree(item, ett_id); } } if (out_tvb) { if(len<=(guint32)tvb_length_remaining(tvb, offset)){ *out_tvb = tvb_new_subset(tvb, offset, len, len); } else { *out_tvb = tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset), tvb_length_remaining(tvb, offset)); } } } if (named_bits) { sep = " ("; term = FALSE; nb = named_bits; while (nb->p_id) { if (nb->bit < (8*len-pad)) { val = tvb_get_guint8(tvb, offset + nb->bit/8); val &= 0x80 >> (nb->bit%8); b0 = (nb->gb0 == -1) ? nb->bit/8 : ((guint32)nb->gb0)/8; b1 = (nb->gb1 == -1) ? nb->bit/8 : ((guint32)nb->gb1)/8; proto_tree_add_item(tree, *(nb->p_id), tvb, offset + b0, b1 - b0 + 1, FALSE); } else { /* 8.6.2.4 */ val = 0; proto_tree_add_boolean(tree, *(nb->p_id), tvb, offset + len, 0, 0x00); } if (val) { if (item && nb->tstr) proto_item_append_text(item, "%s%s", sep, nb->tstr); } else { if (item && nb->fstr) proto_item_append_text(item, "%s%s", sep, nb->fstr); } nb++; sep = ", "; term = TRUE; } if (term) proto_item_append_text(item, ")"); } return end_offset; } int dissect_ber_bitstring32(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, int **bit_fields, gint hf_id, gint ett_id, tvbuff_t **out_tvb) { tvbuff_t *tmp_tvb; proto_tree *tree; guint32 val; int **bf; header_field_info *hfi; char *sep; gboolean term; unsigned int i, tvb_len; offset = dissect_ber_bitstring(implicit_tag, pinfo, parent_tree, tvb, offset, NULL, hf_id, ett_id, &tmp_tvb); tree = proto_item_get_subtree(ber_last_created_item); if (bit_fields && tree) { /* tmp_tvb points to the actual bitstring (including any pad bits at the end. * note that this bitstring is not neccessarily always encoded as 4 bytes * so we have to read it byte by byte. */ val=0; tvb_len=tvb_length(tmp_tvb); for(i=0;i<4;i++){ val<<=8; if(ibitmask) { proto_item_append_text(ber_last_created_item, "%s%s", sep, hfi->name); sep = ", "; term = TRUE; } bf++; } if (term) proto_item_append_text(ber_last_created_item, ")"); } if (out_tvb) *out_tvb = tmp_tvb; return offset; } void proto_register_ber(void) { static hf_register_info hf[] = { { &hf_ber_id_class, { "Class", "ber.id.class", FT_UINT8, BASE_DEC, VALS(ber_class_codes), 0xc0, "Class of BER TLV Identifier", HFILL }}, { &hf_ber_bitstring_padding, { "Padding", "ber.bitstring.padding", FT_UINT8, BASE_DEC, NULL, 0x0, "Number of unsused bits in the last octet of the bitstring", HFILL }}, { &hf_ber_id_pc, { "P/C", "ber.id.pc", FT_BOOLEAN, 8, TFS(&ber_pc_codes), 0x20, "Primitive or Constructed BER encoding", HFILL }}, { &hf_ber_id_uni_tag, { "Tag", "ber.id.uni_tag", FT_UINT8, BASE_DEC, VALS(ber_uni_tag_codes), 0x1f, "Universal tag type", HFILL }}, { &hf_ber_id_tag, { "Tag", "ber.id.tag", FT_UINT32, BASE_DEC, NULL, 0, "Tag value for non-Universal classes", HFILL }}, { &hf_ber_length, { "Length", "ber.length", FT_UINT32, BASE_DEC, NULL, 0, "Length of contents", HFILL }}, { &hf_ber_unknown_OCTETSTRING, { "OCTETSTRING", "ber.unknown.OCTETSTRING", FT_BYTES, BASE_HEX, NULL, 0, "This is an unknown OCTETSTRING", HFILL }}, { &hf_ber_unknown_OID, { "OID", "ber.unknown.OID", FT_STRING, BASE_NONE, NULL, 0, "This is an unknown Object Identifier", HFILL }}, { &hf_ber_unknown_NumericString, { "NumericString", "ber.unknown.NumericString", FT_STRING, BASE_NONE, NULL, 0, "This is an unknown NumericString", HFILL }}, { &hf_ber_unknown_PrintableString, { "PrintableString", "ber.unknown.PrintableString", FT_STRING, BASE_NONE, NULL, 0, "This is an unknown PrintableString", HFILL }}, { &hf_ber_unknown_IA5String, { "IA5String", "ber.unknown.IA5String", FT_STRING, BASE_NONE, NULL, 0, "This is an unknown IA5String", HFILL }}, { &hf_ber_unknown_INTEGER, { "INTEGER", "ber.unknown.INTEGER", FT_UINT32, BASE_DEC, NULL, 0, "This is an unknown INTEGER", HFILL }}, { &hf_ber_unknown_ENUMERATED, { "ENUMERATED", "ber.unknown.ENUMERATED", FT_UINT32, BASE_DEC, NULL, 0, "This is an unknown ENUMERATED", HFILL }}, }; static gint *ett[] = { &ett_ber_octet_string, &ett_ber_unknown, &ett_ber_SEQUENCE, }; module_t *ber_module; proto_ber = proto_register_protocol("Basic Encoding Rules (ASN.1 X.690)", "BER", "ber"); proto_register_field_array(proto_ber, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); proto_set_cant_toggle(proto_ber); /* Register preferences */ ber_module = prefs_register_protocol(proto_ber, NULL); prefs_register_bool_preference(ber_module, "show_internals", "Show internal BER encapsulation tokens", "Whether the dissector should also display internal" " ASN.1 BER details such as Identifier and Length fields", &show_internal_ber_fields); ber_oid_dissector_table = register_dissector_table("ber.oid", "BER OID Dissectors", FT_STRING, BASE_NONE); oid_table=g_hash_table_new(g_str_hash, g_str_equal); } void proto_reg_handoff_ber(void) { }