summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2013-04-18 19:22:24 +0000
committerGuy Harris <guy@alum.mit.edu>2013-04-18 19:22:24 +0000
commitc0c15029ed8d1234c95a71fdf7510a8bf0964fcb (patch)
tree585d3be4be77a93eb5fed23814d87ced468e9df9
parentd89d6c8314b8a5c407f86fe0f6e029b4da582b43 (diff)
downloadwireshark-c0c15029ed8d1234c95a71fdf7510a8bf0964fcb.tar.gz
Add to tvbuffs a "fragment length" field; if the tvbuff represents the
first fragment of a non-reassembled packet, and we know the length the packet would have if it were reassembled, this field holds the length of the fragment, and the "reported length" field shows the length the packet would have if it were reassembled, so going past the end of the fragment but staying within the length of the reassembled packet can be reported as "dissection would have worked if the packet had been reassembled" rather than "the packet is too short, so it was probably malformed". Add a FragmentBoundsError exception, thrown in the "dissection would have worked if the packet had been reassembled" case. Add a new tvb_new_subset_length_fragment() routine to create a new subset tvb with specified fragment and reported lengths. Use it in the CLNP dissector. Add some more sanity checks in the CLNP dissector. svn path=/trunk/; revision=48917
-rw-r--r--epan/dissectors/packet-clnp.c181
-rw-r--r--epan/exceptions.h77
-rw-r--r--epan/exntest.c8
-rw-r--r--epan/packet.c4
-rw-r--r--epan/show_exception.c11
-rw-r--r--epan/tvbtest.c30
-rw-r--r--epan/tvbuff-int.h6
-rw-r--r--epan/tvbuff.c115
-rw-r--r--epan/tvbuff.h13
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,