diff options
author | Philip Withnall <philip.withnall@collabora.co.uk> | 2014-01-16 16:02:07 +0000 |
---|---|---|
committer | Olivier Crête <olivier.crete@collabora.com> | 2014-01-31 01:49:07 -0500 |
commit | e9ebf991e4ff023818c4594e641fb6c85e48c436 (patch) | |
tree | bdbec16f80856106c2d13d39f168118b59d93d18 /stun | |
parent | 899033492418725ccd17a1784529fd6634c7c2fe (diff) | |
download | libnice-e9ebf991e4ff023818c4594e641fb6c85e48c436.tar.gz |
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.c | 73 | ||||
-rw-r--r-- | stun/stunmessage.h | 47 |
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 |