diff options
-rw-r--r-- | epan/dissectors/packet-clnp.c | 181 | ||||
-rw-r--r-- | epan/exceptions.h | 77 | ||||
-rw-r--r-- | epan/exntest.c | 8 | ||||
-rw-r--r-- | epan/packet.c | 4 | ||||
-rw-r--r-- | epan/show_exception.c | 11 | ||||
-rw-r--r-- | epan/tvbtest.c | 30 | ||||
-rw-r--r-- | epan/tvbuff-int.h | 6 | ||||
-rw-r--r-- | epan/tvbuff.c | 115 | ||||
-rw-r--r-- | epan/tvbuff.h | 13 |
9 files changed, 362 insertions, 83 deletions
diff --git a/epan/dissectors/packet-clnp.c b/epan/dissectors/packet-clnp.c index b6cd15b6dd..366a73f112 100644 --- a/epan/dissectors/packet-clnp.c +++ b/epan/dissectors/packet-clnp.c @@ -27,15 +27,18 @@ #include "config.h" #include <glib.h> + #include <epan/packet.h> #include <epan/prefs.h> #include <epan/reassemble.h> +#include <epan/expert.h> +#include <epan/nlpid.h> +#include <epan/ipproto.h> + #include "packet-osi.h" #include "packet-osi-options.h" #include "packet-isis.h" #include "packet-esis.h" -#include <epan/nlpid.h> -#include <epan/ipproto.h> void proto_register_clnp(void); void proto_reg_handoff_clnp(void); @@ -120,6 +123,9 @@ static dissector_handle_t data_handle; /* Fixed part */ +/* Length of fixed part */ +#define FIXED_PART_LEN 9 + #define CNF_TYPE 0x1f #define CNF_ERR_OK 0x20 #define CNF_MORE_SEGS 0x40 @@ -162,6 +168,8 @@ static const value_string npdu_type_vals[] = { /* Segmentation part */ +#define SEGMENTATION_PART_LEN 6 + struct clnp_segment { gushort cng_id; /* data unit identifier */ gushort cng_off; /* segment offset */ @@ -200,7 +208,7 @@ static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *clnp_tree = NULL; - proto_item *ti; + proto_item *ti, *ti_len = NULL, *ti_pdu_len = NULL, *ti_tot_len = NULL; guint8 cnf_proto_id; guint8 cnf_hdr_len; guint8 cnf_vers; @@ -212,6 +220,7 @@ dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) guint16 segment_length; guint16 du_id = 0; guint16 segment_offset = 0; + guint16 total_length; guint16 cnf_cksum; cksum_status_t cksum_status; int offset; @@ -253,15 +262,24 @@ dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) /* fixed part decoding */ cnf_hdr_len = tvb_get_guint8(tvb, P_CLNP_HDR_LEN); - opt_len = cnf_hdr_len; if (tree) { ti = proto_tree_add_item(tree, proto_clnp, tvb, 0, cnf_hdr_len, ENC_NA); clnp_tree = proto_item_add_subtree(ti, ett_clnp); proto_tree_add_uint(clnp_tree, hf_clnp_id, tvb, P_CLNP_PROTO_ID, 1, cnf_proto_id); - proto_tree_add_uint(clnp_tree, hf_clnp_length, tvb, P_CLNP_HDR_LEN, 1, - cnf_hdr_len); + ti_len = proto_tree_add_uint(clnp_tree, hf_clnp_length, tvb, P_CLNP_HDR_LEN, 1, + cnf_hdr_len); + } + if (cnf_hdr_len < FIXED_PART_LEN) { + /* Header length is less than the length of the fixed part of + the header. */ + expert_add_info_format(pinfo, ti_len, PI_MALFORMED, PI_ERROR, + "Header length value < minimum length %u", + FIXED_PART_LEN); + return; + } + if (tree) { proto_tree_add_uint(clnp_tree, hf_clnp_version, tvb, P_CLNP_VERS, 1, cnf_vers); cnf_ttl = tvb_get_guint8(tvb, P_CLNP_TTL); @@ -305,11 +323,20 @@ dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) } segment_length = tvb_get_ntohs(tvb, P_CLNP_SEGLEN); + if (tree) { + ti_pdu_len = proto_tree_add_uint(clnp_tree, hf_clnp_pdu_length, tvb, P_CLNP_SEGLEN, 2, + segment_length); + } + if (segment_length < cnf_hdr_len) { + /* Segment length is less than the header length. */ + expert_add_info_format(pinfo, ti_pdu_len, PI_MALFORMED, PI_ERROR, + "PDU length < header length %u", cnf_hdr_len); + return; + } + total_length = segment_length; cnf_cksum = tvb_get_ntohs(tvb, P_CLNP_CKSUM); cksum_status = calc_checksum(tvb, 0, cnf_hdr_len, cnf_cksum); if (tree) { - proto_tree_add_uint(clnp_tree, hf_clnp_pdu_length, tvb, P_CLNP_SEGLEN, 2, - segment_length); switch (cksum_status) { default: @@ -346,80 +373,136 @@ dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) cnf_cksum); break; } - opt_len -= 9; /* Fixed part of Header */ } /* tree */ + opt_len = cnf_hdr_len; + opt_len -= FIXED_PART_LEN; /* Fixed part of Header */ + /* address part */ offset = P_CLNP_ADDRESS_PART; + if (opt_len < 1) { + /* Header length is less than the minimum value in CLNP, + including the destination address length. */ + expert_add_info_format(pinfo, ti_len, PI_MALFORMED, PI_ERROR, + "Header length value < %u", + FIXED_PART_LEN + 1); + return; + } dst_len = tvb_get_guint8(tvb, offset); - dst_addr = tvb_get_ptr(tvb, offset + 1, dst_len); - nsel = tvb_get_guint8(tvb, offset + dst_len); - src_len = tvb_get_guint8(tvb, offset + dst_len + 1); - src_addr = tvb_get_ptr(tvb, offset + dst_len + 2, src_len); - if (tree) { proto_tree_add_uint(clnp_tree, hf_clnp_dest_length, tvb, offset, 1, dst_len); - proto_tree_add_bytes_format(clnp_tree, hf_clnp_dest, tvb, offset + 1 , dst_len, + } + offset += 1; + opt_len -= 1; + + if (opt_len < dst_len) { + /* Header length is less than the minimum value, + including the destination address length and the + destination address. */ + expert_add_info_format(pinfo, ti_len, PI_MALFORMED, PI_ERROR, + "Header length value < %u", + FIXED_PART_LEN + 1 + dst_len); + return; + } + dst_addr = tvb_get_ptr(tvb, offset, dst_len); + nsel = tvb_get_guint8(tvb, offset + dst_len - 1); + SET_ADDRESS(&pinfo->net_dst, AT_OSI, dst_len, dst_addr); + SET_ADDRESS(&pinfo->dst, AT_OSI, dst_len, dst_addr); + if (tree) { + proto_tree_add_bytes_format(clnp_tree, hf_clnp_dest, tvb, offset, dst_len, dst_addr, " DA : %s", print_nsap_net(dst_addr, dst_len)); + } + offset += dst_len; + opt_len -= dst_len; + + if (opt_len < 1) { + /* Header length is less than the minimum value, + including the destination address length, the + destination address, and the source address length. */ + expert_add_info_format(pinfo, ti_len, PI_MALFORMED, PI_ERROR, + "Header length value < %u", + FIXED_PART_LEN + 1 + dst_len + 1); + return; + } + src_len = tvb_get_guint8(tvb, offset); + if (tree) { proto_tree_add_uint(clnp_tree, hf_clnp_src_length, tvb, - offset + 1 + dst_len, 1, src_len); + offset, 1, src_len); + } + offset += 1; + opt_len -= 1; + + if (opt_len < src_len) { + /* Header length is less than the minimum value, + including the destination address length, the + destination address, the source address length, + and the source address. */ + expert_add_info_format(pinfo, ti_len, PI_MALFORMED, PI_ERROR, + "Header length value < %u", + FIXED_PART_LEN + 1 + dst_len + 1 + src_len); + return; + } + src_addr = tvb_get_ptr(tvb, offset, src_len); + SET_ADDRESS(&pinfo->net_src, AT_OSI, src_len, src_addr); + SET_ADDRESS(&pinfo->src, AT_OSI, src_len, src_addr); + if (tree) { proto_tree_add_bytes_format(clnp_tree, hf_clnp_src, tvb, - offset + dst_len + 2, src_len, + offset, src_len, src_addr, " SA : %s", print_nsap_net(src_addr, src_len)); - opt_len -= dst_len + src_len +2; } - - SET_ADDRESS(&pinfo->net_src, AT_OSI, src_len, src_addr); - SET_ADDRESS(&pinfo->src, AT_OSI, src_len, src_addr); - SET_ADDRESS(&pinfo->net_dst, AT_OSI, dst_len, dst_addr); - SET_ADDRESS(&pinfo->dst, AT_OSI, dst_len, dst_addr); + offset += src_len; + opt_len -= src_len; /* Segmentation Part */ - offset += dst_len + src_len + 2; - if (cnf_type & CNF_SEG_OK) { -#if 0 - struct clnp_segment seg; /* XXX - not used */ - tvb_memcpy(tvb, (guint8 *)&seg, offset, sizeof(seg)); /* XXX - not used */ -#endif - - segment_offset = tvb_get_ntohs(tvb, offset + 2); + if (opt_len < SEGMENTATION_PART_LEN) { + /* Header length is less than the minimum value, + including the destination address length, the + destination address, the source address length, + the source address, and the segmentation part. */ + expert_add_info_format(pinfo, ti_len, PI_MALFORMED, PI_ERROR, + "Header length value < %u", + FIXED_PART_LEN + 1 + dst_len + 1 + SEGMENTATION_PART_LEN); + return; + } du_id = tvb_get_ntohs(tvb, offset); if (tree) { proto_tree_add_text(clnp_tree, tvb, offset, 2, "Data unit identifier: %06u", du_id); + } + segment_offset = tvb_get_ntohs(tvb, offset + 2); + if (tree) { proto_tree_add_text(clnp_tree, tvb, offset + 2 , 2, "Segment offset : %6u", segment_offset); - proto_tree_add_text(clnp_tree, tvb, offset + 4 , 2, - "Total length : %6u", - tvb_get_ntohs(tvb, offset + 4)); } - - offset += 6; - opt_len -= 6; + total_length = tvb_get_ntohs(tvb, offset + 4); + if (tree) { + ti_tot_len = proto_tree_add_text(clnp_tree, tvb, offset + 4 , 2, + "Total length : %6u", + total_length); + } + if (total_length < segment_length) { + /* Reassembled length is less than the length of this segment. */ + expert_add_info_format(pinfo, ti_pdu_len, PI_MALFORMED, PI_ERROR, + "Total length < segment length %u", segment_length); + return; + } + offset += SEGMENTATION_PART_LEN; + opt_len -= SEGMENTATION_PART_LEN; } if (tree) { - /* To do : decode options */ -#if 0 - proto_tree_add_text(clnp_tree, tvb, offset, - cnf_hdr_len - offset, - "Options/Data: <not shown>"); -#endif -/* QUICK HACK Option Len:= PDU_Hd_length-( FixedPart+AddresPart+SegmentPart )*/ - - dissect_osi_options( opt_len, - tvb, offset, clnp_tree ); + dissect_osi_options(opt_len, tvb, offset, clnp_tree); } offset += opt_len; @@ -454,7 +537,9 @@ dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) /* First segment, or not segmented. Dissect what we have here. */ /* Get a tvbuff for the payload. */ - next_tvb = tvb_new_subset_remaining(tvb, offset); + next_tvb = tvb_new_subset_length_fragment(tvb, offset, + segment_length - cnf_hdr_len, + total_length - cnf_hdr_len); /* * If this is the first segment, but not the only segment, diff --git a/epan/exceptions.h b/epan/exceptions.h index 938115e622..601206e986 100644 --- a/epan/exceptions.h +++ b/epan/exceptions.h @@ -52,20 +52,19 @@ code constructed the packet and put it on the wire didn't put enough data into it. It is therefore currently reported as a "Malformed packet". - However, it also happens in some cases where the packet was fragmented - and the fragments weren't reassembled. We need to add another length - field to a tvbuff, so that "length of the packet from the link layer" - and "length of the packet were it fully reassembled" are different, - and going past the first of those without going past the second would - throw a different exception, which would be reported as an "Unreassembled - packet" rather than a "Malformed packet". **/ #define ReportedBoundsError 2 /** + Index is beyond fragment length but not reported length. + This means that the packet wasn't reassembled. +**/ +#define FragmentBoundsError 3 + +/** During dfilter parsing **/ -#define TypeError 3 +#define TypeError 4 /** A bug was detected in a dissector. @@ -76,7 +75,7 @@ Instead, use the DISSECTOR_ASSERT(), etc. macros in epan/proto.h. **/ -#define DissectorError 4 +#define DissectorError 5 /** Index is out of range. @@ -87,13 +86,13 @@ to get the "size" of lun list back after which the initiator will reissue the command with an allocation_length that is big enough. **/ -#define ScsiBoundsError 5 +#define ScsiBoundsError 6 /** Running out of memory. A dissector tried to allocate memory but that failed. **/ -#define OutOfMemoryError 6 +#define OutOfMemoryError 7 /** The reassembly state machine was passed a bad fragment offset, @@ -102,7 +101,7 @@ contains a bad fragment offset, the dissector shouldn't have to figure that out by itself since that's what the reassembly machine is for. **/ -#define ReassemblyError 7 +#define ReassemblyError 8 /* * Catch errors that, if you're calling a subdissector and catching @@ -110,9 +109,14 @@ * stuff after the subdissector returns or fails, mean it makes * sense to continue dissecting: * - * BoundsError indicates a configuration problem; there's no point in + * BoundsError indicates a configuration problem (the capture was + * set up to throw away data, and it did); there's no point in * trying to dissect any more data, as there's no more data to dissect. * + * FragmentBoundsError indicates a configuration problem (reassembly + * wasn't enabled or couldn't be done); there's no point in trying + * to dissect any more data, as there's no more data to dissect. + * * OutOfMemoryError indicates what its name suggests; there's no point * in trying to dissect any more data, as you're probably not going to * have any more memory to use when dissecting them. @@ -129,7 +133,8 @@ * Catch all bounds-checking errors. */ #define CATCH_BOUNDS_ERRORS \ - CATCH3(BoundsError, ReportedBoundsError, ScsiBoundsError) + CATCH4(BoundsError, FragmentBoundsError, ReportedBoundsError, \ + ScsiBoundsError) /* * Catch all bounds-checking errors, and catch dissector bugs. @@ -137,7 +142,8 @@ * go all the way to the top level and get reported immediately. */ #define CATCH_BOUNDS_AND_DISSECTOR_ERRORS \ - CATCH5(BoundsError, ReportedBoundsError, ScsiBoundsError, DissectorError, ReassemblyError) + CATCH6(BoundsError, FragmentBoundsError, ReportedBoundsError, \ + ScsiBoundsError, DissectorError, ReassemblyError) /* Usage: * @@ -153,7 +159,31 @@ * code; * } * - * CATCH_END_OF_DATA_ERROR { + * CATCH3(exception1, exception2, exception3) { + * code; + * } + * + * CATCH4(exception1, exception2, exception3, exception4) { + * code; + * } + * + * CATCH5(exception1, exception2, exception3, exception4, exception5) { + * code; + * } + * + * CATCH6(exception1, exception2, exception3, exception4, exception5, exception6) { + * code; + * } + * + * CATCH_NONFATAL_ERRORS { + * code; + * } + * + * CATCH_BOUNDS_ERRORS { + * code; + * } + * + * CATCH_BOUNDS_AND_DISSECTOR_ERRORS { * code; * } * @@ -191,6 +221,10 @@ * caught = TRUE; * <CATCH2(3,4) code> * } + * if (!caught && (x == 5 || x == 6 || x == 7)) { + * caught = TRUE; + * <CATCH3(5,6,7) code> + * } * if (!caught && x != 0) { * caught = TRUE; * <CATCH_ALL code> @@ -306,6 +340,17 @@ (except_state|=EXCEPT_CAUGHT)) \ /* user's code goes here */ +#define CATCH6(u,v,w,x,y,z) \ + if (except_state == 0 && exc != 0 && \ + (exc->except_id.except_code == (u) || \ + exc->except_id.except_code == (v) || \ + exc->except_id.except_code == (w) || \ + exc->except_id.except_code == (x) || \ + exc->except_id.except_code == (y) || \ + exc->except_id.except_code == (z)) && \ + (except_state|=EXCEPT_CAUGHT)) \ + /* user's code goes here */ + #define CATCH_ALL \ if (except_state == 0 && exc != 0 && \ (except_state|=EXCEPT_CAUGHT)) \ diff --git a/epan/exntest.c b/epan/exntest.c index 1bb6a33ae9..f8b77da8f5 100644 --- a/epan/exntest.c +++ b/epan/exntest.c @@ -40,6 +40,10 @@ run_tests(void) CATCH(BoundsError) { ex_thrown++; } + CATCH(FragmentBoundsError) { + printf("01: Caught wrong exception: FragmentBoundsError\n"); + failed = TRUE; + } CATCH(ReportedBoundsError) { printf("01: Caught wrong exception: ReportedBoundsError\n"); failed = TRUE; @@ -72,6 +76,10 @@ run_tests(void) printf("02: Caught wrong exception: BoundsError\n"); failed = TRUE; } + CATCH(FragmentBoundsError) { + printf("02: Caught wrong exception: FragmentBoundsError\n"); + failed = TRUE; + } CATCH(ReportedBoundsError) { printf("02: Caught wrong exception: ReportedBoundsError\n"); failed = TRUE; diff --git a/epan/packet.c b/epan/packet.c index bf511ffb02..6197f7a966 100644 --- a/epan/packet.c +++ b/epan/packet.c @@ -395,7 +395,7 @@ dissect_packet(epan_dissect_t *edt, struct wtap_pkthdr *phdr, CATCH(BoundsError) { g_assert_not_reached(); } - CATCH(ReportedBoundsError) { + CATCH2(FragmentBoundsError, ReportedBoundsError) { if(proto_malformed != -1){ proto_tree_add_protocol_format(edt->tree, proto_malformed, edt->tvb, 0, 0, "[Malformed Frame: Packet Length]" ); @@ -644,7 +644,7 @@ call_dissector_work_error(dissector_handle_t handle, tvbuff_t *tvb, */ RETHROW; } - CATCH(ReportedBoundsError) { + CATCH2(FragmentBoundsError, ReportedBoundsError) { /* * "ret" wasn't set because an exception was thrown * before "call_dissector_through_handle()" returned. diff --git a/epan/show_exception.c b/epan/show_exception.c index ddc41bff85..641b86f786 100644 --- a/epan/show_exception.c +++ b/epan/show_exception.c @@ -84,6 +84,17 @@ show_exception(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, /* expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR, "Packet size limited");*/ break; + case FragmentBoundsError: + col_append_fstr(pinfo->cinfo, COL_INFO, "[Unreassembled Packet%s]", pinfo->noreassembly_reason); + /*item =*/ proto_tree_add_protocol_format(tree, proto_unreassembled, + tvb, 0, 0, "[Unreassembled Packet%s: %s]", + pinfo->noreassembly_reason, pinfo->current_proto); + /* Don't record FragmentBoundsError exceptions as expert events - they merely + * reflect dissection done with reassembly turned off + * (any case where it's caused by something else is a bug). */ + /* expert_add_info_format(pinfo, item, PI_REASSEMBLE, PI_WARN, "Unreassembled Packet (Exception occured)");*/ + break; + case ReportedBoundsError: show_reported_bounds_error(tvb, pinfo, tree); break; diff --git a/epan/tvbtest.c b/epan/tvbtest.c index 94b91d2dc3..2aa6b187f1 100644 --- a/epan/tvbtest.c +++ b/epan/tvbtest.c @@ -73,9 +73,15 @@ test(tvbuff_t *tvb, gchar* name, CATCH(BoundsError) { ex_thrown = TRUE; } + CATCH(FragmentBoundsError) { + printf("02: Caught wrong exception: FragmentBoundsError\n"); + } CATCH(ReportedBoundsError) { printf("02: Caught wrong exception: ReportedBoundsError\n"); } + CATCH_ALL { + printf("02: Caught wrong exception: %lu, exc->except_id.except_code\n"); + } ENDTRY; if (!ex_thrown) { @@ -94,9 +100,15 @@ test(tvbuff_t *tvb, gchar* name, CATCH(BoundsError) { printf("03: Caught wrong exception: BoundsError\n"); } + CATCH(FragmentBoundsError) { + printf("03: Caught wrong exception: FragmentBoundsError\n"); + } CATCH(ReportedBoundsError) { ex_thrown = TRUE; } + CATCH_ALL { + printf("02: Caught wrong exception: %lu, exc->except_id.except_code\n"); + } ENDTRY; if (!ex_thrown) { @@ -114,9 +126,15 @@ test(tvbuff_t *tvb, gchar* name, CATCH(BoundsError) { ex_thrown = TRUE; } + CATCH(FragmentBoundsError) { + printf("04: Caught wrong exception: FragmentBoundsError\n"); + } CATCH(ReportedBoundsError) { printf("04: Caught wrong exception: ReportedBoundsError\n"); } + CATCH_ALL { + printf("02: Caught wrong exception: %lu, exc->except_id.except_code\n"); + } ENDTRY; if (!ex_thrown) { @@ -134,9 +152,15 @@ test(tvbuff_t *tvb, gchar* name, CATCH(BoundsError) { ex_thrown = TRUE; } + CATCH(FragmentBoundsError) { + printf("05: Caught wrong exception: FragmentBoundsError\n"); + } CATCH(ReportedBoundsError) { printf("05: Caught wrong exception: ReportedBoundsError\n"); } + CATCH_ALL { + printf("02: Caught wrong exception: %lu, exc->except_id.except_code\n"); + } ENDTRY; if (ex_thrown) { @@ -154,9 +178,15 @@ test(tvbuff_t *tvb, gchar* name, CATCH(BoundsError) { ex_thrown = TRUE; } + CATCH(FragmentBoundsError) { + printf("06: Caught wrong exception: FragmentBoundsError\n"); + } CATCH(ReportedBoundsError) { printf("06: Caught wrong exception: ReportedBoundsError\n"); } + CATCH_ALL { + printf("02: Caught wrong exception: %lu, exc->except_id.except_code\n"); + } ENDTRY; if (ex_thrown) { diff --git a/epan/tvbuff-int.h b/epan/tvbuff-int.h index 1dd1be90c6..33057bdd87 100644 --- a/epan/tvbuff-int.h +++ b/epan/tvbuff-int.h @@ -79,6 +79,12 @@ struct tvbuff { /** Reported length. */ guint reported_length; + /* If this tvbuff represents the first fragment of a larger packet + * that was not reassembled, this is the length of the fragment + * ("reported_length" will be the length of the full packet). + */ + guint fragment_length; + /* Offset from beginning of first TVBUFF_REAL. */ gint raw_offset; diff --git a/epan/tvbuff.c b/epan/tvbuff.c index 2f292a6ed1..37c14bbc60 100644 --- a/epan/tvbuff.c +++ b/epan/tvbuff.c @@ -74,6 +74,7 @@ tvb_init(tvbuff_t *tvb, const tvbuff_type type) tvb->initialized = FALSE; tvb->length = 0; tvb->reported_length = 0; + tvb->fragment_length = 0; tvb->free_cb = NULL; tvb->real_data = NULL; tvb->raw_offset = -1; @@ -226,6 +227,7 @@ tvb_new_real_data(const guint8* data, const guint length, const gint reported_le tvb->real_data = data; tvb->length = length; tvb->reported_length = reported_length; + tvb->fragment_length = reported_length; tvb->initialized = TRUE; /* @@ -277,6 +279,12 @@ compute_offset_length(const tvbuff_t *tvb, } return FALSE; } + else if ((guint) offset > tvb->fragment_length) { + if (exception) { + *exception = FragmentBoundsError; + } + return FALSE; + } else if ((guint) offset > tvb->length) { if (exception) { *exception = BoundsError; @@ -295,6 +303,12 @@ compute_offset_length(const tvbuff_t *tvb, } return FALSE; } + else if ((guint) -offset > tvb->fragment_length) { + if (exception) { + *exception = FragmentBoundsError; + } + return FALSE; + } else if ((guint) -offset > tvb->length) { if (exception) { *exception = BoundsError; @@ -359,11 +373,17 @@ check_offset_length_no_exception(const tvbuff_t *tvb, if (end_offset <= tvb->length) { return TRUE; } - else if (end_offset <= tvb->reported_length) { + else if (end_offset <= tvb->fragment_length) { if (exception) { *exception = BoundsError; } } + else if (end_offset <= tvb->reported_length) { + if (exception) { + *exception = FragmentBoundsError; + } + return FALSE; + } else { if (exception) { *exception = ReportedBoundsError; @@ -391,8 +411,9 @@ check_offset_length(const tvbuff_t *tvb, } static tvbuff_t * -tvb_new_with_subset(tvbuff_t *backing, const gint reported_length, - const guint subset_tvb_offset, const guint subset_tvb_length) +tvb_new_with_subset(tvbuff_t *backing, const gint fragment_length, + const gint reported_length, const guint subset_tvb_offset, + const guint subset_tvb_length) { tvbuff_t *tvb = tvb_new(TVBUFF_SUBSET); @@ -403,11 +424,14 @@ tvb_new_with_subset(tvbuff_t *backing, const gint reported_length, tvb->length = tvb->tvbuffs.subset.length; if (reported_length == -1) { + tvb->fragment_length = backing->fragment_length - tvb->tvbuffs.subset.offset; tvb->reported_length = backing->reported_length - tvb->tvbuffs.subset.offset; } else { + tvb->fragment_length = fragment_length; tvb->reported_length = reported_length; } + tvb->initialized = TRUE; add_to_chain(backing, tvb); @@ -435,7 +459,7 @@ tvb_new_subset(tvbuff_t *backing, const gint backing_offset, const gint backing_ &subset_tvb_offset, &subset_tvb_length); - tvb = tvb_new_with_subset(backing, reported_length, + tvb = tvb_new_with_subset(backing, reported_length, reported_length, subset_tvb_offset, subset_tvb_length); /* @@ -471,7 +495,46 @@ tvb_new_subset_length(tvbuff_t *backing, const gint backing_offset, const gint b &subset_tvb_offset, &subset_tvb_length); - tvb = tvb_new_with_subset(backing, backing_length, + tvb = tvb_new_with_subset(backing, backing_length, backing_length, + subset_tvb_offset, subset_tvb_length); + + /* + * The top-level data source of this tvbuff is the top-level + * data source of its parent. + */ + tvb->ds_tvb = backing->ds_tvb; + + return tvb; +} + +tvbuff_t * +tvb_new_subset_length_fragment(tvbuff_t *backing, const gint backing_offset, + const gint fragment_length, const gint reported_length) +{ + gint captured_length; + tvbuff_t *tvb; + guint subset_tvb_offset; + guint subset_tvb_length; + + DISSECTOR_ASSERT(backing && backing->initialized); + + THROW_ON(fragment_length < 0, ReportedBoundsError); + THROW_ON(reported_length < 0, ReportedBoundsError); + THROW_ON(reported_length < fragment_length, ReportedBoundsError); + + /* + * Give the next dissector only captured_length bytes. + */ + captured_length = tvb_length_remaining(backing, backing_offset); + THROW_ON(captured_length < 0, BoundsError); + if (captured_length > fragment_length) + captured_length = fragment_length; + + check_offset_length(backing, backing_offset, captured_length, + &subset_tvb_offset, + &subset_tvb_length); + + tvb = tvb_new_with_subset(backing, fragment_length, reported_length, subset_tvb_offset, subset_tvb_length); /* @@ -494,8 +557,8 @@ tvb_new_subset_remaining(tvbuff_t *backing, const gint backing_offset) &subset_tvb_offset, &subset_tvb_length); - tvb = tvb_new_with_subset(backing, -1 /* reported_length */, - subset_tvb_offset, subset_tvb_length); + tvb = tvb_new_with_subset(backing, -1 /* fragment_length */, + -1 /* reported_length */, subset_tvb_offset, subset_tvb_length); /* * The top-level data source of this tvbuff is the top-level @@ -634,6 +697,7 @@ tvb_composite_finalize(tvbuff_t *tvb) DISSECTOR_ASSERT(tvb && !tvb->initialized); DISSECTOR_ASSERT(tvb->type == TVBUFF_COMPOSITE); DISSECTOR_ASSERT(tvb->length == 0); + DISSECTOR_ASSERT(tvb->fragment_length == 0); DISSECTOR_ASSERT(tvb->reported_length == 0); composite = &tvb->tvbuffs.composite; @@ -653,6 +717,7 @@ tvb_composite_finalize(tvbuff_t *tvb) member_tvb = (tvbuff_t *)slist->data; composite->start_offsets[i] = tvb->length; tvb->length += member_tvb->length; + tvb->fragment_length += member_tvb->fragment_length; tvb->reported_length += member_tvb->reported_length; composite->end_offsets[i] = tvb->length - 1; i++; @@ -705,6 +770,8 @@ tvb_ensure_length_remaining(const tvbuff_t *tvb, const gint offset) */ if (abs_offset >= tvb->reported_length) THROW(ReportedBoundsError); + else if (abs_offset >= tvb->fragment_length) + THROW(FragmentBoundsError); else THROW(BoundsError); } @@ -805,8 +872,10 @@ tvb_reported_length_remaining(const tvbuff_t *tvb, const gint offset) /* Set the reported length of a tvbuff to a given value; used for protocols * whose headers contain an explicit length and where the calling * dissector's payload may include padding as well as the packet for - * this protocol. - * Also adjusts the data length. */ + * this protocol, or where a packet may be a fragment of a larger packet + * and the length of the larger packet is known. + * + * Also adjusts the data length and fragment length. */ void tvb_set_reported_length(tvbuff_t *tvb, const guint reported_length) { @@ -818,6 +887,8 @@ tvb_set_reported_length(tvbuff_t *tvb, const guint reported_length) tvb->reported_length = reported_length; if (reported_length < tvb->length) tvb->length = reported_length; + if (reported_length < tvb->fragment_length) + tvb->fragment_length = reported_length; } @@ -984,6 +1055,9 @@ fast_ensure_contiguous(tvbuff_t *tvb, const gint offset, const guint length) if (end_offset > tvb->reported_length) { THROW(ReportedBoundsError); } + if (end_offset > tvb->fragment_length) { + THROW(FragmentBoundsError); + } THROW(BoundsError); /* not reached */ return NULL; @@ -2017,15 +2091,24 @@ tvb_strsize(tvbuff_t *tvb, const gint offset) * OK, we hit the end of the tvbuff, so we should throw * an exception. * - * Did we hit the end of the captured data, or the end - * of the actual data? If there's less captured data - * than actual data, we presumably hit the end of the - * captured data, otherwise we hit the end of the actual - * data. + * Did we hit the end of the captured data, the end of + * the fragment data, or the end of the actual data? + * + * If there's less captured data than fragment data, + * we presumably hit the end of the captured data. + * + * Otherwise, if there's less fragment data than actual + * data, we presumably hit the end of the fragment data. + * + * Otherwise we hit the end of the actual data. */ - if (tvb_length(tvb) < tvb_reported_length(tvb)) { + if (tvb->length < tvb->fragment_length) { THROW(BoundsError); - } else { + } + else if (tvb->fragment_length < tvb->reported_length) { + THROW(FragmentBoundsError); + } + else { THROW(ReportedBoundsError); } } diff --git a/epan/tvbuff.h b/epan/tvbuff.h index 7a165dc8f9..21abf5ecf7 100644 --- a/epan/tvbuff.h +++ b/epan/tvbuff.h @@ -183,11 +183,22 @@ WS_DLL_PUBLIC tvbuff_t* tvb_new_subset(tvbuff_t* backing, /* * Similar to tvb_new_subset() but with captured length calculated - * to fit within min(reported length, backing_length). + * to fit within the existing captured length and the specified + * backing length (which is used as both the fragment and reported + * length). * Can throw ReportedBoundsError. */ WS_DLL_PUBLIC tvbuff_t* tvb_new_subset_length(tvbuff_t *backing, const gint backing_offset, const gint backing_length); +/** Similar to tvb_new_subset() but with fragment and reported length + * set as specified and captured length calculated to fit within + * the existing captured length and the specified fragment and + * reported lengths. + * Can throw ReportedBoundsError. */ +extern tvbuff_t* tvb_new_subset_length_fragment(tvbuff_t *backing, + const gint backing_offset, const gint fragment_length, + const gint reported_length); + /** Similar to tvb_new_subset() but with backing_length and reported_length set to -1. * Can throw ReportedBoundsError. */ WS_DLL_PUBLIC tvbuff_t* tvb_new_subset_remaining(tvbuff_t* backing, |