summaryrefslogtreecommitdiffstats
path: root/epan/tvbuff.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/tvbuff.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/tvbuff.c')
-rw-r--r--epan/tvbuff.c4781
1 files changed, 4781 insertions, 0 deletions
diff --git a/epan/tvbuff.c b/epan/tvbuff.c
new file mode 100644
index 00000000..93059a5a
--- /dev/null
+++ b/epan/tvbuff.c
@@ -0,0 +1,4781 @@
+/* tvbuff.c
+ *
+ * Testy, Virtual(-izable) Buffer of guint8*'s
+ *
+ * "Testy" -- the buffer gets mad when an attempt to access data
+ * beyond the bounds of the buffer. An exception is thrown.
+ *
+ * "Virtual" -- the buffer can have its own data, can use a subset of
+ * the data of a backing tvbuff, or can be a composite of
+ * other tvbuffs.
+ *
+ * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Code to convert IEEE floating point formats to native floating point
+ * derived from code Copyright (c) Ashok Narayanan, 2000
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "wsutil/pint.h"
+#include "wsutil/sign_ext.h"
+#include "wsutil/strtoi.h"
+#include "wsutil/unicode-utils.h"
+#include "wsutil/nstime.h"
+#include "wsutil/time_util.h"
+#include <wsutil/ws_assert.h>
+#include "tvbuff.h"
+#include "tvbuff-int.h"
+#include "strutil.h"
+#include "to_str.h"
+#include "charsets.h"
+#include "proto.h" /* XXX - only used for DISSECTOR_ASSERT, probably a new header file? */
+#include "exceptions.h"
+
+#include <time.h>
+
+static guint64
+_tvb_get_bits64(tvbuff_t *tvb, guint bit_offset, const gint total_no_of_bits);
+
+static guint64
+_tvb_get_bits64_le(tvbuff_t *tvb, guint bit_offset, const gint total_no_of_bits);
+
+static inline gint
+_tvb_captured_length_remaining(const tvbuff_t *tvb, const gint offset);
+
+static inline const guint8*
+ensure_contiguous(tvbuff_t *tvb, const gint offset, const gint length);
+
+static inline guint8 *
+tvb_get_raw_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length);
+
+tvbuff_t *
+tvb_new(const struct tvb_ops *ops)
+{
+ tvbuff_t *tvb;
+ gsize size = ops->tvb_size;
+
+ ws_assert(size >= sizeof(*tvb));
+
+ tvb = (tvbuff_t *) g_slice_alloc(size);
+
+ tvb->next = NULL;
+ tvb->ops = ops;
+ tvb->initialized = FALSE;
+ tvb->flags = 0;
+ tvb->length = 0;
+ tvb->reported_length = 0;
+ tvb->contained_length = 0;
+ tvb->real_data = NULL;
+ tvb->raw_offset = -1;
+ tvb->ds_tvb = NULL;
+
+ return tvb;
+}
+
+static void
+tvb_free_internal(tvbuff_t *tvb)
+{
+ gsize size;
+
+ DISSECTOR_ASSERT(tvb);
+
+ if (tvb->ops->tvb_free)
+ tvb->ops->tvb_free(tvb);
+
+ size = tvb->ops->tvb_size;
+
+ g_slice_free1(size, tvb);
+}
+
+/* XXX: just call tvb_free_chain();
+ * Not removed so that existing dissectors using tvb_free() need not be changed.
+ * I'd argue that existing calls to tvb_free() should have actually beeen
+ * calls to tvb_free_chain() although the calls were OK as long as no
+ * subsets, etc had been created on the tvb. */
+void
+tvb_free(tvbuff_t *tvb)
+{
+ tvb_free_chain(tvb);
+}
+
+void
+tvb_free_chain(tvbuff_t *tvb)
+{
+ tvbuff_t *next_tvb;
+ DISSECTOR_ASSERT(tvb);
+ while (tvb) {
+ next_tvb = tvb->next;
+ tvb_free_internal(tvb);
+ tvb = next_tvb;
+ }
+}
+
+tvbuff_t *
+tvb_new_chain(tvbuff_t *parent, tvbuff_t *backing)
+{
+ tvbuff_t *tvb = tvb_new_proxy(backing);
+
+ tvb_add_to_chain(parent, tvb);
+ return tvb;
+}
+
+void
+tvb_add_to_chain(tvbuff_t *parent, tvbuff_t *child)
+{
+ tvbuff_t *tmp;
+
+ DISSECTOR_ASSERT(parent);
+ DISSECTOR_ASSERT(child);
+
+ while (child) {
+ tmp = child;
+ child = child->next;
+
+ tmp->next = parent->next;
+ parent->next = tmp;
+ }
+}
+
+/*
+ * Check whether that offset goes more than one byte past the
+ * end of the buffer.
+ *
+ * If not, return 0; otherwise, return exception
+ */
+static inline int
+validate_offset(const tvbuff_t *tvb, const guint abs_offset)
+{
+ if (G_LIKELY(abs_offset <= tvb->length)) {
+ /* It's OK. */
+ return 0;
+ }
+
+ /*
+ * It's not OK, but why? Which boundaries is it
+ * past?
+ */
+ if (abs_offset <= tvb->contained_length) {
+ /*
+ * It's past the captured length, but not past
+ * the reported end of any parent tvbuffs from
+ * which this is constructed, or the reported
+ * end of this tvbuff, so it's out of bounds
+ * solely because we're past the end of the
+ * captured data.
+ */
+ return BoundsError;
+ }
+
+ /*
+ * There's some actual packet boundary, not just the
+ * artificial boundary imposed by packet slicing, that
+ * we're past.
+ */
+
+ if (tvb->flags & TVBUFF_FRAGMENT) {
+ /*
+ * This tvbuff is the first fragment of a larger
+ * packet that hasn't been reassembled, so we
+ * assume that's the source of the problem - if
+ * we'd reassembled the packet, we wouldn't have
+ * gone past the end.
+ *
+ * That might not be true, but for at least
+ * some forms of reassembly, such as IP
+ * reassembly, you don't know how big the
+ * reassembled packet is unless you reassemble
+ * it, so, in those cases, we can't determine
+ * whether we would have gone past the end
+ * had we reassembled the packet.
+ */
+ return FragmentBoundsError;
+ }
+
+ /* OK, we're not an unreassembled fragment (that we know of). */
+ if (abs_offset <= tvb->reported_length) {
+ /*
+ * We're within the bounds of what this tvbuff
+ * purportedly contains, based on some length
+ * value, but we're not within the bounds of
+ * something from which this tvbuff was
+ * extracted, so that length value ran past
+ * the end of some parent tvbuff.
+ */
+ return ContainedBoundsError;
+ }
+
+ /*
+ * OK, it looks as if we ran past the claimed length
+ * of data.
+ */
+ return ReportedBoundsError;
+}
+
+static inline int
+compute_offset(const tvbuff_t *tvb, const gint offset, guint *offset_ptr)
+{
+ if (offset >= 0) {
+ /* Positive offset - relative to the beginning of the packet. */
+ if (G_LIKELY((guint) offset <= tvb->length)) {
+ *offset_ptr = offset;
+ } else if ((guint) offset <= tvb->contained_length) {
+ return BoundsError;
+ } else if (tvb->flags & TVBUFF_FRAGMENT) {
+ return FragmentBoundsError;
+ } else if ((guint) offset <= tvb->reported_length) {
+ return ContainedBoundsError;
+ } else {
+ return ReportedBoundsError;
+ }
+ }
+ else {
+ /* Negative offset - relative to the end of the packet. */
+ if (G_LIKELY((guint) -offset <= tvb->length)) {
+ *offset_ptr = tvb->length + offset;
+ } else if ((guint) -offset <= tvb->contained_length) {
+ return BoundsError;
+ } else if (tvb->flags & TVBUFF_FRAGMENT) {
+ return FragmentBoundsError;
+ } else if ((guint) -offset <= tvb->reported_length) {
+ return ContainedBoundsError;
+ } else {
+ return ReportedBoundsError;
+ }
+ }
+
+ return 0;
+}
+
+static inline int
+compute_offset_and_remaining(const tvbuff_t *tvb, const gint offset, guint *offset_ptr, guint *rem_len)
+{
+ int exception;
+
+ exception = compute_offset(tvb, offset, offset_ptr);
+ if (!exception)
+ *rem_len = tvb->length - *offset_ptr;
+
+ return exception;
+}
+
+/* Computes the absolute offset and length based on a possibly-negative offset
+ * and a length that is possible -1 (which means "to the end of the data").
+ * Returns integer indicating whether the offset is in bounds (0) or
+ * not (exception number). The integer ptrs are modified with the new offset,
+ * captured (available) length, and contained length (amount that's present
+ * in the parent tvbuff based on its reported length).
+ * No exception is thrown; on success, we return 0, otherwise we return an
+ * exception for the caller to throw if appropriate.
+ *
+ * XXX - we return success (0), if the offset is positive and right
+ * after the end of the tvbuff (i.e., equal to the length). We do this
+ * so that a dissector constructing a subset tvbuff for the next protocol
+ * will get a zero-length tvbuff, not an exception, if there's no data
+ * left for the next protocol - we want the next protocol to be the one
+ * that gets an exception, so the error is reported as an error in that
+ * protocol rather than the containing protocol. */
+static inline int
+check_offset_length_no_exception(const tvbuff_t *tvb,
+ const gint offset, gint const length_val,
+ guint *offset_ptr, guint *length_ptr)
+{
+ guint end_offset;
+ int exception;
+
+ DISSECTOR_ASSERT(offset_ptr);
+ DISSECTOR_ASSERT(length_ptr);
+
+ /* Compute the offset */
+ exception = compute_offset(tvb, offset, offset_ptr);
+ if (exception)
+ return exception;
+
+ if (length_val < -1) {
+ /* XXX - ReportedBoundsError? */
+ return BoundsError;
+ }
+
+ /* Compute the length */
+ if (length_val == -1)
+ *length_ptr = tvb->length - *offset_ptr;
+ else
+ *length_ptr = length_val;
+
+ /*
+ * Compute the offset of the first byte past the length.
+ */
+ end_offset = *offset_ptr + *length_ptr;
+
+ /*
+ * Check for an overflow
+ */
+ if (end_offset < *offset_ptr)
+ return BoundsError;
+
+ return validate_offset(tvb, end_offset);
+}
+
+/* Checks (+/-) offset and length and throws an exception if
+ * either is out of bounds. Sets integer ptrs to the new offset
+ * and length. */
+static inline void
+check_offset_length(const tvbuff_t *tvb,
+ const gint offset, gint const length_val,
+ guint *offset_ptr, guint *length_ptr)
+{
+ int exception;
+
+ exception = check_offset_length_no_exception(tvb, offset, length_val, offset_ptr, length_ptr);
+ if (exception)
+ THROW(exception);
+}
+
+void
+tvb_check_offset_length(const tvbuff_t *tvb,
+ const gint offset, gint const length_val,
+ guint *offset_ptr, guint *length_ptr)
+{
+ check_offset_length(tvb, offset, length_val, offset_ptr, length_ptr);
+}
+
+static const unsigned char left_aligned_bitmask[] = {
+ 0xff,
+ 0x80,
+ 0xc0,
+ 0xe0,
+ 0xf0,
+ 0xf8,
+ 0xfc,
+ 0xfe
+};
+
+tvbuff_t *
+tvb_new_octet_aligned(tvbuff_t *tvb, guint32 bit_offset, gint32 no_of_bits)
+{
+ tvbuff_t *sub_tvb = NULL;
+ guint32 byte_offset;
+ gint32 datalen, i;
+ guint8 left, right, remaining_bits, *buf;
+ const guint8 *data;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ byte_offset = bit_offset >> 3;
+ left = bit_offset % 8; /* for left-shifting */
+ right = 8 - left; /* for right-shifting */
+
+ if (no_of_bits == -1) {
+ datalen = _tvb_captured_length_remaining(tvb, byte_offset);
+ remaining_bits = 0;
+ } else {
+ datalen = no_of_bits >> 3;
+ remaining_bits = no_of_bits % 8;
+ if (remaining_bits) {
+ datalen++;
+ }
+ }
+
+ /* already aligned -> shortcut */
+ if ((left == 0) && (remaining_bits == 0)) {
+ return tvb_new_subset_length_caplen(tvb, byte_offset, datalen, datalen);
+ }
+
+ DISSECTOR_ASSERT(datalen>0);
+
+ /* if at least one trailing byte is available, we must use the content
+ * of that byte for the last shift (i.e. tvb_get_ptr() must use datalen + 1
+ * if non extra byte is available, the last shifted byte requires
+ * special treatment
+ */
+ if (_tvb_captured_length_remaining(tvb, byte_offset) > datalen) {
+ data = ensure_contiguous(tvb, byte_offset, datalen + 1); /* tvb_get_ptr */
+
+ /* Do this allocation AFTER tvb_get_ptr() (which could throw an exception) */
+ buf = (guint8 *)g_malloc(datalen);
+
+ /* shift tvb data bit_offset bits to the left */
+ for (i = 0; i < datalen; i++)
+ buf[i] = (data[i] << left) | (data[i+1] >> right);
+ } else {
+ data = ensure_contiguous(tvb, byte_offset, datalen); /* tvb_get_ptr() */
+
+ /* Do this allocation AFTER tvb_get_ptr() (which could throw an exception) */
+ buf = (guint8 *)g_malloc(datalen);
+
+ /* shift tvb data bit_offset bits to the left */
+ for (i = 0; i < (datalen-1); i++)
+ buf[i] = (data[i] << left) | (data[i+1] >> right);
+ buf[datalen-1] = data[datalen-1] << left; /* set last octet */
+ }
+ buf[datalen-1] &= left_aligned_bitmask[remaining_bits];
+
+ sub_tvb = tvb_new_child_real_data(tvb, buf, datalen, datalen);
+ tvb_set_free_cb(sub_tvb, g_free);
+
+ return sub_tvb;
+}
+
+tvbuff_t *
+tvb_new_octet_right_aligned(tvbuff_t *tvb, guint32 bit_offset, gint32 no_of_bits)
+{
+ tvbuff_t *sub_tvb = NULL;
+ guint32 byte_offset;
+ gint src_len, dst_len, i;
+ guint8 left, right, remaining_bits, *buf;
+ const guint8 *data;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ byte_offset = bit_offset / 8;
+ /* right shift to put bits in place and discard least significant bits */
+ right = bit_offset % 8;
+ /* left shift to get most significant bits from next octet */
+ left = 8 - right;
+
+ if (no_of_bits == -1) {
+ dst_len = _tvb_captured_length_remaining(tvb, byte_offset);
+ remaining_bits = 0;
+ } else {
+ dst_len = no_of_bits / 8;
+ remaining_bits = no_of_bits % 8;
+ if (remaining_bits) {
+ dst_len++;
+ }
+ }
+
+ /* already aligned -> shortcut */
+ if ((right == 0) && (remaining_bits == 0)) {
+ return tvb_new_subset_length_caplen(tvb, byte_offset, dst_len, dst_len);
+ }
+
+ DISSECTOR_ASSERT(dst_len>0);
+
+ if (_tvb_captured_length_remaining(tvb, byte_offset) > dst_len) {
+ /* last octet will get data from trailing octet */
+ src_len = dst_len + 1;
+ } else {
+ /* last octet will be zero padded */
+ src_len = dst_len;
+ }
+
+ data = ensure_contiguous(tvb, byte_offset, src_len); /* tvb_get_ptr */
+
+ /* Do this allocation AFTER tvb_get_ptr() (which could throw an exception) */
+ buf = (guint8 *)g_malloc(dst_len);
+
+ for (i = 0; i < (dst_len - 1); i++)
+ buf[i] = (data[i] >> right) | (data[i+1] << left);
+
+ /* Special handling for last octet */
+ buf[i] = (data[i] >> right);
+ /* Shift most significant bits from trailing octet if available */
+ if (src_len > dst_len)
+ buf[i] |= (data[i+1] << left);
+ /* Preserve only remaining bits in last octet if not multiple of 8 */
+ if (remaining_bits)
+ buf[i] &= ((1 << remaining_bits) - 1);
+
+ sub_tvb = tvb_new_child_real_data(tvb, buf, dst_len, dst_len);
+ tvb_set_free_cb(sub_tvb, g_free);
+
+ return sub_tvb;
+}
+
+static tvbuff_t *
+tvb_generic_clone_offset_len(tvbuff_t *tvb, guint offset, guint len)
+{
+ tvbuff_t *cloned_tvb;
+ guint8 *data;
+
+ DISSECTOR_ASSERT(tvb_bytes_exist(tvb, offset, len));
+
+ data = (guint8 *) g_malloc(len);
+
+ tvb_memcpy(tvb, data, offset, len);
+
+ cloned_tvb = tvb_new_real_data(data, len, len);
+ tvb_set_free_cb(cloned_tvb, g_free);
+
+ return cloned_tvb;
+}
+
+tvbuff_t *
+tvb_clone_offset_len(tvbuff_t *tvb, guint offset, guint len)
+{
+ if (tvb->ops->tvb_clone) {
+ tvbuff_t *cloned_tvb;
+
+ cloned_tvb = tvb->ops->tvb_clone(tvb, offset, len);
+ if (cloned_tvb)
+ return cloned_tvb;
+ }
+
+ return tvb_generic_clone_offset_len(tvb, offset, len);
+}
+
+tvbuff_t *
+tvb_clone(tvbuff_t *tvb)
+{
+ return tvb_clone_offset_len(tvb, 0, tvb->length);
+}
+
+guint
+tvb_captured_length(const tvbuff_t *tvb)
+{
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ return tvb->length;
+}
+
+/* For tvbuff internal use */
+static inline gint
+_tvb_captured_length_remaining(const tvbuff_t *tvb, const gint offset)
+{
+ guint abs_offset = 0, rem_length;
+ int exception;
+
+ exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &rem_length);
+ if (exception)
+ return 0;
+
+ return rem_length;
+}
+
+gint
+tvb_captured_length_remaining(const tvbuff_t *tvb, const gint offset)
+{
+ guint abs_offset = 0, rem_length;
+ int exception;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &rem_length);
+ if (exception)
+ return 0;
+
+ return rem_length;
+}
+
+guint
+tvb_ensure_captured_length_remaining(const tvbuff_t *tvb, const gint offset)
+{
+ guint abs_offset = 0, rem_length = 0;
+ int exception;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &rem_length);
+ if (exception)
+ THROW(exception);
+
+ if (rem_length == 0) {
+ /*
+ * This routine ensures there's at least one byte available.
+ * There aren't any bytes available, so throw the appropriate
+ * exception.
+ */
+ if (abs_offset < tvb->contained_length) {
+ THROW(BoundsError);
+ } else if (tvb->flags & TVBUFF_FRAGMENT) {
+ THROW(FragmentBoundsError);
+ } else if (abs_offset < tvb->reported_length) {
+ THROW(ContainedBoundsError);
+ } else {
+ THROW(ReportedBoundsError);
+ }
+ }
+ return rem_length;
+}
+
+/* Validates that 'length' bytes are available starting from
+ * offset (pos/neg). Does not throw an exception. */
+gboolean
+tvb_bytes_exist(const tvbuff_t *tvb, const gint offset, const gint length)
+{
+ guint abs_offset = 0, abs_length;
+ int exception;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ /*
+ * Negative lengths are not possible and indicate a bug (e.g. arithmetic
+ * error or an overly large value from packet data).
+ */
+ if (length < 0)
+ return FALSE;
+
+ exception = check_offset_length_no_exception(tvb, offset, length, &abs_offset, &abs_length);
+ if (exception)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Validates that 'length' bytes, where 'length' is a 64-bit unsigned
+ * integer, are available starting from offset (pos/neg). Throws an
+ * exception if they aren't. */
+void
+tvb_ensure_bytes_exist64(const tvbuff_t *tvb, const gint offset, const guint64 length)
+{
+ /*
+ * Make sure the value fits in a signed integer; if not, assume
+ * that means that it's too big.
+ */
+ if (length > G_MAXINT) {
+ THROW(ReportedBoundsError);
+ }
+
+ /* OK, now cast it and try it with tvb_ensure_bytes_exist(). */
+ tvb_ensure_bytes_exist(tvb, offset, (gint)length);
+}
+
+/* Validates that 'length' bytes are available starting from
+ * offset (pos/neg). Throws an exception if they aren't. */
+void
+tvb_ensure_bytes_exist(const tvbuff_t *tvb, const gint offset, const gint length)
+{
+ guint real_offset, end_offset;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ /*
+ * -1 doesn't mean "until end of buffer", as that's pointless
+ * for this routine. We must treat it as a Really Large Positive
+ * Number, so that we throw an exception; we throw
+ * ReportedBoundsError, as if it were past even the end of a
+ * reassembled packet, and past the end of even the data we
+ * didn't capture.
+ *
+ * We do the same with other negative lengths.
+ */
+ if (length < 0) {
+ THROW(ReportedBoundsError);
+ }
+
+ /* XXX: Below this point could be replaced with a call to
+ * check_offset_length with no functional change, however this is a
+ * *very* hot path and check_offset_length is not well-optimized for
+ * this case, so we eat some code duplication for a lot of speedup. */
+
+ if (offset >= 0) {
+ /* Positive offset - relative to the beginning of the packet. */
+ if (G_LIKELY((guint) offset <= tvb->length)) {
+ real_offset = offset;
+ } else if ((guint) offset <= tvb->contained_length) {
+ THROW(BoundsError);
+ } else if (tvb->flags & TVBUFF_FRAGMENT) {
+ THROW(FragmentBoundsError);
+ } else if ((guint) offset <= tvb->reported_length) {
+ THROW(ContainedBoundsError);
+ } else {
+ THROW(ReportedBoundsError);
+ }
+ }
+ else {
+ /* Negative offset - relative to the end of the packet. */
+ if (G_LIKELY((guint) -offset <= tvb->length)) {
+ real_offset = tvb->length + offset;
+ } else if ((guint) -offset <= tvb->contained_length) {
+ THROW(BoundsError);
+ } else if (tvb->flags & TVBUFF_FRAGMENT) {
+ THROW(FragmentBoundsError);
+ } else if ((guint) -offset <= tvb->reported_length) {
+ THROW(ContainedBoundsError);
+ } else {
+ THROW(ReportedBoundsError);
+ }
+ }
+
+ /*
+ * Compute the offset of the first byte past the length.
+ */
+ end_offset = real_offset + length;
+
+ /*
+ * Check for an overflow
+ */
+ if (end_offset < real_offset)
+ THROW(BoundsError);
+
+ if (G_LIKELY(end_offset <= tvb->length))
+ return;
+ else if (end_offset <= tvb->contained_length)
+ THROW(BoundsError);
+ else if (tvb->flags & TVBUFF_FRAGMENT)
+ THROW(FragmentBoundsError);
+ else if (end_offset <= tvb->reported_length)
+ THROW(ContainedBoundsError);
+ else
+ THROW(ReportedBoundsError);
+}
+
+gboolean
+tvb_offset_exists(const tvbuff_t *tvb, const gint offset)
+{
+ guint abs_offset = 0;
+ int exception;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ exception = compute_offset(tvb, offset, &abs_offset);
+ if (exception)
+ return FALSE;
+
+ /* compute_offset only throws an exception on >, not >= because of the
+ * comment above check_offset_length_no_exception, but here we want the
+ * opposite behaviour so we check ourselves... */
+ if (abs_offset < tvb->length) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+guint
+tvb_reported_length(const tvbuff_t *tvb)
+{
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ return tvb->reported_length;
+}
+
+gint
+tvb_reported_length_remaining(const tvbuff_t *tvb, const gint offset)
+{
+ guint abs_offset = 0;
+ int exception;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ exception = compute_offset(tvb, offset, &abs_offset);
+ if (exception)
+ return 0;
+
+ if (tvb->reported_length >= abs_offset)
+ return tvb->reported_length - abs_offset;
+ else
+ return 0;
+}
+
+guint
+tvb_ensure_reported_length_remaining(const tvbuff_t *tvb, const gint offset)
+{
+ guint abs_offset = 0;
+ int exception;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ exception = compute_offset(tvb, offset, &abs_offset);
+ if (exception)
+ THROW(exception);
+
+ if (tvb->reported_length >= abs_offset)
+ return tvb->reported_length - abs_offset;
+ else
+ THROW(ReportedBoundsError);
+}
+
+/* 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 available and contained length. */
+void
+tvb_set_reported_length(tvbuff_t *tvb, const guint reported_length)
+{
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ if (reported_length > tvb->reported_length)
+ THROW(ReportedBoundsError);
+
+ tvb->reported_length = reported_length;
+ if (reported_length < tvb->length)
+ tvb->length = reported_length;
+ if (reported_length < tvb->contained_length)
+ tvb->contained_length = reported_length;
+}
+
+/* Repair a tvbuff where the captured length is greater than the
+ * reported length; such a tvbuff makes no sense, as it's impossible
+ * to capture more data than is in the packet.
+ */
+void
+tvb_fix_reported_length(tvbuff_t *tvb)
+{
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+ DISSECTOR_ASSERT(tvb->reported_length < tvb->length);
+
+ tvb->reported_length = tvb->length;
+ if (tvb->contained_length < tvb->length)
+ tvb->contained_length = tvb->length;
+}
+
+guint
+tvb_offset_from_real_beginning_counter(const tvbuff_t *tvb, const guint counter)
+{
+ if (tvb->ops->tvb_offset)
+ return tvb->ops->tvb_offset(tvb, counter);
+
+ DISSECTOR_ASSERT_NOT_REACHED();
+ return 0;
+}
+
+guint
+tvb_offset_from_real_beginning(const tvbuff_t *tvb)
+{
+ return tvb_offset_from_real_beginning_counter(tvb, 0);
+}
+
+static inline const guint8*
+ensure_contiguous_no_exception(tvbuff_t *tvb, const gint offset, const gint length, int *pexception)
+{
+ guint abs_offset = 0, abs_length = 0;
+ int exception;
+
+ exception = check_offset_length_no_exception(tvb, offset, length, &abs_offset, &abs_length);
+ if (exception) {
+ if (pexception)
+ *pexception = exception;
+ return NULL;
+ }
+
+ /*
+ * Special case: if the caller (e.g. tvb_get_ptr) requested no data,
+ * then it is acceptable to have an empty tvb (!tvb->real_data).
+ */
+ if (length == 0) {
+ return NULL;
+ }
+
+ /*
+ * We know that all the data is present in the tvbuff, so
+ * no exceptions should be thrown.
+ */
+ if (tvb->real_data)
+ return tvb->real_data + abs_offset;
+
+ if (tvb->ops->tvb_get_ptr)
+ return tvb->ops->tvb_get_ptr(tvb, abs_offset, abs_length);
+
+ DISSECTOR_ASSERT_NOT_REACHED();
+ return NULL;
+}
+
+static inline const guint8*
+ensure_contiguous(tvbuff_t *tvb, const gint offset, const gint length)
+{
+ int exception = 0;
+ const guint8 *p;
+
+ p = ensure_contiguous_no_exception(tvb, offset, length, &exception);
+ if (p == NULL && length != 0) {
+ DISSECTOR_ASSERT(exception > 0);
+ THROW(exception);
+ }
+ return p;
+}
+
+static inline const guint8*
+fast_ensure_contiguous(tvbuff_t *tvb, const gint offset, const guint length)
+{
+ guint end_offset;
+ guint u_offset;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+ /* We don't check for overflow in this fast path so we only handle simple types */
+ DISSECTOR_ASSERT(length <= 8);
+
+ if (offset < 0 || !tvb->real_data) {
+ return ensure_contiguous(tvb, offset, length);
+ }
+
+ u_offset = offset;
+ end_offset = u_offset + length;
+
+ if (G_LIKELY(end_offset <= tvb->length)) {
+ return tvb->real_data + u_offset;
+ } else if (end_offset <= tvb->contained_length) {
+ THROW(BoundsError);
+ } else if (tvb->flags & TVBUFF_FRAGMENT) {
+ THROW(FragmentBoundsError);
+ } else if (end_offset <= tvb->reported_length) {
+ THROW(ContainedBoundsError);
+ } else {
+ THROW(ReportedBoundsError);
+ }
+ /* not reached */
+ return NULL;
+}
+
+
+
+/************** ACCESSORS **************/
+
+void *
+tvb_memcpy(tvbuff_t *tvb, void *target, const gint offset, size_t length)
+{
+ guint abs_offset = 0, abs_length = 0;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ /*
+ * XXX - we should eliminate the "length = -1 means 'to the end
+ * of the tvbuff'" convention, and use other means to achieve
+ * that; this would let us eliminate a bunch of checks for
+ * negative lengths in cases where the protocol has a 32-bit
+ * length field.
+ *
+ * Allowing -1 but throwing an assertion on other negative
+ * lengths is a bit more work with the length being a size_t;
+ * instead, we check for a length <= 2^31-1.
+ */
+ DISSECTOR_ASSERT(length <= 0x7FFFFFFF);
+ check_offset_length(tvb, offset, (gint) length, &abs_offset, &abs_length);
+
+ if (target && tvb->real_data) {
+ return memcpy(target, tvb->real_data + abs_offset, abs_length);
+ }
+
+ if (target && tvb->ops->tvb_memcpy)
+ return tvb->ops->tvb_memcpy(tvb, target, abs_offset, abs_length);
+
+ /*
+ * If the length is 0, there's nothing to do.
+ * (tvb->real_data could be null if it's allocated with
+ * a size of length.)
+ */
+ if (length != 0) {
+ /*
+ * XXX, fallback to slower method
+ */
+ DISSECTOR_ASSERT_NOT_REACHED();
+ }
+ return NULL;
+}
+
+
+/*
+ * XXX - this doesn't treat a length of -1 as an error.
+ * If it did, this could replace some code that calls
+ * "tvb_ensure_bytes_exist()" and then allocates a buffer and copies
+ * data to it.
+ *
+ * "composite_get_ptr()" depends on -1 not being
+ * an error; does anything else depend on this routine treating -1 as
+ * meaning "to the end of the buffer"?
+ *
+ * If scope is NULL, memory is allocated with g_malloc() and user must
+ * explicitly free it with g_free().
+ * If scope is not NULL, memory is allocated with the corresponding pool
+ * lifetime.
+ */
+void *
+tvb_memdup(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, size_t length)
+{
+ guint abs_offset = 0, abs_length = 0;
+ void *duped;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ check_offset_length(tvb, offset, (gint) length, &abs_offset, &abs_length);
+
+ if (abs_length == 0)
+ return NULL;
+
+ duped = wmem_alloc(scope, abs_length);
+ return tvb_memcpy(tvb, duped, abs_offset, abs_length);
+}
+
+
+
+const guint8*
+tvb_get_ptr(tvbuff_t *tvb, const gint offset, const gint length)
+{
+ return ensure_contiguous(tvb, offset, length);
+}
+
+/* ---------------- */
+guint8
+tvb_get_guint8(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 1);
+ return *ptr;
+}
+
+gint8
+tvb_get_gint8(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 1);
+ return *ptr;
+}
+
+guint16
+tvb_get_ntohs(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 2);
+ return pntoh16(ptr);
+}
+
+gint16
+tvb_get_ntohis(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 2);
+ return pntoh16(ptr);
+}
+
+guint32
+tvb_get_ntoh24(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 3);
+ return pntoh24(ptr);
+}
+
+gint32
+tvb_get_ntohi24(tvbuff_t *tvb, const gint offset)
+{
+ guint32 ret;
+
+ ret = ws_sign_ext32(tvb_get_ntoh24(tvb, offset), 24);
+
+ return (gint32)ret;
+}
+
+guint32
+tvb_get_ntohl(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 4);
+ return pntoh32(ptr);
+}
+
+gint32
+tvb_get_ntohil(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 4);
+ return pntoh32(ptr);
+}
+
+guint64
+tvb_get_ntoh40(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 5);
+ return pntoh40(ptr);
+}
+
+gint64
+tvb_get_ntohi40(tvbuff_t *tvb, const gint offset)
+{
+ guint64 ret;
+
+ ret = ws_sign_ext64(tvb_get_ntoh40(tvb, offset), 40);
+
+ return (gint64)ret;
+}
+
+guint64
+tvb_get_ntoh48(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 6);
+ return pntoh48(ptr);
+}
+
+gint64
+tvb_get_ntohi48(tvbuff_t *tvb, const gint offset)
+{
+ guint64 ret;
+
+ ret = ws_sign_ext64(tvb_get_ntoh48(tvb, offset), 48);
+
+ return (gint64)ret;
+}
+
+guint64
+tvb_get_ntoh56(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 7);
+ return pntoh56(ptr);
+}
+
+gint64
+tvb_get_ntohi56(tvbuff_t *tvb, const gint offset)
+{
+ guint64 ret;
+
+ ret = ws_sign_ext64(tvb_get_ntoh56(tvb, offset), 56);
+
+ return (gint64)ret;
+}
+
+guint64
+tvb_get_ntoh64(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 8);
+ return pntoh64(ptr);
+}
+
+gint64
+tvb_get_ntohi64(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 8);
+ return pntoh64(ptr);
+}
+
+guint16
+tvb_get_guint16(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohs(tvb, offset);
+ } else {
+ return tvb_get_ntohs(tvb, offset);
+ }
+}
+
+gint16
+tvb_get_gint16(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohis(tvb, offset);
+ } else {
+ return tvb_get_ntohis(tvb, offset);
+ }
+}
+
+guint32
+tvb_get_guint24(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letoh24(tvb, offset);
+ } else {
+ return tvb_get_ntoh24(tvb, offset);
+ }
+}
+
+gint32
+tvb_get_gint24(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohi24(tvb, offset);
+ } else {
+ return tvb_get_ntohi24(tvb, offset);
+ }
+}
+
+guint32
+tvb_get_guint32(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohl(tvb, offset);
+ } else {
+ return tvb_get_ntohl(tvb, offset);
+ }
+}
+
+gint32
+tvb_get_gint32(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohil(tvb, offset);
+ } else {
+ return tvb_get_ntohil(tvb, offset);
+ }
+}
+
+guint64
+tvb_get_guint40(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letoh40(tvb, offset);
+ } else {
+ return tvb_get_ntoh40(tvb, offset);
+ }
+}
+
+gint64
+tvb_get_gint40(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohi40(tvb, offset);
+ } else {
+ return tvb_get_ntohi40(tvb, offset);
+ }
+}
+
+guint64
+tvb_get_guint48(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letoh48(tvb, offset);
+ } else {
+ return tvb_get_ntoh48(tvb, offset);
+ }
+}
+
+gint64
+tvb_get_gint48(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohi48(tvb, offset);
+ } else {
+ return tvb_get_ntohi48(tvb, offset);
+ }
+}
+
+guint64
+tvb_get_guint56(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letoh56(tvb, offset);
+ } else {
+ return tvb_get_ntoh56(tvb, offset);
+ }
+}
+
+gint64
+tvb_get_gint56(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohi56(tvb, offset);
+ } else {
+ return tvb_get_ntohi56(tvb, offset);
+ }
+}
+
+guint64
+tvb_get_guint64(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letoh64(tvb, offset);
+ } else {
+ return tvb_get_ntoh64(tvb, offset);
+ }
+}
+
+gint64
+tvb_get_gint64(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohi64(tvb, offset);
+ } else {
+ return tvb_get_ntohi64(tvb, offset);
+ }
+}
+
+gfloat
+tvb_get_ieee_float(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohieee_float(tvb, offset);
+ } else {
+ return tvb_get_ntohieee_float(tvb, offset);
+ }
+}
+
+gdouble
+tvb_get_ieee_double(tvbuff_t *tvb, const gint offset, const guint encoding) {
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return tvb_get_letohieee_double(tvb, offset);
+ } else {
+ return tvb_get_ntohieee_double(tvb, offset);
+ }
+}
+
+/*
+ * Stuff for IEEE float handling on platforms that don't have IEEE
+ * format as the native floating-point format.
+ *
+ * For now, we treat only the VAX as such a platform.
+ *
+ * XXX - other non-IEEE boxes that can run UN*X include some Crays,
+ * and possibly other machines. However, I don't know whether there
+ * are any other machines that could run Wireshark and that don't use
+ * IEEE format. As far as I know, all of the main current and past
+ * commercial microprocessor families on which OSes that support
+ * Wireshark can run use IEEE format (x86, ARM, 68k, SPARC, MIPS,
+ * PA-RISC, Alpha, IA-64, and so on), and it appears that the official
+ * Linux port to System/390 and zArchitecture uses IEEE format floating-
+ * point rather than IBM hex floating-point (not a huge surprise), so
+ * I'm not sure that leaves any 32-bit or larger UN*X or Windows boxes,
+ * other than VAXes, that don't use IEEE format. If you're not running
+ * UN*X or Windows, the floating-point format is probably going to be
+ * the least of your problems in a port.
+ */
+
+#if defined(vax)
+
+#include <math.h>
+
+/*
+ * Single-precision.
+ */
+#define IEEE_SP_NUMBER_WIDTH 32 /* bits in number */
+#define IEEE_SP_EXP_WIDTH 8 /* bits in exponent */
+#define IEEE_SP_MANTISSA_WIDTH 23 /* IEEE_SP_NUMBER_WIDTH - 1 - IEEE_SP_EXP_WIDTH */
+
+#define IEEE_SP_SIGN_MASK 0x80000000
+#define IEEE_SP_EXPONENT_MASK 0x7F800000
+#define IEEE_SP_MANTISSA_MASK 0x007FFFFF
+#define IEEE_SP_INFINITY IEEE_SP_EXPONENT_MASK
+
+#define IEEE_SP_IMPLIED_BIT (1 << IEEE_SP_MANTISSA_WIDTH)
+#define IEEE_SP_INFINITE ((1 << IEEE_SP_EXP_WIDTH) - 1)
+#define IEEE_SP_BIAS ((1 << (IEEE_SP_EXP_WIDTH - 1)) - 1)
+
+static int
+ieee_float_is_zero(const guint32 w)
+{
+ return ((w & ~IEEE_SP_SIGN_MASK) == 0);
+}
+
+static gfloat
+get_ieee_float(const guint32 w)
+{
+ long sign;
+ long exponent;
+ long mantissa;
+
+ sign = w & IEEE_SP_SIGN_MASK;
+ exponent = w & IEEE_SP_EXPONENT_MASK;
+ mantissa = w & IEEE_SP_MANTISSA_MASK;
+
+ if (ieee_float_is_zero(w)) {
+ /* number is zero, unnormalized, or not-a-number */
+ return 0.0;
+ }
+#if 0
+ /*
+ * XXX - how to handle this?
+ */
+ if (IEEE_SP_INFINITY == exponent) {
+ /*
+ * number is positive or negative infinity, or a special value
+ */
+ return (sign? MINUS_INFINITY: PLUS_INFINITY);
+ }
+#endif
+
+ exponent = ((exponent >> IEEE_SP_MANTISSA_WIDTH) - IEEE_SP_BIAS) -
+ IEEE_SP_MANTISSA_WIDTH;
+ mantissa |= IEEE_SP_IMPLIED_BIT;
+
+ if (sign)
+ return -mantissa * pow(2, exponent);
+ else
+ return mantissa * pow(2, exponent);
+}
+
+/*
+ * Double-precision.
+ * We assume that if you don't have IEEE floating-point, you have a
+ * compiler that understands 64-bit integral quantities.
+ */
+#define IEEE_DP_NUMBER_WIDTH 64 /* bits in number */
+#define IEEE_DP_EXP_WIDTH 11 /* bits in exponent */
+#define IEEE_DP_MANTISSA_WIDTH 52 /* IEEE_DP_NUMBER_WIDTH - 1 - IEEE_DP_EXP_WIDTH */
+
+#define IEEE_DP_SIGN_MASK G_GINT64_CONSTANT(0x8000000000000000)
+#define IEEE_DP_EXPONENT_MASK G_GINT64_CONSTANT(0x7FF0000000000000)
+#define IEEE_DP_MANTISSA_MASK G_GINT64_CONSTANT(0x000FFFFFFFFFFFFF)
+#define IEEE_DP_INFINITY IEEE_DP_EXPONENT_MASK
+
+#define IEEE_DP_IMPLIED_BIT (G_GINT64_CONSTANT(1) << IEEE_DP_MANTISSA_WIDTH)
+#define IEEE_DP_INFINITE ((1 << IEEE_DP_EXP_WIDTH) - 1)
+#define IEEE_DP_BIAS ((1 << (IEEE_DP_EXP_WIDTH - 1)) - 1)
+
+static int
+ieee_double_is_zero(const guint64 w)
+{
+ return ((w & ~IEEE_SP_SIGN_MASK) == 0);
+}
+
+static gdouble
+get_ieee_double(const guint64 w)
+{
+ gint64 sign;
+ gint64 exponent;
+ gint64 mantissa;
+
+ sign = w & IEEE_DP_SIGN_MASK;
+ exponent = w & IEEE_DP_EXPONENT_MASK;
+ mantissa = w & IEEE_DP_MANTISSA_MASK;
+
+ if (ieee_double_is_zero(w)) {
+ /* number is zero, unnormalized, or not-a-number */
+ return 0.0;
+ }
+#if 0
+ /*
+ * XXX - how to handle this?
+ */
+ if (IEEE_DP_INFINITY == exponent) {
+ /*
+ * number is positive or negative infinity, or a special value
+ */
+ return (sign? MINUS_INFINITY: PLUS_INFINITY);
+ }
+#endif
+
+ exponent = ((exponent >> IEEE_DP_MANTISSA_WIDTH) - IEEE_DP_BIAS) -
+ IEEE_DP_MANTISSA_WIDTH;
+ mantissa |= IEEE_DP_IMPLIED_BIT;
+
+ if (sign)
+ return -mantissa * pow(2, exponent);
+ else
+ return mantissa * pow(2, exponent);
+}
+#endif
+
+/*
+ * Fetches an IEEE single-precision floating-point number, in
+ * big-endian form, and returns a "float".
+ *
+ * XXX - should this be "double", in case there are IEEE single-
+ * precision numbers that won't fit in some platform's native
+ * "float" format?
+ */
+gfloat
+tvb_get_ntohieee_float(tvbuff_t *tvb, const int offset)
+{
+#if defined(vax)
+ return get_ieee_float(tvb_get_ntohl(tvb, offset));
+#else
+ union {
+ gfloat f;
+ guint32 w;
+ } ieee_fp_union;
+
+ ieee_fp_union.w = tvb_get_ntohl(tvb, offset);
+ return ieee_fp_union.f;
+#endif
+}
+
+/*
+ * Fetches an IEEE double-precision floating-point number, in
+ * big-endian form, and returns a "double".
+ */
+gdouble
+tvb_get_ntohieee_double(tvbuff_t *tvb, const int offset)
+{
+#if defined(vax)
+ union {
+ guint32 w[2];
+ guint64 dw;
+ } ieee_fp_union;
+#else
+ union {
+ gdouble d;
+ guint32 w[2];
+ } ieee_fp_union;
+#endif
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ ieee_fp_union.w[0] = tvb_get_ntohl(tvb, offset);
+ ieee_fp_union.w[1] = tvb_get_ntohl(tvb, offset+4);
+#else
+ ieee_fp_union.w[0] = tvb_get_ntohl(tvb, offset+4);
+ ieee_fp_union.w[1] = tvb_get_ntohl(tvb, offset);
+#endif
+#if defined(vax)
+ return get_ieee_double(ieee_fp_union.dw);
+#else
+ return ieee_fp_union.d;
+#endif
+}
+
+guint16
+tvb_get_letohs(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 2);
+ return pletoh16(ptr);
+}
+
+gint16
+tvb_get_letohis(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 2);
+ return pletoh16(ptr);
+}
+
+guint32
+tvb_get_letoh24(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 3);
+ return pletoh24(ptr);
+}
+
+gint32
+tvb_get_letohi24(tvbuff_t *tvb, const gint offset)
+{
+ guint32 ret;
+
+ ret = ws_sign_ext32(tvb_get_letoh24(tvb, offset), 24);
+
+ return (gint32)ret;
+}
+
+guint32
+tvb_get_letohl(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 4);
+ return pletoh32(ptr);
+}
+
+gint32
+tvb_get_letohil(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 4);
+ return pletoh32(ptr);
+}
+
+guint64
+tvb_get_letoh40(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 5);
+ return pletoh40(ptr);
+}
+
+gint64
+tvb_get_letohi40(tvbuff_t *tvb, const gint offset)
+{
+ guint64 ret;
+
+ ret = ws_sign_ext64(tvb_get_letoh40(tvb, offset), 40);
+
+ return (gint64)ret;
+}
+
+guint64
+tvb_get_letoh48(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 6);
+ return pletoh48(ptr);
+}
+
+gint64
+tvb_get_letohi48(tvbuff_t *tvb, const gint offset)
+{
+ guint64 ret;
+
+ ret = ws_sign_ext64(tvb_get_letoh48(tvb, offset), 48);
+
+ return (gint64)ret;
+}
+
+guint64
+tvb_get_letoh56(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 7);
+ return pletoh56(ptr);
+}
+
+gint64
+tvb_get_letohi56(tvbuff_t *tvb, const gint offset)
+{
+ guint64 ret;
+
+ ret = ws_sign_ext64(tvb_get_letoh56(tvb, offset), 56);
+
+ return (gint64)ret;
+}
+
+guint64
+tvb_get_letoh64(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 8);
+ return pletoh64(ptr);
+}
+
+gint64
+tvb_get_letohi64(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, 8);
+ return pletoh64(ptr);
+}
+
+/*
+ * Fetches an IEEE single-precision floating-point number, in
+ * little-endian form, and returns a "float".
+ *
+ * XXX - should this be "double", in case there are IEEE single-
+ * precision numbers that won't fit in some platform's native
+ * "float" format?
+ */
+gfloat
+tvb_get_letohieee_float(tvbuff_t *tvb, const int offset)
+{
+#if defined(vax)
+ return get_ieee_float(tvb_get_letohl(tvb, offset));
+#else
+ union {
+ gfloat f;
+ guint32 w;
+ } ieee_fp_union;
+
+ ieee_fp_union.w = tvb_get_letohl(tvb, offset);
+ return ieee_fp_union.f;
+#endif
+}
+
+/*
+ * Fetches an IEEE double-precision floating-point number, in
+ * little-endian form, and returns a "double".
+ */
+gdouble
+tvb_get_letohieee_double(tvbuff_t *tvb, const int offset)
+{
+#if defined(vax)
+ union {
+ guint32 w[2];
+ guint64 dw;
+ } ieee_fp_union;
+#else
+ union {
+ gdouble d;
+ guint32 w[2];
+ } ieee_fp_union;
+#endif
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ ieee_fp_union.w[0] = tvb_get_letohl(tvb, offset+4);
+ ieee_fp_union.w[1] = tvb_get_letohl(tvb, offset);
+#else
+ ieee_fp_union.w[0] = tvb_get_letohl(tvb, offset);
+ ieee_fp_union.w[1] = tvb_get_letohl(tvb, offset+4);
+#endif
+#if defined(vax)
+ return get_ieee_double(ieee_fp_union.dw);
+#else
+ return ieee_fp_union.d;
+#endif
+}
+
+/* This function is a slight misnomer. It accepts all encodings that are
+ * ASCII "enough", which means encodings that are the same as US-ASCII
+ * for textual representations of dates and hex bytes; i.e., the same
+ * for the hex digits and Z (in practice, all alphanumerics), and the
+ * four separators ':' '-' '.' and ' '
+ * That means that any encoding that keeps the ISO/IEC 646 invariant
+ * characters the same (including the T.61 8 bit encoding and multibyte
+ * encodings like EUC-KR and GB18030) are OK, even if they replace characters
+ * like '$' '#' and '\' with national variants, but not encodings like UTF-16
+ * that include extra null bytes.
+ * For our current purposes, the unpacked GSM 7-bit default alphabet (but not
+ * all National Language Shift Tables) also satisfies this requirement, but
+ * note that it does *not* keep all ISO/IEC 646 invariant characters the same.
+ * If this internal function gets used for additional purposes than currently,
+ * the set of encodings that it accepts could change.
+ * */
+static inline void
+validate_single_byte_ascii_encoding(const guint encoding)
+{
+ const guint enc = encoding & ~ENC_CHARENCODING_MASK;
+
+ switch (enc) {
+ case ENC_UTF_16:
+ case ENC_UCS_2:
+ case ENC_UCS_4:
+ case ENC_3GPP_TS_23_038_7BITS_PACKED:
+ case ENC_ASCII_7BITS:
+ case ENC_EBCDIC:
+ case ENC_EBCDIC_CP037:
+ case ENC_EBCDIC_CP500:
+ case ENC_BCD_DIGITS_0_9:
+ case ENC_KEYPAD_ABC_TBCD:
+ case ENC_KEYPAD_BC_TBCD:
+ case ENC_ETSI_TS_102_221_ANNEX_A:
+ case ENC_APN_STR:
+ case ENC_DECT_STANDARD_4BITS_TBCD:
+ REPORT_DISSECTOR_BUG("Invalid string encoding type passed to tvb_get_string_XXX");
+ break;
+ default:
+ break;
+ }
+ /* make sure something valid was set */
+ if (enc == 0)
+ REPORT_DISSECTOR_BUG("No string encoding type passed to tvb_get_string_XXX");
+}
+
+GByteArray*
+tvb_get_string_bytes(tvbuff_t *tvb, const gint offset, const gint length,
+ const guint encoding, GByteArray *bytes, gint *endoff)
+{
+ gchar *ptr;
+ const gchar *begin;
+ const gchar *end = NULL;
+ GByteArray *retval = NULL;
+
+ validate_single_byte_ascii_encoding(encoding);
+
+ ptr = (gchar*) tvb_get_raw_string(NULL, tvb, offset, length);
+ begin = ptr;
+
+ if (endoff) *endoff = offset;
+
+ while (*begin == ' ') begin++;
+
+ if (*begin && bytes) {
+ if (hex_str_to_bytes_encoding(begin, bytes, &end, encoding, FALSE)) {
+ if (bytes->len > 0) {
+ if (endoff) *endoff = offset + (gint)(end - ptr);
+ retval = bytes;
+ }
+ }
+ }
+
+ wmem_free(NULL, ptr);
+
+ return retval;
+}
+
+static gboolean
+parse_month_name(const char *name, int *tm_mon)
+{
+ static const char months[][4] = { "Jan", "Feb", "Mar", "Apr", "May",
+ "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ for (int i = 0; i < 12; i++) {
+ if (memcmp(months[i], name, 4) == 0) {
+ *tm_mon = i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Is the character a WSP character, as per RFC 5234? (space or tab).
+ */
+#define IS_WSP(c) ((c) == ' ' || (c) == '\t')
+
+/* support hex-encoded time values? */
+nstime_t*
+tvb_get_string_time(tvbuff_t *tvb, const gint offset, const gint length,
+ const guint encoding, nstime_t *ns, gint *endoff)
+{
+ gchar *begin;
+ const gchar *ptr;
+ const gchar *end = NULL;
+ int num_chars = 0;
+ int utc_offset = 0;
+
+ validate_single_byte_ascii_encoding(encoding);
+
+ DISSECTOR_ASSERT(ns);
+
+ begin = (gchar*) tvb_get_raw_string(NULL, tvb, offset, length);
+ ptr = begin;
+
+ while (IS_WSP(*ptr))
+ ptr++;
+
+ if (*ptr) {
+ if ((encoding & ENC_ISO_8601_DATE_TIME) == ENC_ISO_8601_DATE_TIME) {
+ if (!(end = iso8601_to_nstime(ns, ptr, ISO8601_DATETIME))) {
+
+
+ goto fail;
+ }
+ } else if ((encoding & ENC_ISO_8601_DATE_TIME_BASIC) == ENC_ISO_8601_DATE_TIME_BASIC) {
+ if (!(end = iso8601_to_nstime(ns, ptr, ISO8601_DATETIME_BASIC))) {
+
+
+ goto fail;
+ }
+ } else {
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_isdst = -1;
+ ns->secs = 0;
+ ns->nsecs = 0;
+
+ /* note: sscanf is known to be inconsistent across platforms with respect
+ to whether a %n is counted as a return value or not, so we have to use
+ '>=' a lot */
+ if (encoding & ENC_ISO_8601_DATE) {
+ /* 2014-04-07 */
+ if (sscanf(ptr, "%d-%d-%d%n",
+ &tm.tm_year,
+ &tm.tm_mon,
+ &tm.tm_mday,
+ &num_chars) >= 3)
+ {
+ end = ptr + num_chars;
+ tm.tm_mon--;
+ if (tm.tm_year > 1900) tm.tm_year -= 1900;
+ } else {
+ goto fail;
+ }
+ }
+ else if (encoding & ENC_ISO_8601_TIME) {
+ /* 2014-04-07 */
+ if (sscanf(ptr, "%d:%d:%d%n",
+ &tm.tm_hour,
+ &tm.tm_min,
+ &tm.tm_sec,
+ &num_chars) >= 2)
+ {
+ /* what should we do about day/month/year? */
+ /* setting it to "now" for now */
+ time_t time_now = time(NULL);
+ struct tm *tm_now = gmtime(&time_now);
+ if (tm_now != NULL) {
+ tm.tm_year = tm_now->tm_year;
+ tm.tm_mon = tm_now->tm_mon;
+ tm.tm_mday = tm_now->tm_mday;
+ } else {
+ /* The second before the Epoch */
+ tm.tm_year = 69;
+ tm.tm_mon = 12;
+ tm.tm_mday = 31;
+ }
+ end = ptr + num_chars;
+ } else {
+ goto fail;
+ }
+ }
+ else if (encoding & ENC_IMF_DATE_TIME) {
+ /*
+ * Match [dow,] day month year hh:mm[:ss] with
+ * two-digit years (RFC 822) or four-digit
+ * years (RFCs 1123, 2822, 5822). Skip
+ * the day of week since it is locale
+ * dependent and does not affect the resulting
+ * date anyway.
+ */
+ if (g_ascii_isalpha(ptr[0]) && g_ascii_isalpha(ptr[1]) && g_ascii_isalpha(ptr[2]) && ptr[3] == ',')
+ ptr += 4; /* Skip day of week. */
+
+ /*
+ * Parse the day-of-month and month
+ * name.
+ */
+ char month_name[4] = { 0 };
+
+ if (sscanf(ptr, "%d %3s%n",
+ &tm.tm_mday,
+ month_name,
+ &num_chars) < 2)
+ {
+ /* Not matched. */
+ goto fail;
+ }
+ if (!parse_month_name(month_name, &tm.tm_mon)) {
+ goto fail;
+ }
+ ptr += num_chars;
+ while (IS_WSP(*ptr))
+ ptr++;
+
+ /*
+ * Scan the year. Treat 2-digit years
+ * differently from 4-digit years.
+ */
+ guint32 year;
+ const gchar *yearendp;
+
+ if (!ws_strtou32(ptr, &yearendp, &year)) {
+ goto fail;
+ }
+ if (!IS_WSP(*yearendp)) {
+ /* Not followed by WSP. */
+ goto fail;
+ }
+ if (yearendp - ptr < 2) {
+ /* 1-digit year. Error. */
+ goto fail;
+ }
+ if (yearendp - ptr == 2) {
+ /*
+ * 2-digit year.
+ *
+ * Match RFC 2822/RFC 5322 behavior;
+ * add 2000 to years from 0 to
+ * 49 and 1900 to uears from 50
+ * to 99.
+ */
+ if (year <= 49) {
+ year += 2000;
+ } else {
+ year += 1900;
+ }
+ } else if (yearendp - ptr == 3) {
+ /*
+ * 3-digit year.
+ *
+ * Match RFC 2822/RFC 5322 behavior;
+ * add 1900 to the year.
+ */
+ year += 1900;
+ }
+ tm.tm_year = year - 1900;
+ ptr = yearendp;
+ while (IS_WSP(*ptr))
+ ptr++;
+
+ /* Parse the time. */
+ if (sscanf(ptr, "%d:%d%n:%d%n",
+ &tm.tm_hour,
+ &tm.tm_min,
+ &num_chars,
+ &tm.tm_sec,
+ &num_chars) < 2)
+ {
+ goto fail;
+ }
+ ptr += num_chars;
+ while (IS_WSP(*ptr))
+ ptr++;
+
+ /*
+ * Parse the time zone.
+ * Check for obs-zone values first.
+ */
+ if (g_ascii_strncasecmp(ptr, "UT", 2) == 0)
+ {
+ ptr += 2;
+ }
+ else if (g_ascii_strncasecmp(ptr, "GMT", 3) == 0)
+ {
+ ptr += 3;
+ }
+ else
+ {
+ char sign;
+ int off_hr;
+ int off_min;
+
+ if (sscanf(ptr, "%c%2d%2d%n",
+ &sign,
+ &off_hr,
+ &off_min,
+ &num_chars) < 3)
+ {
+ goto fail;
+ }
+
+ /*
+ * If sign is '+', there's a positive
+ * UTC offset.
+ *
+ * If sign is '-', there's a negative
+ * UTC offset.
+ *
+ * Otherwise, that's an invalid UTC
+ * offset string.
+ */
+ if (sign == '+')
+ utc_offset += (off_hr * 3600) + (off_min * 60);
+ else if (sign == '-')
+ utc_offset -= (off_hr * 3600) + (off_min * 60);
+ else {
+ /* Sign must be + or - */
+ goto fail;
+ }
+ ptr += num_chars;
+ }
+ end = ptr;
+ }
+ ns->secs = mktime_utc(&tm);
+ if (ns->secs == (time_t)-1 && errno != 0) {
+ goto fail;
+ }
+ ns->secs += utc_offset;
+ }
+ } else {
+ /* Empty string */
+ goto fail;
+ }
+
+ if (endoff)
+ *endoff = (gint)(offset + (end - begin));
+ wmem_free(NULL, begin);
+ return ns;
+
+fail:
+ wmem_free(NULL, begin);
+ return NULL;
+}
+
+/* Fetch an IPv4 address, in network byte order.
+ * We do *not* convert them to host byte order; we leave them in
+ * network byte order. */
+guint32
+tvb_get_ipv4(tvbuff_t *tvb, const gint offset)
+{
+ const guint8 *ptr;
+ guint32 addr;
+
+ ptr = fast_ensure_contiguous(tvb, offset, sizeof(guint32));
+ memcpy(&addr, ptr, sizeof addr);
+ return addr;
+}
+
+/* Fetch an IPv6 address. */
+void
+tvb_get_ipv6(tvbuff_t *tvb, const gint offset, ws_in6_addr *addr)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, sizeof(*addr));
+ memcpy(addr, ptr, sizeof *addr);
+}
+
+/* Fetch a GUID. */
+void
+tvb_get_ntohguid(tvbuff_t *tvb, const gint offset, e_guid_t *guid)
+{
+ const guint8 *ptr = ensure_contiguous(tvb, offset, GUID_LEN);
+
+ guid->data1 = pntoh32(ptr + 0);
+ guid->data2 = pntoh16(ptr + 4);
+ guid->data3 = pntoh16(ptr + 6);
+ memcpy(guid->data4, ptr + 8, sizeof guid->data4);
+}
+
+void
+tvb_get_letohguid(tvbuff_t *tvb, const gint offset, e_guid_t *guid)
+{
+ const guint8 *ptr = ensure_contiguous(tvb, offset, GUID_LEN);
+
+ guid->data1 = pletoh32(ptr + 0);
+ guid->data2 = pletoh16(ptr + 4);
+ guid->data3 = pletoh16(ptr + 6);
+ memcpy(guid->data4, ptr + 8, sizeof guid->data4);
+}
+
+/*
+ * NOTE: to support code written when proto_tree_add_item() took a
+ * gboolean as its last argument, with FALSE meaning "big-endian"
+ * and TRUE meaning "little-endian", we treat any non-zero value of
+ * "encoding" as meaning "little-endian".
+ */
+void
+tvb_get_guid(tvbuff_t *tvb, const gint offset, e_guid_t *guid, const guint encoding)
+{
+ if (encoding) {
+ tvb_get_letohguid(tvb, offset, guid);
+ } else {
+ tvb_get_ntohguid(tvb, offset, guid);
+ }
+}
+
+static const guint8 bit_mask8[] = {
+ 0x00,
+ 0x01,
+ 0x03,
+ 0x07,
+ 0x0f,
+ 0x1f,
+ 0x3f,
+ 0x7f,
+ 0xff
+};
+
+
+/* Get a variable ammount of bits
+ *
+ * Return a byte array with bit limited data.
+ * When encoding is ENC_BIG_ENDIAN, the data is aligned to the left.
+ * When encoding is ENC_LITTLE_ENDIAN, the data is aligned to the right.
+ */
+guint8 *
+tvb_get_bits_array(wmem_allocator_t *scope, tvbuff_t *tvb, const gint bit_offset,
+ size_t no_of_bits, size_t *data_length, const guint encoding)
+{
+ tvbuff_t *sub_tvb;
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ sub_tvb = tvb_new_octet_right_aligned(tvb, bit_offset, (gint32) no_of_bits);
+ } else {
+ sub_tvb = tvb_new_octet_aligned(tvb, bit_offset, (gint32) no_of_bits);
+ }
+ *data_length = tvb_reported_length(sub_tvb);
+ return (guint8*)tvb_memdup(scope, sub_tvb, 0, *data_length);
+}
+
+/* Get 1 - 8 bits */
+guint8
+tvb_get_bits8(tvbuff_t *tvb, guint bit_offset, const gint no_of_bits)
+{
+ return (guint8)_tvb_get_bits64(tvb, bit_offset, no_of_bits);
+}
+
+/* Get 1 - 16 bits */
+guint16
+tvb_get_bits16(tvbuff_t *tvb, guint bit_offset, const gint no_of_bits, const guint encoding)
+{
+ return (guint16)tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding);
+}
+
+/* Get 1 - 32 bits */
+guint32
+tvb_get_bits32(tvbuff_t *tvb, guint bit_offset, const gint no_of_bits, const guint encoding)
+{
+ return (guint32)tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding);
+}
+
+/* Get 1 - 64 bits */
+guint64
+tvb_get_bits64(tvbuff_t *tvb, guint bit_offset, const gint no_of_bits, const guint encoding)
+{
+ /* encoding determines bit numbering within octet array */
+ if (encoding & ENC_LITTLE_ENDIAN) {
+ return _tvb_get_bits64_le(tvb, bit_offset, no_of_bits);
+ } else {
+ return _tvb_get_bits64(tvb, bit_offset, no_of_bits);
+ }
+}
+
+/*
+ * This function will dissect a sequence of bits that does not need to be byte aligned; the bits
+ * set will be shown in the tree as ..10 10.. and the integer value returned if return_value is set.
+ * Offset should be given in bits from the start of the tvb.
+ * Bits within octet are numbered from MSB (0) to LSB (7). Bit at bit_offset is return value most significant bit.
+ * The function tolerates requests for more than 64 bits, but will only return the least significant 64 bits.
+ */
+static guint64
+_tvb_get_bits64(tvbuff_t *tvb, guint bit_offset, const gint total_no_of_bits)
+{
+ guint64 value;
+ guint octet_offset = bit_offset >> 3;
+ guint8 required_bits_in_first_octet = 8 - (bit_offset % 8);
+
+ if(required_bits_in_first_octet > total_no_of_bits)
+ {
+ /* the required bits don't extend to the end of the first octet */
+ guint8 right_shift = required_bits_in_first_octet - total_no_of_bits;
+ value = (tvb_get_guint8(tvb, octet_offset) >> right_shift) & bit_mask8[total_no_of_bits % 8];
+ }
+ else
+ {
+ guint8 remaining_bit_length = total_no_of_bits;
+
+ /* get the bits up to the first octet boundary */
+ value = 0;
+ required_bits_in_first_octet %= 8;
+ if(required_bits_in_first_octet != 0)
+ {
+ value = tvb_get_guint8(tvb, octet_offset) & bit_mask8[required_bits_in_first_octet];
+ remaining_bit_length -= required_bits_in_first_octet;
+ octet_offset ++;
+ }
+ /* take the biggest words, shorts or octets that we can */
+ while (remaining_bit_length > 7)
+ {
+ switch (remaining_bit_length >> 4)
+ {
+ case 0:
+ /* 8 - 15 bits. (note that 0 - 7 would have dropped out of the while() loop) */
+ value <<= 8;
+ value += tvb_get_guint8(tvb, octet_offset);
+ remaining_bit_length -= 8;
+ octet_offset ++;
+ break;
+
+ case 1:
+ /* 16 - 31 bits */
+ value <<= 16;
+ value += tvb_get_ntohs(tvb, octet_offset);
+ remaining_bit_length -= 16;
+ octet_offset += 2;
+ break;
+
+ case 2:
+ case 3:
+ /* 32 - 63 bits */
+ value <<= 32;
+ value += tvb_get_ntohl(tvb, octet_offset);
+ remaining_bit_length -= 32;
+ octet_offset += 4;
+ break;
+
+ default:
+ /* 64 bits (or more???) */
+ value = tvb_get_ntoh64(tvb, octet_offset);
+ remaining_bit_length -= 64;
+ octet_offset += 8;
+ break;
+ }
+ }
+ /* get bits from any partial octet at the tail */
+ if(remaining_bit_length)
+ {
+ value <<= remaining_bit_length;
+ value += (tvb_get_guint8(tvb, octet_offset) >> (8 - remaining_bit_length));
+ }
+ }
+ return value;
+}
+
+/*
+ * Offset should be given in bits from the start of the tvb.
+ * Bits within octet are numbered from LSB (0) to MSB (7). Bit at bit_offset is return value least significant bit.
+ * The function tolerates requests for more than 64 bits, but will only return the least significant 64 bits.
+ */
+static guint64
+_tvb_get_bits64_le(tvbuff_t *tvb, guint bit_offset, const gint total_no_of_bits)
+{
+ guint64 value = 0;
+ guint octet_offset = bit_offset / 8;
+ gint remaining_bits = total_no_of_bits;
+ gint shift = 0;
+
+ if (remaining_bits > 64)
+ {
+ remaining_bits = 64;
+ }
+
+ if (bit_offset % 8)
+ {
+ /* not aligned, extract bits from first octet */
+ shift = 8 - (bit_offset % 8);
+ value = tvb_get_guint8(tvb, octet_offset) >> (bit_offset % 8);
+ if (shift > total_no_of_bits)
+ {
+ /* keep only the requested bits */
+ value &= (G_GUINT64_CONSTANT(1) << total_no_of_bits) - 1;
+ remaining_bits = 0;
+ }
+ else
+ {
+ remaining_bits = total_no_of_bits - shift;
+ }
+ octet_offset++;
+ }
+
+ while (remaining_bits > 0)
+ {
+ /* take the biggest words, shorts or octets that we can */
+ if (remaining_bits >= 32)
+ {
+ value |= ((guint64)tvb_get_letohl(tvb, octet_offset) << shift);
+ shift += 32;
+ remaining_bits -= 32;
+ octet_offset += 4;
+ }
+ else if (remaining_bits >= 16)
+ {
+ value |= ((guint64)tvb_get_letohs(tvb, octet_offset) << shift);
+ shift += 16;
+ remaining_bits -= 16;
+ octet_offset += 2;
+ }
+ else if (remaining_bits >= 8)
+ {
+ value |= ((guint64)tvb_get_guint8(tvb, octet_offset) << shift);
+ shift += 8;
+ remaining_bits -= 8;
+ octet_offset += 1;
+ }
+ else
+ {
+ guint mask = (1 << remaining_bits) - 1;
+ value |= (((guint64)tvb_get_guint8(tvb, octet_offset) & mask) << shift);
+ shift += remaining_bits;
+ remaining_bits = 0;
+ octet_offset += 1;
+ }
+ }
+ return value;
+}
+
+/* Get 1 - 32 bits (should be deprecated as same as tvb_get_bits32??) */
+guint32
+tvb_get_bits(tvbuff_t *tvb, const guint bit_offset, const gint no_of_bits, const guint encoding)
+{
+ return (guint32)tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding);
+}
+
+static gint
+tvb_find_guint8_generic(tvbuff_t *tvb, guint abs_offset, guint limit, guint8 needle)
+{
+ const guint8 *ptr;
+ const guint8 *result;
+
+ ptr = ensure_contiguous(tvb, abs_offset, limit); /* tvb_get_ptr() */
+ if (!ptr)
+ return -1;
+
+ result = (const guint8 *) memchr(ptr, needle, limit);
+ if (!result)
+ return -1;
+
+ return (gint) ((result - ptr) + abs_offset);
+}
+
+/* Find first occurrence of needle in tvbuff, starting at offset. Searches
+ * at most maxlength number of bytes; if maxlength is -1, searches to
+ * end of tvbuff.
+ * Returns the offset of the found needle, or -1 if not found.
+ * Will not throw an exception, even if maxlength exceeds boundary of tvbuff;
+ * in that case, -1 will be returned if the boundary is reached before
+ * finding needle. */
+gint
+tvb_find_guint8(tvbuff_t *tvb, const gint offset, const gint maxlength, const guint8 needle)
+{
+ const guint8 *result;
+ guint abs_offset = 0;
+ guint limit = 0;
+ int exception;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &limit);
+ if (exception)
+ THROW(exception);
+
+ /* Only search to end of tvbuff, w/o throwing exception. */
+ if (maxlength >= 0 && limit > (guint) maxlength) {
+ /* Maximum length doesn't go past end of tvbuff; search
+ to that value. */
+ limit = (guint) maxlength;
+ }
+
+ /* If we have real data, perform our search now. */
+ if (tvb->real_data) {
+ result = (const guint8 *)memchr(tvb->real_data + abs_offset, needle, limit);
+ if (result == NULL) {
+ return -1;
+ }
+ else {
+ return (gint) (result - tvb->real_data);
+ }
+ }
+
+ if (tvb->ops->tvb_find_guint8)
+ return tvb->ops->tvb_find_guint8(tvb, abs_offset, limit, needle);
+
+ return tvb_find_guint8_generic(tvb, offset, limit, needle);
+}
+
+/* Same as tvb_find_guint8() with 16bit needle. */
+gint
+tvb_find_guint16(tvbuff_t *tvb, const gint offset, const gint maxlength,
+ const guint16 needle)
+{
+ guint abs_offset = 0;
+ guint limit = 0;
+ int exception;
+
+ exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &limit);
+ if (exception)
+ THROW(exception);
+
+ /* Only search to end of tvbuff, w/o throwing exception. */
+ if (maxlength >= 0 && limit > (guint) maxlength) {
+ /* Maximum length doesn't go past end of tvbuff; search
+ to that value. */
+ limit = (guint) maxlength;
+ }
+
+ const guint8 needle1 = ((needle & 0xFF00) >> 8);
+ const guint8 needle2 = ((needle & 0x00FF) >> 0);
+ guint searched_bytes = 0;
+ guint pos = abs_offset;
+
+ do {
+ gint offset1 =
+ tvb_find_guint8(tvb, pos, limit - searched_bytes, needle1);
+ gint offset2 = -1;
+
+ if (offset1 == -1) {
+ return -1;
+ }
+
+ searched_bytes = (guint)offset1 - abs_offset + 1;
+
+ if (searched_bytes >= limit) {
+ return -1;
+ }
+
+ offset2 = tvb_find_guint8(tvb, offset1 + 1, 1, needle2);
+
+ searched_bytes += 1;
+
+ if (offset2 != -1) {
+ if (searched_bytes > limit) {
+ return -1;
+ }
+ return offset1;
+ }
+
+ pos = offset1 + 1;
+ } while (searched_bytes < limit);
+
+ return -1;
+}
+
+static inline gint
+tvb_ws_mempbrk_guint8_generic(tvbuff_t *tvb, guint abs_offset, guint limit, const ws_mempbrk_pattern* pattern, guchar *found_needle)
+{
+ const guint8 *ptr;
+ const guint8 *result;
+
+ ptr = ensure_contiguous(tvb, abs_offset, limit); /* tvb_get_ptr */
+ if (!ptr)
+ return -1;
+
+ result = ws_mempbrk_exec(ptr, limit, pattern, found_needle);
+ if (!result)
+ return -1;
+
+ return (gint) ((result - ptr) + abs_offset);
+}
+
+
+/* Find first occurrence of any of the pattern chars in tvbuff, starting at offset.
+ * Searches at most maxlength number of bytes; if maxlength is -1, searches
+ * to end of tvbuff.
+ * Returns the offset of the found needle, or -1 if not found.
+ * Will not throw an exception, even if maxlength exceeds boundary of tvbuff;
+ * in that case, -1 will be returned if the boundary is reached before
+ * finding needle. */
+gint
+tvb_ws_mempbrk_pattern_guint8(tvbuff_t *tvb, const gint offset, const gint maxlength,
+ const ws_mempbrk_pattern* pattern, guchar *found_needle)
+{
+ const guint8 *result;
+ guint abs_offset = 0;
+ guint limit = 0;
+ int exception;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &limit);
+ if (exception)
+ THROW(exception);
+
+ /* Only search to end of tvbuff, w/o throwing exception. */
+ if (limit > (guint) maxlength) {
+ /* Maximum length doesn't go past end of tvbuff; search
+ to that value. */
+ limit = maxlength;
+ }
+
+ /* If we have real data, perform our search now. */
+ if (tvb->real_data) {
+ result = ws_mempbrk_exec(tvb->real_data + abs_offset, limit, pattern, found_needle);
+ if (result == NULL) {
+ return -1;
+ }
+ else {
+ return (gint) (result - tvb->real_data);
+ }
+ }
+
+ if (tvb->ops->tvb_ws_mempbrk_pattern_guint8)
+ return tvb->ops->tvb_ws_mempbrk_pattern_guint8(tvb, abs_offset, limit, pattern, found_needle);
+
+ return tvb_ws_mempbrk_guint8_generic(tvb, abs_offset, limit, pattern, found_needle);
+}
+
+/* Find size of stringz (NUL-terminated string) by looking for terminating
+ * NUL. The size of the string includes the terminating NUL.
+ *
+ * If the NUL isn't found, it throws the appropriate exception.
+ */
+guint
+tvb_strsize(tvbuff_t *tvb, const gint offset)
+{
+ guint abs_offset = 0, junk_length;
+ gint nul_offset;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ check_offset_length(tvb, offset, 0, &abs_offset, &junk_length);
+ nul_offset = tvb_find_guint8(tvb, abs_offset, -1, 0);
+ if (nul_offset == -1) {
+ /*
+ * OK, we hit the end of the tvbuff, so we should throw
+ * an exception.
+ */
+ if (tvb->length < tvb->contained_length) {
+ THROW(BoundsError);
+ } else if (tvb->flags & TVBUFF_FRAGMENT) {
+ THROW(FragmentBoundsError);
+ } else if (tvb->length < tvb->reported_length) {
+ THROW(ContainedBoundsError);
+ } else {
+ THROW(ReportedBoundsError);
+ }
+ }
+ return (nul_offset - abs_offset) + 1;
+}
+
+/* UTF-16/UCS-2 version of tvb_strsize */
+/* Returns number of bytes including the (two-bytes) null terminator */
+guint
+tvb_unicode_strsize(tvbuff_t *tvb, const gint offset)
+{
+ guint i = 0;
+ gunichar2 uchar;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ do {
+ /* Endianness doesn't matter when looking for null */
+ uchar = tvb_get_ntohs(tvb, offset + i);
+ i += 2;
+ } while(uchar != 0);
+
+ return i;
+}
+
+/* Find length of string by looking for end of string ('\0'), up to
+ * 'maxlength' characters'; if 'maxlength' is -1, searches to end
+ * of tvbuff.
+ * Returns -1 if 'maxlength' reached before finding EOS. */
+gint
+tvb_strnlen(tvbuff_t *tvb, const gint offset, const guint maxlength)
+{
+ gint result_offset;
+ guint abs_offset = 0, junk_length;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ check_offset_length(tvb, offset, 0, &abs_offset, &junk_length);
+
+ result_offset = tvb_find_guint8(tvb, abs_offset, maxlength, 0);
+
+ if (result_offset == -1) {
+ return -1;
+ }
+ else {
+ return result_offset - abs_offset;
+ }
+}
+
+/*
+ * Implement strneql etc
+ */
+
+/*
+ * Call strncmp after checking if enough chars left, returning 0 if
+ * it returns 0 (meaning "equal") and -1 otherwise, otherwise return -1.
+ */
+gint
+tvb_strneql(tvbuff_t *tvb, const gint offset, const gchar *str, const size_t size)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous_no_exception(tvb, offset, (gint)size, NULL);
+
+ if (ptr) {
+ int cmp = strncmp((const char *)ptr, str, size);
+
+ /*
+ * Return 0 if equal, -1 otherwise.
+ */
+ return (cmp == 0 ? 0 : -1);
+ } else {
+ /*
+ * Not enough characters in the tvbuff to match the
+ * string.
+ */
+ return -1;
+ }
+}
+
+/*
+ * Call g_ascii_strncasecmp after checking if enough chars left, returning
+ * 0 if it returns 0 (meaning "equal") and -1 otherwise, otherwise return -1.
+ */
+gint
+tvb_strncaseeql(tvbuff_t *tvb, const gint offset, const gchar *str, const size_t size)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous_no_exception(tvb, offset, (gint)size, NULL);
+
+ if (ptr) {
+ int cmp = g_ascii_strncasecmp((const char *)ptr, str, size);
+
+ /*
+ * Return 0 if equal, -1 otherwise.
+ */
+ return (cmp == 0 ? 0 : -1);
+ } else {
+ /*
+ * Not enough characters in the tvbuff to match the
+ * string.
+ */
+ return -1;
+ }
+}
+
+/*
+ * Check that the tvbuff contains at least size bytes, starting at
+ * offset, and that those bytes are equal to str. Return 0 for success
+ * and -1 for error. This function does not throw an exception.
+ */
+gint
+tvb_memeql(tvbuff_t *tvb, const gint offset, const guint8 *str, size_t size)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous_no_exception(tvb, offset, (gint) size, NULL);
+
+ if (ptr) {
+ int cmp = memcmp(ptr, str, size);
+
+ /*
+ * Return 0 if equal, -1 otherwise.
+ */
+ return (cmp == 0 ? 0 : -1);
+ } else {
+ /*
+ * Not enough characters in the tvbuff to match the
+ * string.
+ */
+ return -1;
+ }
+}
+
+/**
+ * Format the data in the tvb from offset for size. Returned string is
+ * wmem packet_scoped so call must be in that scope.
+ */
+gchar *
+tvb_format_text(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint size)
+{
+ const guint8 *ptr;
+ gint len;
+
+ len = (size > 0) ? size : 0;
+
+ ptr = ensure_contiguous(tvb, offset, size);
+ return format_text(scope, ptr, len);
+}
+
+/*
+ * Format the data in the tvb from offset for length ...
+ */
+gchar *
+tvb_format_text_wsp(wmem_allocator_t* allocator, tvbuff_t *tvb, const gint offset, const gint size)
+{
+ const guint8 *ptr;
+ gint len;
+
+ len = (size > 0) ? size : 0;
+
+ ptr = ensure_contiguous(tvb, offset, size);
+ return format_text_wsp(allocator, ptr, len);
+}
+
+/**
+ * Like "tvb_format_text()", but for null-padded strings; don't show
+ * the null padding characters as "\000". Returned string is wmem packet_scoped
+ * so call must be in that scope.
+ */
+gchar *
+tvb_format_stringzpad(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint size)
+{
+ const guint8 *ptr, *p;
+ gint len;
+ gint stringlen;
+
+ len = (size > 0) ? size : 0;
+
+ ptr = ensure_contiguous(tvb, offset, size);
+ for (p = ptr, stringlen = 0; stringlen < len && *p != '\0'; p++, stringlen++)
+ ;
+ return format_text(scope, ptr, stringlen);
+}
+
+/*
+ * Like "tvb_format_text_wsp()", but for null-padded strings; don't show
+ * the null padding characters as "\000".
+ */
+gchar *
+tvb_format_stringzpad_wsp(wmem_allocator_t* allocator, tvbuff_t *tvb, const gint offset, const gint size)
+{
+ const guint8 *ptr, *p;
+ gint len;
+ gint stringlen;
+
+ len = (size > 0) ? size : 0;
+
+ ptr = ensure_contiguous(tvb, offset, size);
+ for (p = ptr, stringlen = 0; stringlen < len && *p != '\0'; p++, stringlen++)
+ ;
+ return format_text_wsp(allocator, ptr, stringlen);
+}
+
+/*
+ * All string functions below take a scope as an argument.
+ *
+ *
+ * If scope is NULL, memory is allocated with g_malloc() and user must
+ * explicitly free it with g_free().
+ * If scope is not NULL, memory is allocated with the corresponding pool
+ * lifetime.
+ *
+ * All functions throw an exception if the tvbuff ends before the string
+ * does.
+ */
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
+ * of bytes referred to by the tvbuff, offset, and length as an ASCII string,
+ * with all bytes with the high-order bit set being invalid, and return a
+ * pointer to a UTF-8 string, allocated using the wmem scope.
+ *
+ * Octets with the highest bit set will be converted to the Unicode
+ * REPLACEMENT CHARACTER.
+ */
+static guint8 *
+tvb_get_ascii_string(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_ascii_string(scope, ptr, length);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, a length, and a translation table,
+ * treat the string of bytes referred to by the tvbuff, offset, and length
+ * as a string encoded using one octet per character, with octets with the
+ * high-order bit clear being mapped by the translation table to 2-byte
+ * Unicode Basic Multilingual Plane characters (including REPLACEMENT
+ * CHARACTER) and octets with the high-order bit set being mapped to
+ * REPLACEMENT CHARACTER, and return a pointer to a UTF-8 string,
+ * allocated using the wmem scope.
+ *
+ * Octets with the highest bit set will be converted to the Unicode
+ * REPLACEMENT CHARACTER.
+ */
+static guint8 *
+tvb_get_iso_646_string(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length, const gunichar2 table[0x80])
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_iso_646_string(scope, ptr, length, table);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
+ * of bytes referred to by the tvbuff, the offset. and the length as a UTF-8
+ * string, and return a pointer to a UTF-8 string, allocated using the wmem
+ * scope, with all ill-formed sequences replaced with the Unicode REPLACEMENT
+ * CHARACTER according to the recommended "best practices" given in the Unicode
+ * Standard and specified by W3C/WHATWG.
+ *
+ * Note that in conformance with the Unicode Standard, this treats three
+ * byte sequences corresponding to UTF-16 surrogate halves (paired or unpaired)
+ * and two byte overlong encodings of 7-bit ASCII characters as invalid and
+ * substitutes REPLACEMENT CHARACTER for them. Explicit support for nonstandard
+ * derivative encoding formats (e.g. CESU-8, Java Modified UTF-8, WTF-8) could
+ * be added later.
+ */
+static guint8 *
+tvb_get_utf_8_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_utf_8_string(scope, ptr, length);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
+ * of bytes referred to by the tvbuff, the offset, and the length as a
+ * raw string, and return a pointer to that string, allocated using the
+ * wmem scope. This means a null is appended at the end, but no replacement
+ * checking is done otherwise, unlike tvb_get_utf_8_string().
+ *
+ * Also, this one allows a length of -1 to mean get all, but does not
+ * allow a negative offset.
+ */
+static inline guint8 *
+tvb_get_raw_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint length)
+{
+ guint8 *strbuf;
+ gint abs_length = length;
+
+ DISSECTOR_ASSERT(offset >= 0);
+ DISSECTOR_ASSERT(abs_length >= -1);
+
+ if (abs_length < 0)
+ abs_length = tvb->length - offset;
+
+ tvb_ensure_bytes_exist(tvb, offset, abs_length);
+ strbuf = (guint8 *)wmem_alloc(scope, abs_length + 1);
+ tvb_memcpy(tvb, strbuf, offset, abs_length);
+ strbuf[abs_length] = '\0';
+ return strbuf;
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
+ * of bytes referred to by the tvbuff, the offset, and the length as an
+ * ISO 8859/1 string, and return a pointer to a UTF-8 string, allocated
+ * using the wmem scope.
+ */
+static guint8 *
+tvb_get_string_8859_1(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_8859_1_string(scope, ptr, length);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, a length, and a translation
+ * table, treat the string of bytes referred to by the tvbuff, the offset,
+ * and the length as a string encoded using one octet per character, with
+ * octets with the high-order bit clear being ASCII and octets with the
+ * high-order bit set being mapped by the translation table to 2-byte
+ * Unicode Basic Multilingual Plane characters (including REPLACEMENT
+ * CHARACTER), and return a pointer to a UTF-8 string, allocated with the
+ * wmem scope.
+ */
+static guint8 *
+tvb_get_string_unichar2(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length, const gunichar2 table[0x80])
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_unichar2_string(scope, ptr, length, table);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, a length, and an encoding
+ * giving the byte order, treat the string of bytes referred to by the
+ * tvbuff, the offset, and the length as a UCS-2 encoded string in
+ * the byte order in question, containing characters from the Basic
+ * Multilingual Plane (plane 0) of Unicode, and return a pointer to a
+ * UTF-8 string, allocated with the wmem scope.
+ *
+ * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN.
+ *
+ * Specify length in bytes.
+ *
+ * XXX - should map lead and trail surrogate values to REPLACEMENT
+ * CHARACTERs (0xFFFD)?
+ * XXX - if there are an odd number of bytes, should put a
+ * REPLACEMENT CHARACTER at the end.
+ */
+static guint8 *
+tvb_get_ucs_2_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint length, const guint encoding)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_ucs_2_string(scope, ptr, length, encoding);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, a length, and an encoding
+ * giving the byte order, treat the string of bytes referred to by the
+ * tvbuff, the offset, and the length as a UTF-16 encoded string in
+ * the byte order in question, and return a pointer to a UTF-8 string,
+ * allocated with the wmem scope.
+ *
+ * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN.
+ *
+ * Specify length in bytes.
+ *
+ * XXX - should map surrogate errors to REPLACEMENT CHARACTERs (0xFFFD).
+ * XXX - should map code points > 10FFFF to REPLACEMENT CHARACTERs.
+ * XXX - if there are an odd number of bytes, should put a
+ * REPLACEMENT CHARACTER at the end.
+ */
+static guint8 *
+tvb_get_utf_16_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint length, const guint encoding)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_utf_16_string(scope, ptr, length, encoding);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, a length, and an encoding
+ * giving the byte order, treat the string of bytes referred to by the
+ * tvbuff, the offset, and the length as a UCS-4 encoded string in
+ * the byte order in question, and return a pointer to a UTF-8 string,
+ * allocated with the wmem scope.
+ *
+ * Encoding parameter should be ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN
+ *
+ * Specify length in bytes
+ *
+ * XXX - should map lead and trail surrogate values to a "substitute"
+ * UTF-8 character?
+ * XXX - should map code points > 10FFFF to REPLACEMENT CHARACTERs.
+ * XXX - if the number of bytes isn't a multiple of 4, should put a
+ * REPLACEMENT CHARACTER at the end.
+ */
+static gchar *
+tvb_get_ucs_4_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint length, const guint encoding)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_ucs_4_string(scope, ptr, length, encoding);
+}
+
+gchar *
+tvb_get_ts_23_038_7bits_string_packed(wmem_allocator_t *scope, tvbuff_t *tvb,
+ const gint bit_offset, gint no_of_chars)
+{
+ gint in_offset = bit_offset >> 3; /* Current pointer to the input buffer */
+ gint length = ((no_of_chars + 1) * 7 + (bit_offset & 0x07)) >> 3;
+ const guint8 *ptr;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ ptr = ensure_contiguous(tvb, in_offset, length);
+ return get_ts_23_038_7bits_string_packed(scope, ptr, bit_offset, no_of_chars);
+}
+
+gchar *
+tvb_get_ts_23_038_7bits_string_unpacked(wmem_allocator_t *scope, tvbuff_t *tvb,
+ const gint offset, gint length)
+{
+ const guint8 *ptr;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_ts_23_038_7bits_string_unpacked(scope, ptr, length);
+}
+
+gchar *
+tvb_get_etsi_ts_102_221_annex_a_string(wmem_allocator_t *scope, tvbuff_t *tvb,
+ const gint offset, gint length)
+{
+ const guint8 *ptr;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_etsi_ts_102_221_annex_a_string(scope, ptr, length);
+}
+
+gchar *
+tvb_get_ascii_7bits_string(wmem_allocator_t *scope, tvbuff_t *tvb,
+ const gint bit_offset, gint no_of_chars)
+{
+ gint in_offset = bit_offset >> 3; /* Current pointer to the input buffer */
+ gint length = ((no_of_chars + 1) * 7 + (bit_offset & 0x07)) >> 3;
+ const guint8 *ptr;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ ptr = ensure_contiguous(tvb, in_offset, length);
+ return get_ascii_7bits_string(scope, ptr, bit_offset, no_of_chars);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, a length, and a translation
+ * table, treat the string of bytes referred to by the tvbuff, the offset,
+ * and the length as a string encoded using one octet per character, with
+ * octets being mapped by the translation table to 2-byte Unicode Basic
+ * Multilingual Plane characters (including REPLACEMENT CHARACTER), and
+ * return a pointer to a UTF-8 string, allocated with the wmem scope.
+ */
+static guint8 *
+tvb_get_nonascii_unichar2_string(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length, const gunichar2 table[256])
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_nonascii_unichar2_string(scope, ptr, length, table);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, and a length, treat the bytes
+ * referred to by the tvbuff, offset, and length as a GB18030 encoded string,
+ * and return a pointer to a UTF-8 string, allocated with the wmem scope,
+ * converted having substituted REPLACEMENT CHARACTER according to the
+ * Unicode Standard 5.22 U+FFFD Substitution for Conversion.
+ * ( https://www.unicode.org/versions/Unicode13.0.0/ch05.pdf )
+ *
+ * As expected, this will also decode GBK and GB2312 strings.
+ */
+static guint8 *
+tvb_get_gb18030_string(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_gb18030_string(scope, ptr, length);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, and a length, treat the bytes
+ * referred to by the tvbuff, offset, and length as a EUC-KR encoded string,
+ * and return a pointer to a UTF-8 string, allocated with the wmem scope,
+ * converted having substituted REPLACEMENT CHARACTER according to the
+ * Unicode Standard 5.22 U+FFFD Substitution for Conversion.
+ * ( https://www.unicode.org/versions/Unicode13.0.0/ch05.pdf )
+ */
+static guint8 *
+tvb_get_euc_kr_string(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_euc_kr_string(scope, ptr, length);
+}
+
+static guint8 *
+tvb_get_t61_string(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_t61_string(scope, ptr, length);
+}
+
+/*
+ * Encoding tables for BCD strings.
+ */
+static const dgt_set_t Dgt0_9_bcd = {
+ {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ '0','1','2','3','4','5','6','7','8','9','?','?','?','?','?','?'
+ }
+};
+
+static const dgt_set_t Dgt_keypad_abc_tbcd = {
+ {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ '0','1','2','3','4','5','6','7','8','9','*','#','a','b','c','?'
+ }
+};
+
+static const dgt_set_t Dgt_ansi_tbcd = {
+ {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ '0','1','2','3','4','5','6','7','8','9','?','B','C','*','#','?'
+ }
+};
+
+static const dgt_set_t Dgt_dect_standard_4bits_tbcd = {
+ {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ '0','1','2','3','4','5','6','7','8','9','?',' ','?','?','?','?'
+ }
+};
+
+static guint8 *
+tvb_get_apn_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
+ gint length)
+{
+ wmem_strbuf_t *str;
+
+ /*
+ * This is a domain name.
+ *
+ * 3GPP TS 23.003, section 19.4.2 "Fully Qualified Domain Names
+ * (FQDNs)", subsection 19.4.2.1 "General", says:
+ *
+ * The encoding of any identifier used as part of a Fully
+ * Qualifed Domain Name (FQDN) shall follow the Name Syntax
+ * defined in IETF RFC 2181 [18], IETF RFC 1035 [19] and
+ * IETF RFC 1123 [20]. An FQDN consists of one or more
+ * labels. Each label is coded as a one octet length field
+ * followed by that number of octets coded as 8 bit ASCII
+ * characters.
+ *
+ * so this does not appear to use full-blown DNS compression -
+ * the upper 2 bits of the length don't indicate that it's a
+ * pointer or an extended label (RFC 2673).
+ */
+ str = wmem_strbuf_new_sized(scope, length + 1);
+ if (length > 0) {
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+
+ for (;;) {
+ guint label_len;
+
+ /*
+ * Process this label.
+ */
+ label_len = *ptr;
+ ptr++;
+ length--;
+
+ while (label_len != 0) {
+ guint8 ch;
+
+ if (length == 0)
+ goto end;
+
+ ch = *ptr;
+ if (ch < 0x80)
+ wmem_strbuf_append_c(str, ch);
+ else
+ wmem_strbuf_append_unichar_repl(str);
+ ptr++;
+ label_len--;
+ length--;
+ }
+
+ if (length == 0)
+ goto end;
+
+ wmem_strbuf_append_c(str, '.');
+ }
+ }
+
+end:
+ return (guint8 *) wmem_strbuf_finalize(str);
+}
+
+static guint8 *
+tvb_get_dect_standard_8bits_string(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint length)
+{
+ const guint8 *ptr;
+
+ ptr = ensure_contiguous(tvb, offset, length);
+ return get_dect_standard_8bits_string(scope, ptr, length);
+}
+
+/*
+ * Given a tvbuff, an offset, a length, and an encoding, allocate a
+ * buffer big enough to hold a non-null-terminated string of that length
+ * at that offset, plus a trailing '\0', copy into the buffer the
+ * string as converted from the appropriate encoding to UTF-8, and
+ * return a pointer to the string.
+ */
+guint8 *
+tvb_get_string_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
+ const gint length, const guint encoding)
+{
+ guint8 *strptr;
+ gboolean odd, skip_first;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ /* make sure length = -1 fails */
+ if (length < 0) {
+ THROW(ReportedBoundsError);
+ }
+
+ switch (encoding & ENC_CHARENCODING_MASK) {
+
+ case ENC_ASCII:
+ default:
+ /*
+ * For now, we treat bogus values as meaning
+ * "ASCII" rather than reporting an error,
+ * for the benefit of old dissectors written
+ * when the last argument to proto_tree_add_item()
+ * was a gboolean for the byte order, not an
+ * encoding value, and passed non-zero values
+ * other than TRUE to mean "little-endian".
+ */
+ strptr = tvb_get_ascii_string(scope, tvb, offset, length);
+ break;
+
+ case ENC_UTF_8:
+ strptr = tvb_get_utf_8_string(scope, tvb, offset, length);
+ break;
+
+ case ENC_UTF_16:
+ strptr = tvb_get_utf_16_string(scope, tvb, offset, length,
+ encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
+ break;
+
+ case ENC_UCS_2:
+ strptr = tvb_get_ucs_2_string(scope, tvb, offset, length,
+ encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
+ break;
+
+ case ENC_UCS_4:
+ strptr = tvb_get_ucs_4_string(scope, tvb, offset, length,
+ encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
+ break;
+
+ case ENC_ISO_8859_1:
+ /*
+ * ISO 8859-1 printable code point values are equal
+ * to the equivalent Unicode code point value, so
+ * no translation table is needed.
+ */
+ strptr = tvb_get_string_8859_1(scope, tvb, offset, length);
+ break;
+
+ case ENC_ISO_8859_2:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_2);
+ break;
+
+ case ENC_ISO_8859_3:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_3);
+ break;
+
+ case ENC_ISO_8859_4:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_4);
+ break;
+
+ case ENC_ISO_8859_5:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_5);
+ break;
+
+ case ENC_ISO_8859_6:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_6);
+ break;
+
+ case ENC_ISO_8859_7:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_7);
+ break;
+
+ case ENC_ISO_8859_8:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_8);
+ break;
+
+ case ENC_ISO_8859_9:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_9);
+ break;
+
+ case ENC_ISO_8859_10:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_10);
+ break;
+
+ case ENC_ISO_8859_11:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_11);
+ break;
+
+ case ENC_ISO_8859_13:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_13);
+ break;
+
+ case ENC_ISO_8859_14:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_14);
+ break;
+
+ case ENC_ISO_8859_15:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_15);
+ break;
+
+ case ENC_ISO_8859_16:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_16);
+ break;
+
+ case ENC_WINDOWS_1250:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp1250);
+ break;
+
+ case ENC_WINDOWS_1251:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp1251);
+ break;
+
+ case ENC_WINDOWS_1252:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp1252);
+ break;
+
+ case ENC_MAC_ROMAN:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_mac_roman);
+ break;
+
+ case ENC_CP437:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp437);
+ break;
+
+ case ENC_CP855:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp855);
+ break;
+
+ case ENC_CP866:
+ strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp866);
+ break;
+
+ case ENC_ISO_646_BASIC:
+ strptr = tvb_get_iso_646_string(scope, tvb, offset, length, charset_table_iso_646_basic);
+ break;
+
+ case ENC_3GPP_TS_23_038_7BITS_PACKED:
+ {
+ gint bit_offset = offset << 3;
+ gint no_of_chars = (length << 3) / 7;
+ strptr = tvb_get_ts_23_038_7bits_string_packed(scope, tvb, bit_offset, no_of_chars);
+ }
+ break;
+
+ case ENC_ASCII_7BITS:
+ {
+ gint bit_offset = offset << 3;
+ gint no_of_chars = (length << 3) / 7;
+ strptr = tvb_get_ascii_7bits_string(scope, tvb, bit_offset, no_of_chars);
+ }
+ break;
+
+ case ENC_EBCDIC:
+ /*
+ * "Common" EBCDIC, covering all characters with the
+ * same code point in all Roman-alphabet EBCDIC code
+ * pages.
+ */
+ strptr = tvb_get_nonascii_unichar2_string(scope, tvb, offset, length, charset_table_ebcdic);
+ break;
+
+ case ENC_EBCDIC_CP037:
+ /*
+ * EBCDIC code page 037.
+ */
+ strptr = tvb_get_nonascii_unichar2_string(scope, tvb, offset, length, charset_table_ebcdic_cp037);
+ break;
+
+ case ENC_EBCDIC_CP500:
+ /*
+ * EBCDIC code page 500.
+ */
+ strptr = tvb_get_nonascii_unichar2_string(scope, tvb, offset, length, charset_table_ebcdic_cp500);
+ break;
+
+ case ENC_T61:
+ strptr = tvb_get_t61_string(scope, tvb, offset, length);
+ break;
+
+ case ENC_BCD_DIGITS_0_9:
+ /*
+ * Packed BCD, with digits 0-9.
+ */
+ odd = (encoding & ENC_BCD_ODD_NUM_DIG) >> 16;
+ skip_first = (encoding & ENC_BCD_SKIP_FIRST) >> 17;
+ strptr = tvb_get_bcd_string(scope, tvb, offset, length, &Dgt0_9_bcd, skip_first, odd, FALSE);
+ break;
+
+ case ENC_KEYPAD_ABC_TBCD:
+ /*
+ * Keypad-with-a/b/c "telephony BCD" - packed BCD, with
+ * digits 0-9 and symbols *, #, a, b, and c.
+ */
+ odd = (encoding & ENC_BCD_ODD_NUM_DIG) >> 16;
+ skip_first = (encoding & ENC_BCD_SKIP_FIRST) >> 17;
+ strptr = tvb_get_bcd_string(scope, tvb, offset, length, &Dgt_keypad_abc_tbcd, skip_first, odd, FALSE);
+ break;
+
+ case ENC_KEYPAD_BC_TBCD:
+ /*
+ * Keypad-with-B/C "telephony BCD" - packed BCD, with
+ * digits 0-9 and symbols B, C, *, and #.
+ */
+ odd = (encoding & ENC_BCD_ODD_NUM_DIG) >> 16;
+ skip_first = (encoding & ENC_BCD_SKIP_FIRST) >> 17;
+ strptr = tvb_get_bcd_string(scope, tvb, offset, length, &Dgt_ansi_tbcd, skip_first, odd, FALSE);
+ break;
+
+ case ENC_3GPP_TS_23_038_7BITS_UNPACKED:
+ strptr = tvb_get_ts_23_038_7bits_string_unpacked(scope, tvb, offset, length);
+ break;
+
+ case ENC_ETSI_TS_102_221_ANNEX_A:
+ strptr = tvb_get_etsi_ts_102_221_annex_a_string(scope, tvb, offset, length);
+ break;
+
+ case ENC_GB18030:
+ strptr = tvb_get_gb18030_string(scope, tvb, offset, length);
+ break;
+
+ case ENC_EUC_KR:
+ strptr = tvb_get_euc_kr_string(scope, tvb, offset, length);
+ break;
+
+ case ENC_APN_STR:
+ strptr = tvb_get_apn_string(scope, tvb, offset, length);
+ break;
+
+ case ENC_DECT_STANDARD_8BITS:
+ strptr = tvb_get_dect_standard_8bits_string(scope, tvb, offset, length);
+ break;
+
+ case ENC_DECT_STANDARD_4BITS_TBCD:
+ /*
+ * DECT standard 4bits "telephony BCD" - packed BCD, with
+ * digits 0-9 and symbol SPACE for 0xb.
+ */
+ odd = (encoding & ENC_BCD_ODD_NUM_DIG) >> 16;
+ skip_first = (encoding & ENC_BCD_SKIP_FIRST) >> 17;
+ strptr = tvb_get_bcd_string(scope, tvb, offset, length, &Dgt_dect_standard_4bits_tbcd, skip_first, odd, FALSE);
+ break;
+ }
+ return strptr;
+}
+
+/*
+ * This is like tvb_get_string_enc(), except that it handles null-padded
+ * strings.
+ *
+ * Currently, string values are stored as UTF-8 null-terminated strings,
+ * so nothing needs to be done differently for null-padded strings; we
+ * could save a little memory by not storing the null padding.
+ *
+ * If we ever store string values differently, in a fashion that doesn't
+ * involve null termination, that might change.
+ */
+guint8 *
+tvb_get_stringzpad(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset,
+ const gint length, const guint encoding)
+{
+ return tvb_get_string_enc(scope, tvb, offset, length, encoding);
+}
+
+/*
+ * These routines are like the above routines, except that they handle
+ * null-terminated strings. They find the length of that string (and
+ * throw an exception if the tvbuff ends before we find the null), and
+ * also return through a pointer the length of the string, in bytes,
+ * including the terminating null (the terminating null being 2 bytes
+ * for UCS-2 and UTF-16, 4 bytes for UCS-4, and 1 byte for other
+ * encodings).
+ */
+static guint8 *
+tvb_get_ascii_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp)
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_ascii_string(scope, ptr, size);
+}
+
+static guint8 *
+tvb_get_iso_646_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp, const gunichar2 table[0x80])
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_iso_646_string(scope, ptr, size, table);
+}
+
+static guint8 *
+tvb_get_utf_8_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp)
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_utf_8_string(scope, ptr, size);
+}
+
+static guint8 *
+tvb_get_stringz_8859_1(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp)
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_8859_1_string(scope, ptr, size);
+}
+
+static guint8 *
+tvb_get_stringz_unichar2(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp, const gunichar2 table[0x80])
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_unichar2_string(scope, ptr, size, table);
+}
+
+/*
+ * Given a tvbuff and an offset, with the offset assumed to refer to
+ * a null-terminated string, find the length of that string (and throw
+ * an exception if the tvbuff ends before we find the null), ensure that
+ * the TVB is flat, and return a pointer to the string (in the TVB).
+ * Also return the length of the string (including the terminating null)
+ * through a pointer.
+ *
+ * As long as we aren't using composite TVBs, this saves the cycles used
+ * (often unnecessariliy) in allocating a buffer and copying the string into
+ * it. (If we do start using composite TVBs, we may want to replace this
+ * function with the _ephemeral version.)
+ */
+const guint8 *
+tvb_get_const_stringz(tvbuff_t *tvb, const gint offset, gint *lengthp)
+{
+ guint size;
+ const guint8 *strptr;
+
+ size = tvb_strsize(tvb, offset);
+ strptr = ensure_contiguous(tvb, offset, size);
+ if (lengthp)
+ *lengthp = size;
+ return strptr;
+}
+
+static gchar *
+tvb_get_ucs_2_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp, const guint encoding)
+{
+ gint size; /* Number of bytes in string */
+ const guint8 *ptr;
+
+ size = tvb_unicode_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_ucs_2_string(scope, ptr, size, encoding);
+}
+
+static gchar *
+tvb_get_utf_16_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp, const guint encoding)
+{
+ gint size;
+ const guint8 *ptr;
+
+ size = tvb_unicode_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_utf_16_string(scope, ptr, size, encoding);
+}
+
+static gchar *
+tvb_get_ucs_4_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp, const guint encoding)
+{
+ gint size;
+ gunichar uchar;
+ const guint8 *ptr;
+
+ size = 0;
+ do {
+ /* Endianness doesn't matter when looking for null */
+ uchar = tvb_get_ntohl(tvb, offset + size);
+ size += 4;
+ } while(uchar != 0);
+
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_ucs_4_string(scope, ptr, size, encoding);
+}
+
+static guint8 *
+tvb_get_nonascii_unichar2_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp, const gunichar2 table[256])
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_nonascii_unichar2_string(scope, ptr, size, table);
+}
+
+static guint8 *
+tvb_get_t61_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp)
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_t61_string(scope, ptr, size);
+}
+
+static guint8 *
+tvb_get_gb18030_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp)
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_gb18030_string(scope, ptr, size);
+}
+
+static guint8 *
+tvb_get_euc_kr_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp)
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_euc_kr_string(scope, ptr, size);
+}
+
+static guint8 *
+tvb_get_dect_standard_8bits_stringz(wmem_allocator_t *scope, tvbuff_t *tvb, gint offset, gint *lengthp)
+{
+ guint size;
+ const guint8 *ptr;
+
+ size = tvb_strsize(tvb, offset);
+ ptr = ensure_contiguous(tvb, offset, size);
+ /* XXX, conversion between signed/unsigned integer */
+ if (lengthp)
+ *lengthp = size;
+ return get_t61_string(scope, ptr, size);
+}
+
+guint8 *
+tvb_get_stringz_enc(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint *lengthp, const guint encoding)
+{
+ guint8 *strptr;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ switch (encoding & ENC_CHARENCODING_MASK) {
+
+ case ENC_ASCII:
+ default:
+ /*
+ * For now, we treat bogus values as meaning
+ * "ASCII" rather than reporting an error,
+ * for the benefit of old dissectors written
+ * when the last argument to proto_tree_add_item()
+ * was a gboolean for the byte order, not an
+ * encoding value, and passed non-zero values
+ * other than TRUE to mean "little-endian".
+ */
+ strptr = tvb_get_ascii_stringz(scope, tvb, offset, lengthp);
+ break;
+
+ case ENC_UTF_8:
+ /*
+ * XXX - should map all invalid UTF-8 sequences
+ * to a "substitute" UTF-8 character.
+ * XXX - should map code points > 10FFFF to REPLACEMENT
+ * CHARACTERs.
+ */
+ strptr = tvb_get_utf_8_stringz(scope, tvb, offset, lengthp);
+ break;
+
+ case ENC_UTF_16:
+ strptr = tvb_get_utf_16_stringz(scope, tvb, offset, lengthp,
+ encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
+ break;
+
+ case ENC_UCS_2:
+ strptr = tvb_get_ucs_2_stringz(scope, tvb, offset, lengthp,
+ encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
+ break;
+
+ case ENC_UCS_4:
+ strptr = tvb_get_ucs_4_stringz(scope, tvb, offset, lengthp,
+ encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
+ break;
+
+ case ENC_ISO_8859_1:
+ /*
+ * ISO 8859-1 printable code point values are equal
+ * to the equivalent Unicode code point value, so
+ * no translation table is needed.
+ */
+ strptr = tvb_get_stringz_8859_1(scope, tvb, offset, lengthp);
+ break;
+
+ case ENC_ISO_8859_2:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_2);
+ break;
+
+ case ENC_ISO_8859_3:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_3);
+ break;
+
+ case ENC_ISO_8859_4:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_4);
+ break;
+
+ case ENC_ISO_8859_5:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_5);
+ break;
+
+ case ENC_ISO_8859_6:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_6);
+ break;
+
+ case ENC_ISO_8859_7:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_7);
+ break;
+
+ case ENC_ISO_8859_8:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_8);
+ break;
+
+ case ENC_ISO_8859_9:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_9);
+ break;
+
+ case ENC_ISO_8859_10:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_10);
+ break;
+
+ case ENC_ISO_8859_11:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_11);
+ break;
+
+ case ENC_ISO_8859_13:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_13);
+ break;
+
+ case ENC_ISO_8859_14:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_14);
+ break;
+
+ case ENC_ISO_8859_15:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_15);
+ break;
+
+ case ENC_ISO_8859_16:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_16);
+ break;
+
+ case ENC_WINDOWS_1250:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp1250);
+ break;
+
+ case ENC_WINDOWS_1251:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp1251);
+ break;
+
+ case ENC_WINDOWS_1252:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp1252);
+ break;
+
+ case ENC_MAC_ROMAN:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_mac_roman);
+ break;
+
+ case ENC_CP437:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp437);
+ break;
+
+ case ENC_CP855:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp855);
+ break;
+
+ case ENC_CP866:
+ strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp866);
+ break;
+
+ case ENC_ISO_646_BASIC:
+ strptr = tvb_get_iso_646_stringz(scope, tvb, offset, lengthp, charset_table_iso_646_basic);
+ break;
+
+ case ENC_3GPP_TS_23_038_7BITS_PACKED:
+ case ENC_3GPP_TS_23_038_7BITS_UNPACKED:
+ case ENC_ETSI_TS_102_221_ANNEX_A:
+ REPORT_DISSECTOR_BUG("TS 23.038 7bits has no null character and doesn't support null-terminated strings");
+ break;
+
+ case ENC_ASCII_7BITS:
+ REPORT_DISSECTOR_BUG("tvb_get_stringz_enc function with ENC_ASCII_7BITS not implemented yet");
+ break;
+
+ case ENC_EBCDIC:
+ /*
+ * "Common" EBCDIC, covering all characters with the
+ * same code point in all Roman-alphabet EBCDIC code
+ * pages.
+ */
+ strptr = tvb_get_nonascii_unichar2_stringz(scope, tvb, offset, lengthp, charset_table_ebcdic);
+ break;
+
+ case ENC_EBCDIC_CP037:
+ /*
+ * EBCDIC code page 037.
+ */
+ strptr = tvb_get_nonascii_unichar2_stringz(scope, tvb, offset, lengthp, charset_table_ebcdic_cp037);
+ break;
+
+ case ENC_EBCDIC_CP500:
+ /*
+ * EBCDIC code page 500.
+ */
+ strptr = tvb_get_nonascii_unichar2_stringz(scope, tvb, offset, lengthp, charset_table_ebcdic_cp500);
+ break;
+
+ case ENC_T61:
+ strptr = tvb_get_t61_stringz(scope, tvb, offset, lengthp);
+ break;
+
+ case ENC_GB18030:
+ strptr = tvb_get_gb18030_stringz(scope, tvb, offset, lengthp);
+ break;
+
+ case ENC_EUC_KR:
+ strptr = tvb_get_euc_kr_stringz(scope, tvb, offset, lengthp);
+ break;
+
+ case ENC_DECT_STANDARD_8BITS:
+ strptr = tvb_get_dect_standard_8bits_stringz(scope, tvb, offset, lengthp);
+ break;
+ }
+
+ return strptr;
+}
+
+/* Looks for a stringz (NUL-terminated string) in tvbuff and copies
+ * no more than bufsize number of bytes, including terminating NUL, to buffer.
+ * Returns length of string (not including terminating NUL), or -1 if the string was
+ * truncated in the buffer due to not having reached the terminating NUL.
+ * In this way, it acts like snprintf().
+ *
+ * bufsize MUST be greater than 0.
+ *
+ * When processing a packet where the remaining number of bytes is less
+ * than bufsize, an exception is not thrown if the end of the packet
+ * is reached before the NUL is found. If no NUL is found before reaching
+ * the end of the short packet, -1 is still returned, and the string
+ * is truncated with a NUL, albeit not at buffer[bufsize - 1], but
+ * at the correct spot, terminating the string.
+ *
+ * *bytes_copied will contain the number of bytes actually copied,
+ * including the terminating-NUL.
+ */
+static gint
+_tvb_get_raw_bytes_as_stringz(tvbuff_t *tvb, const gint offset, const guint bufsize, guint8* buffer, gint *bytes_copied)
+{
+ gint stringlen;
+ guint abs_offset = 0;
+ gint limit, len = 0;
+ gboolean decreased_max = FALSE;
+
+ /* Only read to end of tvbuff, w/o throwing exception. */
+ check_offset_length(tvb, offset, -1, &abs_offset, &len);
+
+ /* There must at least be room for the terminating NUL. */
+ DISSECTOR_ASSERT(bufsize != 0);
+
+ /* If there's no room for anything else, just return the NUL. */
+ if (bufsize == 1) {
+ buffer[0] = 0;
+ *bytes_copied = 1;
+ return 0;
+ }
+
+ /* check_offset_length() won't throw an exception if we're
+ * looking at the byte immediately after the end of the tvbuff. */
+ if (len == 0) {
+ THROW(ReportedBoundsError);
+ }
+
+ /* This should not happen because check_offset_length() would
+ * have already thrown an exception if 'offset' were out-of-bounds.
+ */
+ DISSECTOR_ASSERT(len != -1);
+
+ /*
+ * If we've been passed a negative number, bufsize will
+ * be huge.
+ */
+ DISSECTOR_ASSERT(bufsize <= G_MAXINT);
+
+ if ((guint)len < bufsize) {
+ limit = len;
+ decreased_max = TRUE;
+ }
+ else {
+ limit = bufsize;
+ }
+
+ stringlen = tvb_strnlen(tvb, abs_offset, limit - 1);
+ /* If NUL wasn't found, copy the data and return -1 */
+ if (stringlen == -1) {
+ tvb_memcpy(tvb, buffer, abs_offset, limit);
+ if (decreased_max) {
+ buffer[limit] = 0;
+ /* Add 1 for the extra NUL that we set at buffer[limit],
+ * pretending that it was copied as part of the string. */
+ *bytes_copied = limit + 1;
+ }
+ else {
+ *bytes_copied = limit;
+ }
+ return -1;
+ }
+
+ /* Copy the string to buffer */
+ tvb_memcpy(tvb, buffer, abs_offset, stringlen + 1);
+ *bytes_copied = stringlen + 1;
+ return stringlen;
+}
+
+gint
+tvb_get_raw_bytes_as_stringz(tvbuff_t *tvb, const gint offset, const guint bufsize, guint8* buffer)
+{
+ gint len, bytes_copied;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ len = _tvb_get_raw_bytes_as_stringz(tvb, offset, bufsize, buffer, &bytes_copied);
+
+ if (len == -1) {
+ buffer[bufsize - 1] = 0;
+ return bytes_copied - 1;
+ }
+ else {
+ return len;
+ }
+}
+
+/*
+ * Given a tvbuff, an offset into the tvbuff, a buffer, and a buffer size,
+ * extract as many raw bytes from the tvbuff, starting at the offset,
+ * as 1) are available in the tvbuff and 2) will fit in the buffer, leaving
+ * room for a terminating NUL.
+ */
+gint
+tvb_get_raw_bytes_as_string(tvbuff_t *tvb, const gint offset, char *buffer, size_t bufsize)
+{
+ gint len = 0;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ /* There must be room for the string and the terminating NUL. */
+ DISSECTOR_ASSERT(bufsize > 0);
+
+ DISSECTOR_ASSERT(bufsize - 1 < G_MAXINT);
+
+ len = tvb_captured_length_remaining(tvb, offset);
+ if (len <= 0) {
+ buffer[0] = '\0';
+ return 0;
+ }
+ if (len > (gint)(bufsize - 1))
+ len = (gint)(bufsize - 1);
+
+ /* Copy the string to buffer */
+ tvb_memcpy(tvb, buffer, offset, len);
+ buffer[len] = '\0';
+ return len;
+}
+
+gboolean tvb_ascii_isprint(tvbuff_t *tvb, const gint offset, const gint length)
+{
+ const guint8* buf = tvb_get_ptr(tvb, offset, length);
+ guint abs_offset, abs_length = length;
+
+ if (length == -1) {
+ /* tvb_get_ptr has already checked for exceptions. */
+ compute_offset_and_remaining(tvb, offset, &abs_offset, &abs_length);
+ }
+ for (guint i = 0; i < abs_length; i++, buf++)
+ if (!g_ascii_isprint(*buf))
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean tvb_utf_8_isprint(tvbuff_t *tvb, const gint offset, const gint length)
+{
+ const guint8* buf = tvb_get_ptr(tvb, offset, length);
+ guint abs_offset, abs_length = length;
+
+ if (length == -1) {
+ /* tvb_get_ptr has already checked for exceptions. */
+ compute_offset_and_remaining(tvb, offset, &abs_offset, &abs_length);
+ }
+
+ return isprint_utf8_string(buf, abs_length);
+}
+
+gboolean tvb_ascii_isdigit(tvbuff_t *tvb, const gint offset, const gint length)
+{
+ const guint8* buf = tvb_get_ptr(tvb, offset, length);
+ guint abs_offset, abs_length = length;
+
+ if (length == -1) {
+ /* tvb_get_ptr has already checked for exceptions. */
+ compute_offset_and_remaining(tvb, offset, &abs_offset, &abs_length);
+ }
+ for (guint i = 0; i < abs_length; i++, buf++)
+ if (!g_ascii_isdigit(*buf))
+ return FALSE;
+
+ return TRUE;
+}
+
+static ws_mempbrk_pattern pbrk_crlf;
+/*
+ * Given a tvbuff, an offset into the tvbuff, and a length that starts
+ * at that offset (which may be -1 for "all the way to the end of the
+ * tvbuff"), find the end of the (putative) line that starts at the
+ * specified offset in the tvbuff, going no further than the specified
+ * length.
+ *
+ * Return the length of the line (not counting the line terminator at
+ * the end), or, if we don't find a line terminator:
+ *
+ * if "desegment" is true, return -1;
+ *
+ * if "desegment" is false, return the amount of data remaining in
+ * the buffer.
+ *
+ * If "next_offset" is not NULL, set "*next_offset" to the offset of the
+ * character past the line terminator, or past the end of the buffer if
+ * we don't find a line terminator. (It's not set if we return -1.)
+ */
+gint
+tvb_find_line_end(tvbuff_t *tvb, const gint offset, int len, gint *next_offset, const gboolean desegment)
+{
+ gint eob_offset;
+ gint eol_offset;
+ int linelen;
+ guchar found_needle = 0;
+ static gboolean compiled = FALSE;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ if (len == -1) {
+ len = _tvb_captured_length_remaining(tvb, offset);
+ /* if offset is past the end of the tvbuff, len is now 0 */
+ }
+
+ eob_offset = offset + len;
+
+ if (!compiled) {
+ ws_mempbrk_compile(&pbrk_crlf, "\r\n");
+ compiled = TRUE;
+ }
+
+ /*
+ * Look either for a CR or an LF.
+ */
+ eol_offset = tvb_ws_mempbrk_pattern_guint8(tvb, offset, len, &pbrk_crlf, &found_needle);
+ if (eol_offset == -1) {
+ /*
+ * No CR or LF - line is presumably continued in next packet.
+ */
+ if (desegment) {
+ /*
+ * Tell our caller we saw no EOL, so they can
+ * try to desegment and get the entire line
+ * into one tvbuff.
+ */
+ return -1;
+ } else {
+ /*
+ * Pretend the line runs to the end of the tvbuff.
+ */
+ linelen = eob_offset - offset;
+ if (next_offset)
+ *next_offset = eob_offset;
+ }
+ } else {
+ /*
+ * Find the number of bytes between the starting offset
+ * and the CR or LF.
+ */
+ linelen = eol_offset - offset;
+
+ /*
+ * Is it a CR?
+ */
+ if (found_needle == '\r') {
+ /*
+ * Yes - is it followed by an LF?
+ */
+ if (eol_offset + 1 >= eob_offset) {
+ /*
+ * Dunno - the next byte isn't in this
+ * tvbuff.
+ */
+ if (desegment) {
+ /*
+ * We'll return -1, although that
+ * runs the risk that if the line
+ * really *is* terminated with a CR,
+ * we won't properly dissect this
+ * tvbuff.
+ *
+ * It's probably more likely that
+ * the line ends with CR-LF than
+ * that it ends with CR by itself.
+ */
+ return -1;
+ }
+ } else {
+ /*
+ * Well, we can at least look at the next
+ * byte.
+ */
+ if (tvb_get_guint8(tvb, eol_offset + 1) == '\n') {
+ /*
+ * It's an LF; skip over the CR.
+ */
+ eol_offset++;
+ }
+ }
+ }
+
+ /*
+ * Return the offset of the character after the last
+ * character in the line, skipping over the last character
+ * in the line terminator.
+ */
+ if (next_offset)
+ *next_offset = eol_offset + 1;
+ }
+ return linelen;
+}
+
+static ws_mempbrk_pattern pbrk_crlf_dquote;
+/*
+ * Given a tvbuff, an offset into the tvbuff, and a length that starts
+ * at that offset (which may be -1 for "all the way to the end of the
+ * tvbuff"), find the end of the (putative) line that starts at the
+ * specified offset in the tvbuff, going no further than the specified
+ * length.
+ *
+ * However, treat quoted strings inside the buffer specially - don't
+ * treat newlines in quoted strings as line terminators.
+ *
+ * Return the length of the line (not counting the line terminator at
+ * the end), or the amount of data remaining in the buffer if we don't
+ * find a line terminator.
+ *
+ * If "next_offset" is not NULL, set "*next_offset" to the offset of the
+ * character past the line terminator, or past the end of the buffer if
+ * we don't find a line terminator.
+ */
+gint
+tvb_find_line_end_unquoted(tvbuff_t *tvb, const gint offset, int len, gint *next_offset)
+{
+ gint cur_offset, char_offset;
+ gboolean is_quoted;
+ guchar c = 0;
+ gint eob_offset;
+ int linelen;
+ static gboolean compiled = FALSE;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ if (len == -1)
+ len = _tvb_captured_length_remaining(tvb, offset);
+
+ if (!compiled) {
+ ws_mempbrk_compile(&pbrk_crlf_dquote, "\r\n\"");
+ compiled = TRUE;
+ }
+
+ /*
+ * XXX - what if "len" is still -1, meaning "offset is past the
+ * end of the tvbuff"?
+ */
+ eob_offset = offset + len;
+
+ cur_offset = offset;
+ is_quoted = FALSE;
+ for (;;) {
+ /*
+ * Is this part of the string quoted?
+ */
+ if (is_quoted) {
+ /*
+ * Yes - look only for the terminating quote.
+ */
+ char_offset = tvb_find_guint8(tvb, cur_offset, len,
+ '"');
+ } else {
+ /*
+ * Look either for a CR, an LF, or a '"'.
+ */
+ char_offset = tvb_ws_mempbrk_pattern_guint8(tvb, cur_offset, len, &pbrk_crlf_dquote, &c);
+ }
+ if (char_offset == -1) {
+ /*
+ * Not found - line is presumably continued in
+ * next packet.
+ * We pretend the line runs to the end of the tvbuff.
+ */
+ linelen = eob_offset - offset;
+ if (next_offset)
+ *next_offset = eob_offset;
+ break;
+ }
+
+ if (is_quoted) {
+ /*
+ * We're processing a quoted string.
+ * We only looked for ", so we know it's a ";
+ * as we're processing a quoted string, it's a
+ * closing quote.
+ */
+ is_quoted = FALSE;
+ } else {
+ /*
+ * OK, what is it?
+ */
+ if (c == '"') {
+ /*
+ * Un-quoted "; it begins a quoted
+ * string.
+ */
+ is_quoted = TRUE;
+ } else {
+ /*
+ * It's a CR or LF; we've found a line
+ * terminator.
+ *
+ * Find the number of bytes between the
+ * starting offset and the CR or LF.
+ */
+ linelen = char_offset - offset;
+
+ /*
+ * Is it a CR?
+ */
+ if (c == '\r') {
+ /*
+ * Yes; is it followed by an LF?
+ */
+ if (char_offset + 1 < eob_offset &&
+ tvb_get_guint8(tvb, char_offset + 1)
+ == '\n') {
+ /*
+ * Yes; skip over the CR.
+ */
+ char_offset++;
+ }
+ }
+
+ /*
+ * Return the offset of the character after
+ * the last character in the line, skipping
+ * over the last character in the line
+ * terminator, and quit.
+ */
+ if (next_offset)
+ *next_offset = char_offset + 1;
+ break;
+ }
+ }
+
+ /*
+ * Step past the character we found.
+ */
+ cur_offset = char_offset + 1;
+ if (cur_offset >= eob_offset) {
+ /*
+ * The character we found was the last character
+ * in the tvbuff - line is presumably continued in
+ * next packet.
+ * We pretend the line runs to the end of the tvbuff.
+ */
+ linelen = eob_offset - offset;
+ if (next_offset)
+ *next_offset = eob_offset;
+ break;
+ }
+ }
+ return linelen;
+}
+
+/*
+ * Copied from the mgcp dissector. (This function should be moved to /epan )
+ * tvb_skip_wsp - Returns the position in tvb of the first non-whitespace
+ * character following offset or offset + maxlength -1 whichever
+ * is smaller.
+ *
+ * Parameters:
+ * tvb - The tvbuff in which we are skipping whitespace.
+ * offset - The offset in tvb from which we begin trying to skip whitespace.
+ * maxlength - The maximum distance from offset that we may try to skip
+ * whitespace.
+ *
+ * Returns: The position in tvb of the first non-whitespace
+ * character following offset or offset + maxlength -1 whichever
+ * is smaller.
+ */
+gint
+tvb_skip_wsp(tvbuff_t *tvb, const gint offset, const gint maxlength)
+{
+ gint counter;
+ gint end, tvb_len;
+ guint8 tempchar;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ /* Get the length remaining */
+ /*tvb_len = tvb_captured_length(tvb);*/
+ tvb_len = tvb->length;
+
+ end = offset + maxlength;
+ if (end >= tvb_len)
+ {
+ end = tvb_len;
+ }
+
+ /* Skip past spaces, tabs, CRs and LFs until run out or meet something else */
+ for (counter = offset;
+ counter < end &&
+ ((tempchar = tvb_get_guint8(tvb,counter)) == ' ' ||
+ tempchar == '\t' || tempchar == '\r' || tempchar == '\n');
+ counter++);
+
+ return (counter);
+}
+
+gint
+tvb_skip_wsp_return(tvbuff_t *tvb, const gint offset)
+{
+ gint counter;
+ guint8 tempchar;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ for (counter = offset; counter > 0 &&
+ ((tempchar = tvb_get_guint8(tvb,counter)) == ' ' ||
+ tempchar == '\t' || tempchar == '\n' || tempchar == '\r'); counter--);
+ counter++;
+
+ return (counter);
+}
+
+int
+tvb_skip_guint8(tvbuff_t *tvb, int offset, const int maxlength, const guint8 ch)
+{
+ int end, tvb_len;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ /* Get the length remaining */
+ /*tvb_len = tvb_captured_length(tvb);*/
+ tvb_len = tvb->length;
+
+ end = offset + maxlength;
+ if (end >= tvb_len)
+ end = tvb_len;
+
+ while (offset < end) {
+ guint8 tempch = tvb_get_guint8(tvb, offset);
+
+ if (tempch != ch)
+ break;
+ offset++;
+ }
+
+ return offset;
+}
+
+static ws_mempbrk_pattern pbrk_whitespace;
+
+int tvb_get_token_len(tvbuff_t *tvb, const gint offset, int len, gint *next_offset, const gboolean desegment)
+{
+ gint eob_offset;
+ gint eot_offset;
+ int tokenlen;
+ guchar found_needle = 0;
+ static gboolean compiled = FALSE;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ if (len == -1) {
+ len = _tvb_captured_length_remaining(tvb, offset);
+ /* if offset is past the end of the tvbuff, len is now 0 */
+ }
+
+ eob_offset = offset + len;
+
+ if (!compiled) {
+ ws_mempbrk_compile(&pbrk_whitespace, " \r\n");
+ compiled = TRUE;
+ }
+
+ /*
+ * Look either for a space, CR, or LF.
+ */
+ eot_offset = tvb_ws_mempbrk_pattern_guint8(tvb, offset, len, &pbrk_whitespace, &found_needle);
+ if (eot_offset == -1) {
+ /*
+ * No space, CR or LF - token is presumably continued in next packet.
+ */
+ if (desegment) {
+ /*
+ * Tell our caller we saw no whitespace, so they can
+ * try to desegment and get the entire line
+ * into one tvbuff.
+ */
+ return -1;
+ }
+ else {
+ /*
+ * Pretend the token runs to the end of the tvbuff.
+ */
+ tokenlen = eob_offset - offset;
+ if (next_offset)
+ *next_offset = eob_offset;
+ }
+ }
+ else {
+ /*
+ * Find the number of bytes between the starting offset
+ * and the space, CR or LF.
+ */
+ tokenlen = eot_offset - offset;
+
+ /*
+ * Return the offset of the character after the last
+ * character in the line, skipping over the last character
+ * in the line terminator.
+ */
+ if (next_offset)
+ *next_offset = eot_offset + 1;
+ }
+ return tokenlen;
+}
+
+/*
+ * Format a bunch of data from a tvbuff as bytes, returning a pointer
+ * to the string with the formatted data, with "punct" as a byte
+ * separator.
+ */
+gchar *
+tvb_bytes_to_str_punct(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint len, const gchar punct)
+{
+ DISSECTOR_ASSERT(len > 0);
+ return bytes_to_str_punct(scope, ensure_contiguous(tvb, offset, len), len, punct);
+}
+
+/*
+ * Given a wmem scope, a tvbuff, an offset, a length, an input digit
+ * set, and a boolean indicator, fetch BCD-encoded digits from a
+ * tvbuff starting from either the low or high half byte of the
+ * first byte depending on the boolean indicator (TRUE means "start
+ * with the high half byte, ignoring the low half byte", and FALSE
+ * means "start with the low half byte and proceed to the high half
+ * byte), formating the digits into characters according to the
+ * input digit set, and return a pointer to a UTF-8 string, allocated
+ * using the wmem scope. A high-order nibble of 0xf is considered a
+ * 'filler' and will end the conversion. Similarrily if odd is set the last
+ * high nibble will be omitted.
+ */
+gchar *
+tvb_get_bcd_string(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, gint len, const dgt_set_t *dgt, gboolean skip_first, gboolean odd, gboolean bigendian)
+{
+ const guint8 *ptr;
+ int i = 0;
+ char *digit_str;
+ guint8 octet;
+
+ DISSECTOR_ASSERT(tvb && tvb->initialized);
+
+ if (len == -1) {
+ /*
+ * Run to the end of the captured data.
+ *
+ * XXX - captured, or total?
+ */
+ /*length = tvb_captured_length(tvb);*/
+ len = tvb->length;
+ if (len < offset) {
+ return (char *)"";
+ }
+ len -= offset;
+ }
+
+ ptr = ensure_contiguous(tvb, offset, len);
+
+ /*
+ * XXX - map illegal digits (digits that map to 0) to REPLACEMENT
+ * CHARACTER, and have all the tables in epan/tvbuff.c use 0 rather
+ * than '?'?
+ */
+ digit_str = (char *)wmem_alloc(scope, len*2 + 1);
+
+ while (len > 0) {
+ octet = *ptr;
+ if (!skip_first) {
+ if (bigendian) {
+ digit_str[i] = dgt->out[(octet >> 4) & 0x0f];
+ } else {
+ digit_str[i] = dgt->out[octet & 0x0f];
+ }
+ i++;
+ }
+ skip_first = FALSE;
+
+ /*
+ * unpack second value in byte
+ */
+ if (!bigendian) {
+ octet = octet >> 4;
+ }
+
+ if (octet == 0x0f) {
+ /*
+ * This is the stop digit or a filler digit. Ignore
+ * it.
+ */
+ break;
+ }
+ if ((len == 1) && (odd == TRUE )){
+ /* Last octet, skipp last high nibble incase of odd number of digits*/
+ break;
+ }
+ digit_str[i] = dgt->out[octet & 0x0f];
+ i++;
+
+ ptr++;
+ len--;
+ }
+ digit_str[i] = '\0';
+ return digit_str;
+}
+
+/* XXXX Fix me - needs odd indicator added */
+const gchar *
+tvb_bcd_dig_to_str(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint len, const dgt_set_t *dgt, gboolean skip_first)
+{
+ if (!dgt)
+ dgt = &Dgt0_9_bcd;
+
+ return tvb_get_bcd_string(scope, tvb, offset, len, dgt, skip_first, FALSE, FALSE);
+}
+
+const gchar *
+tvb_bcd_dig_to_str_be(wmem_allocator_t *scope, tvbuff_t *tvb, const gint offset, const gint len, const dgt_set_t *dgt, gboolean skip_first)
+{
+ if (!dgt)
+ dgt = &Dgt0_9_bcd;
+
+ return tvb_get_bcd_string(scope, tvb, offset, len, dgt, skip_first, FALSE, TRUE);
+}
+
+/*
+ * Format a bunch of data from a tvbuff as bytes, returning a pointer
+ * to the string with the formatted data.
+ */
+gchar *tvb_bytes_to_str(wmem_allocator_t *allocator, tvbuff_t *tvb,
+ const gint offset, const gint len)
+{
+ DISSECTOR_ASSERT(len > 0);
+ return bytes_to_str(allocator, ensure_contiguous(tvb, offset, len), len);
+}
+
+/* Find a needle tvbuff within a haystack tvbuff. */
+gint
+tvb_find_tvb(tvbuff_t *haystack_tvb, tvbuff_t *needle_tvb, const gint haystack_offset)
+{
+ guint haystack_abs_offset = 0, haystack_abs_length = 0;
+ const guint8 *haystack_data;
+ const guint8 *needle_data;
+ const guint needle_len = needle_tvb->length;
+ const guint8 *location;
+
+ DISSECTOR_ASSERT(haystack_tvb && haystack_tvb->initialized);
+
+ if (haystack_tvb->length < 1 || needle_tvb->length < 1) {
+ return -1;
+ }
+
+ /* Get pointers to the tvbuffs' data. */
+ haystack_data = ensure_contiguous(haystack_tvb, 0, -1);
+ needle_data = ensure_contiguous(needle_tvb, 0, -1);
+
+ check_offset_length(haystack_tvb, haystack_offset, -1,
+ &haystack_abs_offset, &haystack_abs_length);
+
+ location = ws_memmem(haystack_data + haystack_abs_offset, haystack_abs_length,
+ needle_data, needle_len);
+
+ if (location) {
+ return (gint) (location - haystack_data);
+ }
+
+ return -1;
+}
+
+gint
+tvb_raw_offset(tvbuff_t *tvb)
+{
+ return ((tvb->raw_offset==-1) ? (tvb->raw_offset = tvb_offset_from_real_beginning(tvb)) : tvb->raw_offset);
+}
+
+void
+tvb_set_fragment(tvbuff_t *tvb)
+{
+ tvb->flags |= TVBUFF_FRAGMENT;
+}
+
+struct tvbuff *
+tvb_get_ds_tvb(tvbuff_t *tvb)
+{
+ return(tvb->ds_tvb);
+}
+
+guint
+tvb_get_varint(tvbuff_t *tvb, guint offset, guint maxlen, guint64 *value, const guint encoding)
+{
+ *value = 0;
+
+ switch (encoding & ENC_VARINT_MASK) {
+ case ENC_VARINT_PROTOBUF:
+ {
+ guint i;
+ guint64 b; /* current byte */
+
+ for (i = 0; ((i < FT_VARINT_MAX_LEN) && (i < maxlen)); ++i) {
+ b = tvb_get_guint8(tvb, offset++);
+ *value |= ((b & 0x7F) << (i * 7)); /* add lower 7 bits to val */
+
+ if (b < 0x80) {
+ /* end successfully becauseof last byte's msb(most significant bit) is zero */
+ return i + 1;
+ }
+ }
+ break;
+ }
+
+ case ENC_VARINT_ZIGZAG:
+ {
+ guint i;
+ guint64 b; /* current byte */
+
+ for (i = 0; ((i < FT_VARINT_MAX_LEN) && (i < maxlen)); ++i) {
+ b = tvb_get_guint8(tvb, offset++);
+ *value |= ((b & 0x7F) << (i * 7)); /* add lower 7 bits to val */
+
+ if (b < 0x80) {
+ /* end successfully becauseof last byte's msb(most significant bit) is zero */
+ *value = (*value >> 1) ^ ((*value & 1) ? -1 : 0);
+ return i + 1;
+ }
+ }
+ break;
+ }
+
+ case ENC_VARINT_SDNV:
+ {
+ /* Decodes similar to protobuf but in MSByte order */
+ guint i;
+ guint64 b; /* current byte */
+
+ for (i = 0; ((i < FT_VARINT_MAX_LEN) && (i < maxlen)); ++i) {
+ b = tvb_get_guint8(tvb, offset++);
+ if ((i == 9) && (*value >= G_GUINT64_CONSTANT(1)<<(64-7))) {
+ // guaranteed overflow, not valid SDNV
+ return 0;
+ }
+ *value <<= 7;
+ *value |= (b & 0x7F); /* add lower 7 bits to val */
+
+ if (b < 0x80) {
+ /* end successfully because of last byte's msb(most significant bit) is zero */
+ return i + 1;
+ }
+ }
+ break;
+ }
+
+ case ENC_VARINT_QUIC:
+ {
+ /* calculate variable length */
+ *value = tvb_get_guint8(tvb, offset);
+ switch((*value) >> 6) {
+ case 0: /* 0b00 => 1 byte length (6 bits Usable) */
+ (*value) &= 0x3F;
+ return 1;
+ case 1: /* 0b01 => 2 bytes length (14 bits Usable) */
+ *value = tvb_get_ntohs(tvb, offset) & 0x3FFF;
+ return 2;
+ case 2: /* 0b10 => 4 bytes length (30 bits Usable) */
+ *value = tvb_get_ntohl(tvb, offset) & 0x3FFFFFFF;
+ return 4;
+ case 3: /* 0b11 => 8 bytes length (62 bits Usable) */
+ *value = tvb_get_ntoh64(tvb, offset) & G_GUINT64_CONSTANT(0x3FFFFFFFFFFFFFFF);
+ return 8;
+ default: /* No Possible */
+ ws_assert_not_reached();
+ break;
+ }
+ break;
+ }
+
+ default:
+ DISSECTOR_ASSERT_NOT_REACHED();
+ }
+
+ return 0; /* 10 bytes scanned, but no bytes' msb is zero */
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */