Commit e9ebf991 authored by Philip Withnall's avatar Philip Withnall Committed by Olivier Crête
Browse files

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.
parent 89903349
......@@ -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;
}
......
......@@ -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;
......
......@@ -878,6 +878,53 @@ StunMessageReturn stun_message_append_error (StunMessage * msg,
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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment