summaryrefslogtreecommitdiff
path: root/stun
diff options
context:
space:
mode:
authorPhilip Withnall <philip.withnall@collabora.co.uk>2014-01-16 16:02:07 +0000
committerOlivier Crête <olivier.crete@collabora.com>2014-01-31 01:49:07 -0500
commite9ebf991e4ff023818c4594e641fb6c85e48c436 (patch)
treebdbec16f80856106c2d13d39f168118b59d93d18 /stun
parent899033492418725ccd17a1784529fd6634c7c2fe (diff)
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.
Diffstat (limited to 'stun')
-rw-r--r--stun/stunmessage.c73
-rw-r--r--stun/stunmessage.h47
2 files changed, 107 insertions, 13 deletions
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