From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../third_party/nICEr/src/stun/stun_codec.c | 1550 ++++++++++++++++++++ 1 file changed, 1550 insertions(+) create mode 100644 dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c (limited to 'dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c') diff --git a/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c new file mode 100644 index 0000000000..ae748a667b --- /dev/null +++ b/dom/media/webrtc/transport/third_party/nICEr/src/stun/stun_codec.c @@ -0,0 +1,1550 @@ +/* +Copyright (c) 2007, Adobe Systems, Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems, Network Resonance nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#ifdef WIN32 +#include +#include +#include +#include +#else /* UNIX */ +#include +#endif /* end UNIX */ +#include +#include + +#include "nr_api.h" +#include "stun.h" +#include "byteorder.h" +#include "r_crc32.h" +#include "nr_crypto.h" + +#define NR_STUN_IPV4_FAMILY 0x01 +#define NR_STUN_IPV6_FAMILY 0x02 + +#define SKIP_ATTRIBUTE_DECODE -1 + +static int nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info); + +static int nr_stun_fix_attribute_ordering(nr_stun_message *msg); + +static int nr_stun_encode_htons(UINT2 data, size_t buflen, UCHAR *buf, size_t *offset); +static int nr_stun_encode_htonl(UINT4 data, size_t buflen, UCHAR *buf, size_t *offset); +static int nr_stun_encode_htonll(UINT8 data, size_t buflen, UCHAR *buf, size_t *offset); +static int nr_stun_encode(UCHAR *data, size_t length, size_t buflen, UCHAR *buf, size_t *offset); + +static int nr_stun_decode_htons(UCHAR *buf, size_t buflen, size_t *offset, UINT2 *data); +static int nr_stun_decode_htonl(UCHAR *buf, size_t buflen, size_t *offset, UINT4 *data); +static int nr_stun_decode_htonll(UCHAR *buf, size_t buflen, size_t *offset, UINT8 *data); +static int nr_stun_decode(size_t length, UCHAR *buf, size_t buflen, size_t *offset, UCHAR *data); + +static int nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, size_t len, void *data, size_t max_bytes, size_t max_chars); + +static int nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data); +static int +nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data); + + +int +nr_stun_encode_htons(UINT2 data, size_t buflen, UCHAR *buf, size_t *offset) +{ + UINT2 d = htons(data); + + if (*offset + sizeof(d) >= buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd >= %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode_htonl(UINT4 data, size_t buflen, UCHAR *buf, size_t *offset) +{ + UINT4 d = htonl(data); + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode_htonll(UINT8 data, size_t buflen, UCHAR *buf, size_t *offset) +{ + UINT8 d = nr_htonll(data); + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], &d, sizeof(d)); + *offset += sizeof(d); + + return 0; +} + +int +nr_stun_encode(UCHAR *data, size_t length, size_t buflen, UCHAR *buf, size_t *offset) +{ + if (*offset + length > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen); + return R_BAD_DATA; + } + + memcpy(&buf[*offset], data, length); + *offset += length; + + return 0; +} + + +int +nr_stun_decode_htons(UCHAR *buf, size_t buflen, size_t *offset, UINT2 *data) +{ + UINT2 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = htons(d); + + return 0; +} + +int +nr_stun_decode_htonl(UCHAR *buf, size_t buflen, size_t *offset, UINT4 *data) +{ + UINT4 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = htonl(d); + + return 0; +} + +int +nr_stun_decode_htonll(UCHAR *buf, size_t buflen, size_t *offset, UINT8 *data) +{ + UINT8 d; + + if (*offset + sizeof(d) > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); + return R_BAD_DATA; + } + + memcpy(&d, &buf[*offset], sizeof(d)); + *offset += sizeof(d); + *data = nr_htonll(d); + + return 0; +} + +int +nr_stun_decode(size_t length, UCHAR *buf, size_t buflen, size_t *offset, UCHAR *data) +{ + if (*offset + length > buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %d > %d", *offset, length, buflen); + return R_BAD_DATA; + } + + memcpy(data, &buf[*offset], length); + *offset += length; + + return 0; +} + +/** + * The argument must be a non-null pointer to a zero-terminated string. + * + * If the argument is valid UTF-8, returns the number of code points in the + * string excluding the zero-terminator. + * + * If the argument is invalid UTF-8, returns a lower bound for the number of + * code points in the string. (If UTF-8 error handling was performed on the + * string, new REPLACEMENT CHARACTER code points could be introduced in + * a way that would increase the total number of code points compared to + * what this function counts.) + */ +size_t +nr_count_utf8_code_points_without_validation(const char *s) { + size_t nchars = 0; + char c; + while ((c = *s)) { + if ((c & 0xC0) != 0x80) { + ++nchars; + } + ++s; + } + return nchars; +} + +int +nr_stun_attr_string_illegal(nr_stun_attr_info *attr_info, size_t len, void *data, size_t max_bytes, size_t max_chars) +{ + int _status; + char *s = data; + size_t nchars; + + if (len > max_bytes) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %d bytes", attr_info->name, len); + ABORT(R_FAILED); + } + + nchars = nr_count_utf8_code_points_without_validation(s); + if (nchars > max_chars) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s is too large: %zd characters", attr_info->name, nchars); + ABORT(R_FAILED); + } + + _status = 0; + abort: + return _status; +} + +int +nr_stun_attr_error_code_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + int r,_status; + nr_stun_attr_error_code *ec = data; + + if (ec->number < 300 || ec->number > 699) + ABORT(R_FAILED); + + if ((r=nr_stun_attr_string_illegal(attr_info, strlen(ec->reason), ec->reason, NR_STUN_MAX_ERROR_CODE_REASON_BYTES, NR_STUN_MAX_ERROR_CODE_REASON_CHARS))) + ABORT(r); + + _status = 0; + abort: + return _status; +} + +int +nr_stun_attr_nonce_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_NONCE_BYTES, NR_STUN_MAX_NONCE_CHARS); +} + +int +nr_stun_attr_realm_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_REALM_BYTES, NR_STUN_MAX_REALM_CHARS); +} + +int +nr_stun_attr_server_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_SERVER_BYTES, NR_STUN_MAX_SERVER_CHARS); +} + +int +nr_stun_attr_username_illegal(nr_stun_attr_info *attr_info, size_t attrlen, void *data) +{ + return nr_stun_attr_string_illegal(attr_info, attrlen, data, NR_STUN_MAX_USERNAME_BYTES, -1); +} + +static int +nr_stun_attr_codec_UCHAR_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UCHAR*)data); + return 0; +} + +static int +nr_stun_attr_codec_UCHAR_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + UINT4 tmp = *((UCHAR *)data); + tmp <<= 24; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset) + || nr_stun_encode_htonl(tmp , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UCHAR_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + UINT4 tmp; + + if (attrlen != sizeof(UINT4)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonl(buf, buflen, &offset, &tmp)) + return R_FAILED; + + *((UCHAR *)data) = (tmp >> 24) & 0xff; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UCHAR = { + "UCHAR", + nr_stun_attr_codec_UCHAR_print, + nr_stun_attr_codec_UCHAR_encode, + nr_stun_attr_codec_UCHAR_decode +}; + +static int +nr_stun_attr_codec_UINT4_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %u", msg, attr_info->name, *(UINT4*)data); + return 0; +} + +static int +nr_stun_attr_codec_UINT4_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT4) , buflen, buf, &offset) + || nr_stun_encode_htonl(*(UINT4*)data , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UINT4_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + if (attrlen != sizeof(UINT4)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonl(buf, buflen, &offset, (UINT4*)data)) + return R_FAILED; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UINT4 = { + "UINT4", + nr_stun_attr_codec_UINT4_print, + nr_stun_attr_codec_UINT4_encode, + nr_stun_attr_codec_UINT4_decode +}; + +static int +nr_stun_attr_codec_UINT8_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %llu", msg, attr_info->name, *(UINT8*)data); + return 0; +} + +static int +nr_stun_attr_codec_UINT8_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(UINT8) , buflen, buf, &offset) + || nr_stun_encode_htonll(*(UINT8*)data , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_UINT8_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + if (attrlen != sizeof(UINT8)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Integer is illegal size: %d", attrlen); + return R_FAILED; + } + + if (nr_stun_decode_htonll(buf, buflen, &offset, (UINT8*)data)) + return R_FAILED; + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_UINT8 = { + "UINT8", + nr_stun_attr_codec_UINT8_print, + nr_stun_attr_codec_UINT8_encode, + nr_stun_attr_codec_UINT8_decode +}; + +static int +nr_stun_attr_codec_addr_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", msg, attr_info->name, ((nr_transport_addr*)data)->as_string); + return 0; +} + +static int +nr_stun_attr_codec_addr_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int r,_status; + int start = offset; + nr_transport_addr *addr = data; + UCHAR pad = '\0'; + UCHAR family; + + if ((r=nr_stun_encode_htons(attr_info->type, buflen, buf, &offset))) + ABORT(r); + + switch (addr->ip_version) { + case NR_IPV4: + family = NR_STUN_IPV4_FAMILY; + if (nr_stun_encode_htons(8 , buflen, buf, &offset) + || nr_stun_encode(&pad, 1 , buflen, buf, &offset) + || nr_stun_encode(&family, 1 , buflen, buf, &offset) + || nr_stun_encode_htons(ntohs(addr->u.addr4.sin_port), buflen, buf, &offset) + || nr_stun_encode_htonl(ntohl(addr->u.addr4.sin_addr.s_addr), buflen, buf, &offset)) + ABORT(R_FAILED); + break; + + case NR_IPV6: + family = NR_STUN_IPV6_FAMILY; + if (nr_stun_encode_htons(20 , buflen, buf, &offset) + || nr_stun_encode(&pad, 1 , buflen, buf, &offset) + || nr_stun_encode(&family, 1 , buflen, buf, &offset) + || nr_stun_encode_htons(ntohs(addr->u.addr6.sin6_port), buflen, buf, &offset) + || nr_stun_encode(addr->u.addr6.sin6_addr.s6_addr, 16, buflen, buf, &offset)) + ABORT(R_FAILED); + break; + + default: + assert(0); + ABORT(R_INTERNAL); + break; + } + + *attrlen = offset - start; + + _status = 0; + abort: + return _status; +} + +static int +nr_stun_attr_codec_addr_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + UCHAR pad; + UCHAR family; + UINT2 port; + UINT4 addr4; + struct in6_addr addr6; + nr_transport_addr *result = data; + + if (nr_stun_decode(1, buf, buflen, &offset, &pad) + || nr_stun_decode(1, buf, buflen, &offset, &family)) + ABORT(R_FAILED); + + switch (family) { + case NR_STUN_IPV4_FAMILY: + if (attrlen != 8) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode_htons(buf, buflen, &offset, &port) + || nr_stun_decode_htonl(buf, buflen, &offset, &addr4)) + ABORT(R_FAILED); + + if (nr_ip4_port_to_transport_addr(addr4, port, IPPROTO_UDP, result)) + ABORT(R_FAILED); + break; + + case NR_STUN_IPV6_FAMILY: + if (attrlen != 20) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal attribute length: %d", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode_htons(buf, buflen, &offset, &port) + || nr_stun_decode(16, buf, buflen, &offset, addr6.s6_addr)) + ABORT(R_FAILED); + + if (nr_ip6_port_to_transport_addr(&addr6, port, IPPROTO_UDP, result)) + ABORT(R_FAILED); + break; + + default: + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal address family: %d", family); + ABORT(R_FAILED); + break; + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_addr = { + "addr", + nr_stun_attr_codec_addr_print, + nr_stun_attr_codec_addr_encode, + nr_stun_attr_codec_addr_decode +}; + +static int +nr_stun_attr_codec_data_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_data *d = data; + r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)d->data, d->length); + return 0; +} + +static int +nr_stun_attr_codec_data_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + nr_stun_attr_data *d = data; + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(d->length , buflen, buf, &offset) + || nr_stun_encode(d->data, d->length , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_data_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + nr_stun_attr_data *result = data; + + /* -1 because it is going to be null terminated just to be safe */ + if (attrlen >= (sizeof(result->data) - 1)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too much data: %d bytes", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode(attrlen, buf, buflen, &offset, result->data)) + ABORT(R_FAILED); + + result->length = attrlen; + result->data[attrlen] = '\0'; /* just to be nice */ + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_data = { + "data", + nr_stun_attr_codec_data_print, + nr_stun_attr_codec_data_encode, + nr_stun_attr_codec_data_decode +}; + +static int +nr_stun_attr_codec_error_code_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_error_code *error_code = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %d %s", + msg, attr_info->name, error_code->number, + error_code->reason); + return 0; +} + +static int +nr_stun_attr_codec_error_code_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + nr_stun_attr_error_code *error_code = data; + int start = offset; + int length = strlen(error_code->reason); + UCHAR pad[2] = { 0 }; + UCHAR class = error_code->number / 100; + UCHAR number = error_code->number % 100; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(4 + length , buflen, buf, &offset) + || nr_stun_encode(pad, 2 , buflen, buf, &offset) + || nr_stun_encode(&class, 1 , buflen, buf, &offset) + || nr_stun_encode(&number, 1 , buflen, buf, &offset) + || nr_stun_encode((UCHAR*)error_code->reason, length, buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_error_code_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + nr_stun_attr_error_code *result = data; + UCHAR pad[2]; + UCHAR class; + UCHAR number; + size_t size_reason; + + if (nr_stun_decode(2, buf, buflen, &offset, pad) + || nr_stun_decode(1, buf, buflen, &offset, &class) + || nr_stun_decode(1, buf, buflen, &offset, &number)) + ABORT(R_FAILED); + + result->number = (class * 100) + number; + + size_reason = attrlen - 4; + + /* -1 because the string will be null terminated */ + if (size_reason > (sizeof(result->reason) - 1)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Reason is too large, truncating"); + /* don't fail, but instead truncate the reason */ + size_reason = sizeof(result->reason) - 1; + } + + if (nr_stun_decode(size_reason, buf, buflen, &offset, (UCHAR*)result->reason)) + ABORT(R_FAILED); + result->reason[size_reason] = '\0'; + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_error_code = { + "error_code", + nr_stun_attr_codec_error_code_print, + nr_stun_attr_codec_error_code_encode, + nr_stun_attr_codec_error_code_decode +}; + +static int +nr_stun_attr_codec_fingerprint_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_fingerprint *fingerprint = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %08x", msg, attr_info->name, fingerprint->checksum); + return 0; +} + +static int +nr_stun_attr_codec_fingerprint_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + UINT4 checksum; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + + /* the length must include the FINGERPRINT attribute when computing + * the fingerprint */ + header->length = ntohs(header->length); + header->length += 8; /* Fingerprint */ + header->length = htons(header->length); + + if (r_crc32((char*)buf, offset, &checksum)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint"); + return R_FAILED; + } + + fingerprint->checksum = checksum ^ 0x5354554e; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", fingerprint->checksum); + + fingerprint->valid = 1; + return nr_stun_attr_codec_UINT4.encode(attr_info, &fingerprint->checksum, offset, buflen, buf, attrlen); +} + +static int +nr_stun_attr_codec_fingerprint_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int r,_status; + nr_stun_attr_fingerprint *fingerprint = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + size_t length; + UINT4 checksum; + + if ((r=nr_stun_attr_codec_UINT4.decode(attr_info, attrlen, buf, offset, buflen, &fingerprint->checksum))) + ABORT(r); + + offset -= 4; /* rewind to before the length and type fields */ + + /* the length must include the FINGERPRINT attribute when computing + * the fingerprint */ + length = offset; /* right before FINGERPRINT */ + length -= sizeof(*header); /* remove header length */ + length += 8; /* add length of Fingerprint */ + header->length = htons(length); + + /* make sure FINGERPRINT is final attribute in message */ + if (length + sizeof(*header) != buflen) { + r_log(NR_LOG_STUN, LOG_WARNING, "Fingerprint is not final attribute in message"); + ABORT(R_FAILED); + } + + if (r_crc32((char*)buf, offset, &checksum)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to compute fingerprint"); + ABORT(R_FAILED); + } + + fingerprint->valid = (fingerprint->checksum == (checksum ^ 0x5354554e)); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computed FINGERPRINT %08x", (checksum ^ 0x5354554e)); + if (! fingerprint->valid) + r_log(NR_LOG_STUN, LOG_WARNING, "Invalid FINGERPRINT %08x", fingerprint->checksum); + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_fingerprint = { + "fingerprint", + nr_stun_attr_codec_fingerprint_print, + nr_stun_attr_codec_fingerprint_encode, + nr_stun_attr_codec_fingerprint_decode +}; + +static int +nr_stun_attr_codec_flag_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: on", msg, attr_info->name); + return 0; +} + +static int +nr_stun_attr_codec_flag_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(0 , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_flag_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + if (attrlen != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal flag length: %d", attrlen); + return R_FAILED; + } + + return 0; +} + +nr_stun_attr_codec nr_stun_attr_codec_flag = { + "flag", + nr_stun_attr_codec_flag_print, + nr_stun_attr_codec_flag_encode, + nr_stun_attr_codec_flag_decode +}; + +static int +nr_stun_attr_codec_message_integrity_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_message_integrity *integrity = data; + r_dump(NR_LOG_STUN, LOG_DEBUG, attr_info->name, (char*)integrity->hash, sizeof(integrity->hash)); + return 0; +} + +static int +nr_stun_compute_message_integrity(UCHAR *buf, int offset, UCHAR *password, int passwordlen, UCHAR *computedHMAC) +{ + int r,_status; + UINT2 hold; + UINT2 length; + nr_stun_message_header *header; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Computing MESSAGE-INTEGRITY"); + + header = (nr_stun_message_header*)buf; + hold = header->length; + + /* adjust the length of the message */ + length = offset; + length -= sizeof(*header); + length += 24; /* for MESSAGE-INTEGRITY attribute */ + header->length = htons(length); + + if ((r=nr_crypto_hmac_sha1((UCHAR*)password, passwordlen, + buf, offset, computedHMAC))) + ABORT(r); + + r_dump(NR_LOG_STUN, LOG_DEBUG, "Computed MESSAGE-INTEGRITY ", (char*)computedHMAC, 20); + + _status=0; + abort: + header->length = hold; + return _status; +} + +static int +nr_stun_attr_codec_message_integrity_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + nr_stun_attr_message_integrity *integrity = data; + + if (nr_stun_compute_message_integrity(buf, offset, integrity->password, integrity->passwordlen, integrity->hash)) + return R_FAILED; + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(sizeof(integrity->hash) , buflen, buf, &offset) + || nr_stun_encode(integrity->hash, sizeof(integrity->hash) , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_message_integrity_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + int start; + nr_stun_attr_message_integrity *result = data; + UCHAR computedHMAC[20]; + + result->valid = 0; + + if (attrlen != 20) { + r_log(NR_LOG_STUN, LOG_WARNING, "%s must be 20 bytes, not %d", attr_info->name, attrlen); + ABORT(R_FAILED); + } + + start = offset - 4; /* rewind to before the length and type fields */ + if (start < 0) + ABORT(R_INTERNAL); + + if (nr_stun_decode(attrlen, buf, buflen, &offset, result->hash)) + ABORT(R_FAILED); + + if (result->unknown_user) { + result->valid = 0; + } + else { + if (nr_stun_compute_message_integrity(buf, start, result->password, result->passwordlen, computedHMAC)) + ABORT(R_FAILED); + + assert(sizeof(computedHMAC) == sizeof(result->hash)); + + result->valid = (memcmp(computedHMAC, result->hash, 20) == 0); + } + + _status=0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_message_integrity = { + "message_integrity", + nr_stun_attr_codec_message_integrity_print, + nr_stun_attr_codec_message_integrity_encode, + nr_stun_attr_codec_message_integrity_decode +}; + +static int +nr_stun_attr_codec_noop_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + return SKIP_ATTRIBUTE_DECODE; +} + +nr_stun_attr_codec nr_stun_attr_codec_noop = { + "NOOP", + 0, /* ignore, never print these attributes */ + 0, /* ignore, never encode these attributes */ + nr_stun_attr_codec_noop_decode +}; + +static int +nr_stun_attr_codec_quoted_string_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", + msg, attr_info->name, (char*)data); + return 0; +} + +static int +nr_stun_attr_codec_quoted_string_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ +//TODO: !nn! syntax check, conversion if not quoted already? +//We'll just restrict this in the API -- EKR + return nr_stun_attr_codec_string.encode(attr_info, data, offset, buflen, buf, attrlen); +} + +static int +nr_stun_attr_codec_quoted_string_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ +//TODO: !nn! I don't see any need to unquote this but we may +//find one later -- EKR + return nr_stun_attr_codec_string.decode(attr_info, attrlen, buf, offset, buflen, data); +} + +nr_stun_attr_codec nr_stun_attr_codec_quoted_string = { + "quoted_string", + nr_stun_attr_codec_quoted_string_print, + nr_stun_attr_codec_quoted_string_encode, + nr_stun_attr_codec_quoted_string_decode +}; + +static int +nr_stun_attr_codec_string_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s", + msg, attr_info->name, (char*)data); + return 0; +} + +static int +nr_stun_attr_codec_string_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int start = offset; + char *str = data; + int length = strlen(str); + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(length , buflen, buf, &offset) + || nr_stun_encode((UCHAR*)str, length , buflen, buf, &offset)) + return R_FAILED; + + *attrlen = offset - start; + + return 0; +} + +static int +nr_stun_attr_codec_string_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + char *result = data; + + /* actual enforcement of the specific string size happens elsewhere */ + if (attrlen >= NR_STUN_MAX_STRING_SIZE) { + r_log(NR_LOG_STUN, LOG_WARNING, "String is too large: %d bytes", attrlen); + ABORT(R_FAILED); + } + + if (nr_stun_decode(attrlen, buf, buflen, &offset, (UCHAR*)result)) + ABORT(R_FAILED); + result[attrlen] = '\0'; /* just to be nice */ + + if (strlen(result) != attrlen) { + /* stund 0.96 sends a final null in the Server attribute, so + * only error if the null appears anywhere else in a string */ + if (strlen(result) != attrlen-1) { + r_log(NR_LOG_STUN, LOG_WARNING, "Error in string: %zd/%d", strlen(result), attrlen); + ABORT(R_FAILED); + } + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_string = { + "string", + nr_stun_attr_codec_string_print, + nr_stun_attr_codec_string_encode, + nr_stun_attr_codec_string_decode +}; + +static int +nr_stun_attr_codec_unknown_attributes_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_unknown_attributes *unknown_attributes = data; + char type[9]; + char str[64 + (NR_STUN_MAX_UNKNOWN_ATTRIBUTES * sizeof(type))]; + int i; + + snprintf(str, sizeof(str), "%s %s:", msg, attr_info->name); + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + snprintf(type, sizeof(type), "%s 0x%04x", ((i>0)?",":""), unknown_attributes->attribute[i]); + strlcat(str, type, sizeof(str)); + } + + r_log(NR_LOG_STUN, LOG_DEBUG, "%s", str); + return 0; +} + +static int +nr_stun_attr_codec_unknown_attributes_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + int _status; + int start = offset; + nr_stun_attr_unknown_attributes *unknown_attributes = data; + int length = (2 * unknown_attributes->num_attributes); + int i; + + if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes); + ABORT(R_FAILED); + } + + if (nr_stun_encode_htons(attr_info->type , buflen, buf, &offset) + || nr_stun_encode_htons(length , buflen, buf, &offset)) + ABORT(R_FAILED); + + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + if (nr_stun_encode_htons(unknown_attributes->attribute[i], buflen, buf, &offset)) + ABORT(R_FAILED); + } + + *attrlen = offset - start; + + _status = 0; + abort: + return _status; +} + +static int +nr_stun_attr_codec_unknown_attributes_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int _status; + nr_stun_attr_unknown_attributes *unknown_attributes = data; + int i; + UINT2 *a; + + if ((attrlen % 4) != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attribute is illegal size: %d", attrlen); + ABORT(R_REJECTED); + } + + unknown_attributes->num_attributes = attrlen / 2; + + if (unknown_attributes->num_attributes > NR_STUN_MAX_UNKNOWN_ATTRIBUTES) { + r_log(NR_LOG_STUN, LOG_WARNING, "Too many UNKNOWN-ATTRIBUTES: %d", unknown_attributes->num_attributes); + ABORT(R_REJECTED); + } + + for (i = 0; i < unknown_attributes->num_attributes; ++i) { + a = &(unknown_attributes->attribute[i]); + if (nr_stun_decode_htons(buf, buflen, &offset, a)) + return R_FAILED; + } + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_unknown_attributes = { + "unknown_attributes", + nr_stun_attr_codec_unknown_attributes_print, + nr_stun_attr_codec_unknown_attributes_encode, + nr_stun_attr_codec_unknown_attributes_decode +}; + +static int +nr_stun_attr_codec_xor_mapped_address_print(nr_stun_attr_info *attr_info, char *msg, void *data) +{ + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + r_log(NR_LOG_STUN, LOG_DEBUG, "%s %s: %s (unmasked) %s (masked)", + msg, attr_info->name, + xor_mapped_address->unmasked.as_string, + xor_mapped_address->masked.as_string); + return 0; +} + +static int +nr_stun_attr_codec_xor_mapped_address_encode(nr_stun_attr_info *attr_info, void *data, size_t offset, size_t buflen, UCHAR *buf, size_t *attrlen) +{ + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + UINT4 magic_cookie; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string); + + /* this needs to be the magic cookie in the header and not + * the MAGIC_COOKIE constant because if we're talking to + * older servers (that don't have a magic cookie) they use + * message ID for this */ + magic_cookie = ntohl(header->magic_cookie); + + nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->unmasked, &xor_mapped_address->masked); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string); + + if (nr_stun_attr_codec_addr.encode(attr_info, &xor_mapped_address->masked, offset, buflen, buf, attrlen)) + return R_FAILED; + + return 0; +} + +static int +nr_stun_attr_codec_xor_mapped_address_decode(nr_stun_attr_info *attr_info, size_t attrlen, UCHAR *buf, size_t offset, size_t buflen, void *data) +{ + int r,_status; + nr_stun_attr_xor_mapped_address *xor_mapped_address = data; + nr_stun_message_header *header = (nr_stun_message_header*)buf; + UINT4 magic_cookie; + + if ((r=nr_stun_attr_codec_addr.decode(attr_info, attrlen, buf, offset, buflen, &xor_mapped_address->masked))) + ABORT(r); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Masked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->masked.as_string); + + /* this needs to be the magic cookie in the header and not + * the MAGIC_COOKIE constant because if we're talking to + * older servers (that don't have a magic cookie) they use + * message ID for this */ + magic_cookie = ntohl(header->magic_cookie); + + nr_stun_xor_mapped_address(magic_cookie, header->id, &xor_mapped_address->masked, &xor_mapped_address->unmasked); + + r_log(NR_LOG_STUN, LOG_DEBUG, "Unmasked XOR-MAPPED-ADDRESS = %s", xor_mapped_address->unmasked.as_string); + + _status = 0; + abort: + return _status; +} + +nr_stun_attr_codec nr_stun_attr_codec_xor_mapped_address = { + "xor_mapped_address", + nr_stun_attr_codec_xor_mapped_address_print, + nr_stun_attr_codec_xor_mapped_address_encode, + nr_stun_attr_codec_xor_mapped_address_decode +}; + +nr_stun_attr_codec nr_stun_attr_codec_old_xor_mapped_address = { + "xor_mapped_address", + nr_stun_attr_codec_xor_mapped_address_print, + 0, /* never encode this type */ + nr_stun_attr_codec_xor_mapped_address_decode +}; + +nr_stun_attr_codec nr_stun_attr_codec_xor_peer_address = { + "xor_peer_address", + nr_stun_attr_codec_xor_mapped_address_print, + nr_stun_attr_codec_xor_mapped_address_encode, + nr_stun_attr_codec_xor_mapped_address_decode +}; + +#define NR_ADD_STUN_ATTRIBUTE(type, name, codec, illegal) \ + { (type), (name), &(codec), illegal }, + +#define NR_ADD_STUN_ATTRIBUTE_IGNORE(type, name) \ + { (type), (name), &nr_stun_attr_codec_noop, 0 }, + + +static nr_stun_attr_info attrs[] = { + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ALTERNATE_SERVER, "ALTERNATE-SERVER", nr_stun_attr_codec_addr, 0) +#ifdef USE_STUND_0_96 + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_CHANGE_REQUEST, "CHANGE-REQUEST", nr_stun_attr_codec_UINT4, 0) +#endif + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ERROR_CODE, "ERROR-CODE", nr_stun_attr_codec_error_code, nr_stun_attr_error_code_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_FINGERPRINT, "FINGERPRINT", nr_stun_attr_codec_fingerprint, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MAPPED_ADDRESS, "MAPPED-ADDRESS", nr_stun_attr_codec_addr, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_MESSAGE_INTEGRITY, "MESSAGE-INTEGRITY", nr_stun_attr_codec_message_integrity, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_NONCE, "NONCE", nr_stun_attr_codec_quoted_string, nr_stun_attr_nonce_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REALM, "REALM", nr_stun_attr_codec_quoted_string, nr_stun_attr_realm_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_SERVER, "SERVER", nr_stun_attr_codec_string, nr_stun_attr_server_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_UNKNOWN_ATTRIBUTES, "UNKNOWN-ATTRIBUTES", nr_stun_attr_codec_unknown_attributes, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USERNAME, "USERNAME", nr_stun_attr_codec_string, nr_stun_attr_username_illegal) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_MAPPED_ADDRESS, "XOR-MAPPED-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0) + +#ifdef USE_ICE + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLED, "ICE-CONTROLLED", nr_stun_attr_codec_UINT8, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_ICE_CONTROLLING, "ICE-CONTROLLING", nr_stun_attr_codec_UINT8, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_PRIORITY, "PRIORITY", nr_stun_attr_codec_UINT4, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_USE_CANDIDATE, "USE-CANDIDATE", nr_stun_attr_codec_flag, 0) +#endif + +#ifdef USE_TURN + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_DATA, "DATA", nr_stun_attr_codec_data, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_LIFETIME, "LIFETIME", nr_stun_attr_codec_UINT4, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_RELAY_ADDRESS, "XOR-RELAY-ADDRESS", nr_stun_attr_codec_xor_mapped_address, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_XOR_PEER_ADDRESS, "XOR-PEER-ADDRESS", nr_stun_attr_codec_xor_peer_address, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_REQUESTED_TRANSPORT, "REQUESTED-TRANSPORT", nr_stun_attr_codec_UCHAR, 0) + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_BANDWIDTH, "BANDWIDTH", nr_stun_attr_codec_UINT4, 0) +#endif /* USE_TURN */ + + /* for backwards compatibilty */ + NR_ADD_STUN_ATTRIBUTE(NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS, "Old XOR-MAPPED-ADDRESS", nr_stun_attr_codec_old_xor_mapped_address, 0) +#ifdef USE_RFC_3489_BACKWARDS_COMPATIBLE + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_RESPONSE_ADDRESS, "RESPONSE-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_SOURCE_ADDRESS, "SOURCE-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_CHANGED_ADDRESS, "CHANGED-ADDRESS") + NR_ADD_STUN_ATTRIBUTE_IGNORE(NR_STUN_ATTR_OLD_PASSWORD, "PASSWORD") +#endif /* USE_RFC_3489_BACKWARDS_COMPATIBLE */ +}; + + +int +nr_stun_find_attr_info(UINT2 type, nr_stun_attr_info **info) +{ + int _status; + size_t i; + + *info = 0; + for (i = 0; i < sizeof(attrs)/sizeof(*attrs); ++i) { + if (type == attrs[i].type) { + *info = &attrs[i]; + break; + } + } + + if (*info == 0) + ABORT(R_NOT_FOUND); + + _status=0; + abort: + return(_status); +} + +int +nr_stun_fix_attribute_ordering(nr_stun_message *msg) +{ + nr_stun_message_attribute *message_integrity; + nr_stun_message_attribute *fingerprint; + + /* 2nd to the last */ + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_MESSAGE_INTEGRITY, &message_integrity)) { + TAILQ_REMOVE(&msg->attributes, message_integrity, entry); + TAILQ_INSERT_TAIL(&msg->attributes, message_integrity, entry); + } + + /* last */ + if (nr_stun_message_has_attribute(msg, NR_STUN_ATTR_FINGERPRINT, &fingerprint)) { + TAILQ_REMOVE(&msg->attributes, fingerprint, entry); + TAILQ_INSERT_TAIL(&msg->attributes, fingerprint, entry); + } + + return 0; +} + +// Since this sanity check is only a collection of assert statements and those +// assert statements are compiled out in non-debug builds, undef SANITY_CHECKS +// so we can avoid the warning that padding_bytes is never used in opt builds. +#ifdef NDEBUG +#undef SANITY_CHECKS +#endif + +#ifdef SANITY_CHECKS +static void sanity_check_encoding_stuff(nr_stun_message *msg) +{ + nr_stun_message_attribute *attr = 0; + int padding_bytes; + int l; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Starting to sanity check encoding"); + + l = 0; + TAILQ_FOREACH(attr, &msg->attributes, entry) { + padding_bytes = 0; + if ((attr->length % 4) != 0) { + padding_bytes = 4 - (attr->length % 4); + } + assert(attr->length == (attr->encoding_length - (4 + padding_bytes))); + assert(((void*)attr->encoding) == (msg->buffer + 20 + l)); + l += attr->encoding_length; + assert((l % 4) == 0); + } + assert(l == msg->header.length); +} +#endif /* SANITY_CHECKS */ + + +int +nr_stun_encode_message(nr_stun_message *msg) +{ + int r,_status; + size_t length_offset; + size_t length_offset_hold; + nr_stun_attr_info *attr_info; + nr_stun_message_attribute *attr; + int padding_bytes; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoding STUN message"); + + nr_stun_fix_attribute_ordering(msg); + + msg->name = nr_stun_msg_type(msg->header.type); + msg->length = 0; + msg->header.length = 0; + + if ((r=nr_stun_encode_htons(msg->header.type, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + if (msg->name) + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: %s", msg->name); + else + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded MsgType: 0x%03x", msg->header.type); + + /* grab the offset to be used later to re-write the header length field */ + length_offset_hold = msg->length; + + if ((r=nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + + if ((r=nr_stun_encode_htonl(msg->header.magic_cookie, sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Cookie: %08x", msg->header.magic_cookie); + + if ((r=nr_stun_encode((UCHAR*)(&msg->header.id), sizeof(msg->header.id), sizeof(msg->buffer), msg->buffer, &msg->length))) + ABORT(r); + r_dump(NR_LOG_STUN, LOG_DEBUG, "Encoded ID", (void*)&msg->header.id, sizeof(msg->header.id)); + + TAILQ_FOREACH(attr, &msg->attributes, entry) { + if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unrecognized attribute: 0x%04x", attr->type); + ABORT(R_INTERNAL); + } + + attr->name = attr_info->name; + attr->type_name = attr_info->codec->name; + attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[msg->length]; + + if (attr_info->codec->encode != 0) { + if ((r=attr_info->codec->encode(attr_info, &attr->u, msg->length, sizeof(msg->buffer), msg->buffer, &attr->encoding_length))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to encode %s", attr_info->name); + ABORT(r); + } + + msg->length += attr->encoding_length; + attr->length = attr->encoding_length - 4; /* -4 for type and length fields */ + + if (attr_info->illegal) { + if ((r=attr_info->illegal(attr_info, attr->length, &attr->u))) + ABORT(r); + } + + attr_info->codec->print(attr_info, "Encoded", &attr->u); + + if ((attr->length % 4) == 0) { + padding_bytes = 0; + } + else { + padding_bytes = 4 - (attr->length % 4); + nr_stun_encode((UCHAR*)"\0\0\0\0", padding_bytes, sizeof(msg->buffer), msg->buffer, &msg->length); + attr->encoding_length += padding_bytes; + } + + msg->header.length += attr->encoding_length; + length_offset = length_offset_hold; + (void)nr_stun_encode_htons(msg->header.length, sizeof(msg->buffer), msg->buffer, &length_offset); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Missing encode function for attribute: %s", attr_info->name); + } + } + + r_log(NR_LOG_STUN, LOG_DEBUG, "Encoded Length: %d", msg->header.length); + + assert(msg->length < NR_STUN_MAX_MESSAGE_SIZE); + +#ifdef SANITY_CHECKS + sanity_check_encoding_stuff(msg); +#endif /* SANITY_CHECKS */ + + _status=0; +abort: + return _status; +} + +int +nr_stun_decode_message(nr_stun_message *msg, int (*get_password)(void *arg, nr_stun_message *msg, Data **password), void *arg) +{ + int r,_status; + int offset; + int size; + int padding_bytes; + nr_stun_message_attribute *attr; + nr_stun_attr_info *attr_info; + Data *password; + + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsing STUN message of %d bytes", msg->length); + + if (!TAILQ_EMPTY(&msg->attributes)) + ABORT(R_BAD_ARGS); + + if (sizeof(nr_stun_message_header) > msg->length) { + r_log(NR_LOG_STUN, LOG_WARNING, "Message too small"); + ABORT(R_FAILED); + } + + memcpy(&msg->header, msg->buffer, sizeof(msg->header)); + msg->header.type = ntohs(msg->header.type); + msg->header.length = ntohs(msg->header.length); + msg->header.magic_cookie = ntohl(msg->header.magic_cookie); + + msg->name = nr_stun_msg_type(msg->header.type); + + if (msg->name) + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: %s", msg->name); + else + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed MsgType: 0x%03x", msg->header.type); + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Length: %d", msg->header.length); + r_log(NR_LOG_STUN, LOG_DEBUG, "Parsed Cookie: %08x", msg->header.magic_cookie); + r_dump(NR_LOG_STUN, LOG_DEBUG, "Parsed ID", (void*)&msg->header.id, sizeof(msg->header.id)); + + if (msg->header.length + sizeof(msg->header) != msg->length) { + r_log(NR_LOG_STUN, LOG_WARNING, "Inconsistent message header length: %d/%d", + msg->header.length, msg->length); + ABORT(R_FAILED); + } + + size = msg->header.length; + + if ((size % 4) != 0) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message size: %d", msg->header.length); + ABORT(R_FAILED); + } + + offset = sizeof(msg->header); + + while (size > 0) { + r_log(NR_LOG_STUN, LOG_DEBUG, "size = %d", size); + + if (size < 4) { + r_log(NR_LOG_STUN, LOG_WARNING, "Illegal message length: %d", size); + ABORT(R_FAILED); + } + + if ((r=nr_stun_message_attribute_create(msg, &attr))) + ABORT(R_NO_MEMORY); + + attr->encoding = (nr_stun_encoded_attribute*)&msg->buffer[offset]; + attr->type = ntohs(attr->encoding->type); + attr->length = ntohs(attr->encoding->length); + attr->encoding_length = attr->length + 4; + + if ((attr->length % 4) != 0) { + padding_bytes = 4 - (attr->length % 4); + attr->encoding_length += padding_bytes; + } + + if ((attr->encoding_length) > (size_t)size) { + r_log(NR_LOG_STUN, LOG_WARNING, "Attribute length larger than remaining message size: %d/%d", attr->encoding_length, size); + ABORT(R_FAILED); + } + + if ((r=nr_stun_find_attr_info(attr->type, &attr_info))) { + if (attr->type <= 0x7FFF) + ++msg->comprehension_required_unknown_attributes; + else + ++msg->comprehension_optional_unknown_attributes; + r_log(NR_LOG_STUN, LOG_INFO, "Unrecognized attribute: 0x%04x", attr->type); + } + else { + attr_info->name = attr_info->name; + attr->type_name = attr_info->codec->name; + + if (attr->type == NR_STUN_ATTR_MESSAGE_INTEGRITY) { + if (get_password && get_password(arg, msg, &password) == 0) { + if (password->len > sizeof(attr->u.message_integrity.password)) { + r_log(NR_LOG_STUN, LOG_WARNING, "Password too long: %d bytes", password->len); + ABORT(R_FAILED); + } + + memcpy(attr->u.message_integrity.password, password->data, password->len); + attr->u.message_integrity.passwordlen = password->len; + } + else { + /* set to user "not found" */ + attr->u.message_integrity.unknown_user = 1; + } + } + else if (attr->type == NR_STUN_ATTR_OLD_XOR_MAPPED_ADDRESS) { + attr->type = NR_STUN_ATTR_XOR_MAPPED_ADDRESS; + r_log(NR_LOG_STUN, LOG_INFO, "Translating obsolete XOR-MAPPED-ADDRESS type"); + } + + if ((r=attr_info->codec->decode(attr_info, attr->length, msg->buffer, offset+4, msg->length, &attr->u))) { + if (r == SKIP_ATTRIBUTE_DECODE) { + r_log(NR_LOG_STUN, LOG_INFO, "Skipping %s", attr_info->name); + } + else { + r_log(NR_LOG_STUN, LOG_WARNING, "Unable to parse %s", attr_info->name); + } + + attr->invalid = 1; + } + else { + attr_info->codec->print(attr_info, "Parsed", &attr->u); + +#ifdef USE_STUN_PEDANTIC + r_log(NR_LOG_STUN, LOG_DEBUG, "Before pedantic attr_info checks"); + if (attr_info->illegal) { + if ((r=attr_info->illegal(attr_info, attr->length, &attr->u))) { + r_log(NR_LOG_STUN, LOG_WARNING, "Failed pedantic attr_info checks"); + ABORT(r); + } + } + r_log(NR_LOG_STUN, LOG_DEBUG, "After pedantic attr_info checks"); +#endif /* USE_STUN_PEDANTIC */ + } + } + + offset += attr->encoding_length; + size -= attr->encoding_length; + } + +#ifdef SANITY_CHECKS + sanity_check_encoding_stuff(msg); +#endif /* SANITY_CHECKS */ + + _status=0; + abort: + return _status; +} + -- cgit v1.2.3