[Pkg-telepathy-commits] [libnice] 115/265: stun: Add a fast version of stun_message_validate_buffer_length()

Simon McVittie smcv at debian.org
Wed May 14 12:04:59 UTC 2014


This is an automated email from the git hooks/post-receive script.

smcv pushed a commit to branch debian
in repository libnice.

commit e9ebf991e4ff023818c4594e641fb6c85e48c436
Author: Philip Withnall <philip.withnall at collabora.co.uk>
Date:   Thu Jan 16 16:02:07 2014 +0000

    stun: Add a fast version of stun_message_validate_buffer_length()
    
    stun_message_validate_buffer_length() is already fast, but requires the
    entire message to be in a single monolithic buffer. For introducing
    vectored I/O, this becomes impossible to guarantee.
    
    Rather than rewriting the STUN code to natively support vectors of
    buffers (which would be a huge undertaking, and would probably slow
    the code down considerably), implement a fast check of whether a message
    is likely to be a STUN packet which *does* support vectored I/O. This
    can then be used to determine whether to compact the vector of buffers
    to a single monolithic one in order to validate the message more
    thoroughly.
---
 agent/agent.c      |  7 ++++++
 stun/stunmessage.c | 73 ++++++++++++++++++++++++++++++++++++++++++++----------
 stun/stunmessage.h | 47 +++++++++++++++++++++++++++++++++++
 3 files changed, 114 insertions(+), 13 deletions(-)

diff --git a/agent/agent.c b/agent/agent.c
index c0521e7..b4e38cb 100644
--- a/agent/agent.c
+++ b/agent/agent.c
@@ -2651,6 +2651,13 @@ memcpy_buffer_to_input_message (NiceInputMessage *message,
 
   nice_debug_message_composition (message, 1);
 
+  if (buffer_length > 0) {
+    g_warning ("Dropped %" G_GSIZE_FORMAT " bytes of data from the end of "
+        "buffer %p (length: %" G_GSIZE_FORMAT ") due to not fitting in "
+        "message %p", buffer_length, buffer - message->length,
+        message->length + buffer_length, message);
+  }
+
   return message->length;
 }
 
diff --git a/stun/stunmessage.c b/stun/stunmessage.c
index 3e55368..712d140 100644
--- a/stun/stunmessage.c
+++ b/stun/stunmessage.c
@@ -538,46 +538,93 @@ stun_message_append_error (StunMessage *msg, StunError code)
   return STUN_MESSAGE_RETURN_SUCCESS;
 }
 
-int stun_message_validate_buffer_length (const uint8_t *msg, size_t length,
-    bool has_padding)
+/* Fast validity check for a potential STUN packet. Examines the type and
+ * length, but none of the attributes. Designed to allow vectored I/O on all
+ * incoming packets, filtering packets for closer inspection as to whether
+ * they’re STUN packets. If they look like they might be, their buffers are
+ * compacted to allow a more thorough check. */
+ssize_t stun_message_validate_buffer_length_fast (StunInputVector *buffers,
+    unsigned int n_buffers, size_t total_length, bool has_padding)
 {
   size_t mlen;
-  size_t len;
 
-  if (length < 1)
+  if (total_length < 1 || n_buffers < 1)
   {
     stun_debug ("STUN error: No data!\n");
     return STUN_MESSAGE_BUFFER_INVALID;
   }
 
-  if (msg[0] >> 6)
+  if (buffers[0].buffer[0] >> 6)
   {
     stun_debug ("STUN error: RTP or other non-protocol packet!\n");
     return STUN_MESSAGE_BUFFER_INVALID; // RTP or other non-STUN packet
   }
 
-  if (length < 4)
+  if (total_length < STUN_MESSAGE_LENGTH_POS + STUN_MESSAGE_LENGTH_LEN)
   {
     stun_debug ("STUN error: Incomplete STUN message header!\n");
     return STUN_MESSAGE_BUFFER_INCOMPLETE;
   }
 
-  mlen = stun_getw (msg + STUN_MESSAGE_LENGTH_POS) +
-      STUN_MESSAGE_HEADER_LENGTH;
+  if (buffers[0].size >= STUN_MESSAGE_LENGTH_POS + STUN_MESSAGE_LENGTH_LEN) {
+    /* Fast path. */
+    mlen = stun_getw (buffers[0].buffer + STUN_MESSAGE_LENGTH_POS);
+  } else {
+    /* Slow path. Tiny buffers abound. */
+    size_t skip_remaining = STUN_MESSAGE_LENGTH_POS;
+    unsigned int i;
+
+    /* Skip bytes. */
+    for (i = 0; i < n_buffers; i++) {
+      if (buffers[i].size <= skip_remaining)
+        skip_remaining -= buffers[i].size;
+      else
+        break;
+    }
 
-  if (has_padding && stun_padding (mlen))
-  {
+    /* Read bytes. May be split over two buffers. We’ve already checked that
+     * @total_length is long enough, so @n_buffers should be too. */
+    if (buffers[i].size - skip_remaining > 1) {
+      mlen = stun_getw (buffers[i].buffer + skip_remaining);
+    } else {
+      mlen = (*(buffers[i].buffer + skip_remaining) << 8) |
+             (*(buffers[i + 1].buffer));
+    }
+  }
+
+  mlen += STUN_MESSAGE_HEADER_LENGTH;
+
+  if (has_padding && stun_padding (mlen)) {
     stun_debug ("STUN error: Invalid message length: %u!\n", (unsigned)mlen);
     return STUN_MESSAGE_BUFFER_INVALID; // wrong padding
   }
 
-  if (length < mlen)
-  {
+  if (total_length < mlen) {
     stun_debug ("STUN error: Incomplete message: %u of %u bytes!\n",
-        (unsigned)length, (unsigned)mlen);
+        (unsigned) total_length, (unsigned) mlen);
     return STUN_MESSAGE_BUFFER_INCOMPLETE; // partial message
   }
 
+  return mlen;
+}
+
+int stun_message_validate_buffer_length (const uint8_t *msg, size_t length,
+    bool has_padding)
+{
+  ssize_t fast_retval;
+  size_t mlen;
+  size_t len;
+  StunInputVector input_buffer = { msg, length };
+
+  /* Fast pre-check first. */
+  fast_retval = stun_message_validate_buffer_length_fast (&input_buffer, 1,
+      length, has_padding);
+  if (fast_retval <= 0)
+    return fast_retval;
+
+  mlen = fast_retval;
+
+  /* Skip past the header (validated above). */
   msg += 20;
   len = mlen - 20;
 
diff --git a/stun/stunmessage.h b/stun/stunmessage.h
index b31189e..3fb486e 100644
--- a/stun/stunmessage.h
+++ b/stun/stunmessage.h
@@ -879,6 +879,53 @@ int stun_message_validate_buffer_length (const uint8_t *msg, size_t length,
     bool has_padding);
 
 /**
+ * StunInputVector:
+ * @buffer: a buffer containing already-received binary data
+ * @size: length of @buffer, in bytes
+ *
+ * Container for a single buffer which also stores its length. This is designed
+ * for vectored I/O: typically an array of #StunInputVectors is passed to
+ * functions, providing multiple buffers which store logically contiguous
+ * received data.
+ *
+ * This is guaranteed to be layed out identically in memory to #GInputVector.
+ *
+ * Since: 0.1.5
+ */
+typedef struct {
+  const uint8_t *buffer;
+  size_t size;
+} StunInputVector;
+
+/**
+ * stun_message_validate_buffer_length_fast:
+ * @buffers: (array length=n_buffers) (in caller-allocated): array of contiguous
+ * #StunInputVectors containing already-received message data
+ * @n_buffers: number of entries in @buffers
+ * @total_length: total number of valid bytes stored consecutively in @buffers
+ * @has_padding: %TRUE if attributes should be padded to 4-byte boundaries
+ *
+ * Quickly validate whether the message in the given @buffers is potentially a
+ * valid STUN message, an incomplete STUN message, or if it’s definitely not one
+ * at all.
+ *
+ * This is designed as a first-pass validation only, and does not check the
+ * message’s attributes for validity. If this function returns success, the
+ * buffers can be compacted and a more thorough validation can be performed
+ * using stun_message_validate_buffer_length(). If it fails, the buffers
+ * definitely do not contain a complete, valid STUN message.
+ *
+ * Returns: The length of the valid STUN message in the buffer, or zero or -1 on
+ * failure
+ * <para> See also: #STUN_MESSAGE_BUFFER_INCOMPLETE </para>
+ * <para> See also: #STUN_MESSAGE_BUFFER_INVALID </para>
+ *
+ * Since: 0.1.5
+ */
+ssize_t stun_message_validate_buffer_length_fast (StunInputVector *buffers,
+    unsigned int n_buffers, size_t total_length, bool has_padding);
+
+/**
  * stun_message_id:
  * @msg: The #StunMessage
  * @id: The #StunTransactionId to fill

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-telepathy/libnice.git



More information about the Pkg-telepathy-commits mailing list