summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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,