summaryrefslogtreecommitdiffstats
path: root/third_party/sipcc/sdp_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/sipcc/sdp_utils.c')
-rw-r--r--third_party/sipcc/sdp_utils.c725
1 files changed, 725 insertions, 0 deletions
diff --git a/third_party/sipcc/sdp_utils.c b/third_party/sipcc/sdp_utils.c
new file mode 100644
index 0000000000..a5b558282e
--- /dev/null
+++ b/third_party/sipcc/sdp_utils.c
@@ -0,0 +1,725 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+#include <limits.h>
+#include <ctype.h>
+#include "sdp_os_defs.h"
+#include "sipcc_sdp.h"
+#include "sdp_private.h"
+#include "sdp_log.h"
+
+#define MKI_BUF_LEN 4
+
+static const char* logTag = "sdp_utils";
+
+// Actually checks for ASCII only unlike isdigit() on Windows.
+// Also avoids UB issues with isdigit() sign extension when
+// char is signed.
+int sdp_is_ascii_digit(const char c) {
+ return '0' <= c && c <= '9';
+}
+
+sdp_mca_t *sdp_alloc_mca (uint32_t line) {
+ sdp_mca_t *mca_p;
+
+ /* Allocate resource for new media stream. */
+ mca_p = (sdp_mca_t *)SDP_MALLOC(sizeof(sdp_mca_t));
+ if (mca_p == NULL) {
+ return (NULL);
+ }
+ /* Initialize mca structure */
+ mca_p->media = SDP_MEDIA_INVALID;
+ mca_p->conn.nettype = SDP_NT_INVALID;
+ mca_p->conn.addrtype = SDP_AT_INVALID;
+ mca_p->conn.conn_addr[0] = '\0';
+ mca_p->conn.is_multicast = FALSE;
+ mca_p->conn.ttl = 0;
+ mca_p->conn.num_of_addresses = 0;
+ mca_p->transport = SDP_TRANSPORT_INVALID;
+ mca_p->port = SDP_INVALID_VALUE;
+ mca_p->num_ports = SDP_INVALID_VALUE;
+ mca_p->vpi = SDP_INVALID_VALUE;
+ mca_p->vci = 0;
+ mca_p->vcci = SDP_INVALID_VALUE;
+ mca_p->cid = SDP_INVALID_VALUE;
+ mca_p->num_payloads = 0;
+ mca_p->sessinfo_found = FALSE;
+ mca_p->encrypt.encrypt_type = SDP_ENCRYPT_INVALID;
+ mca_p->media_attrs_p = NULL;
+ mca_p->next_p = NULL;
+ mca_p->mid = 0;
+ mca_p->bw.bw_data_count = 0;
+ mca_p->bw.bw_data_list = NULL;
+ mca_p->line_number = line;
+ mca_p->sctp_fmt = SDP_SCTP_MEDIA_FMT_UNKNOWN;
+
+ return (mca_p);
+}
+
+/*
+ * next_token
+ *
+ * copy token param with chars from str until null, cr, lf, or one of the delimiters is found.
+ * delimiters at the beginning will be skipped.
+ * The pointer *string_of_tokens is moved forward to the next token on sucess.
+ *
+ */
+static sdp_result_e next_token(const char **string_of_tokens, char *token, unsigned token_max_len, const char *delim)
+{
+ int flag2moveon = 0;
+ const char *str;
+ const char *token_end;
+ const char *next_delim;
+
+ if (!string_of_tokens || !*string_of_tokens || !token || !delim) {
+ return SDP_FAILURE;
+ }
+
+ str = *string_of_tokens;
+ token_end = token + token_max_len - 1;
+
+ /* Locate front of token, skipping any delimiters */
+ for ( ; ((*str != '\0') && (*str != '\n') && (*str != '\r')); str++) {
+ flag2moveon = 1; /* Default to move on unless we find a delimiter */
+ for (next_delim=delim; *next_delim; next_delim++) {
+ if (*str == *next_delim) {
+ flag2moveon = 0;
+ break;
+ }
+ }
+ if( flag2moveon ) {
+ break; /* We're at the beginning of the token */
+ }
+ }
+
+ /* Make sure there's really a token present. */
+ if ((*str == '\0') || (*str == '\n') || (*str == '\r')) {
+ return SDP_EMPTY_TOKEN;
+ }
+
+ /* Now locate end of token */
+ flag2moveon = 0;
+
+ while ((token < token_end) &&
+ (*str != '\0') && (*str != '\n') && (*str != '\r')) {
+ for (next_delim=delim; *next_delim; next_delim++) {
+ if (*str == *next_delim) {
+ flag2moveon = 1;
+ break;
+ }
+ }
+ if( flag2moveon ) {
+ break;
+ } else {
+ *token++ = *str++;
+ }
+ }
+
+ /* mark end of token */
+ *token = '\0';
+
+ /* set the string of tokens to the next token */
+ *string_of_tokens = str;
+
+ return SDP_SUCCESS;
+}
+
+/*
+ * verify_sdescriptions_mki
+ *
+ * Verifies the syntax of the MKI parameter.
+ *
+ * mki = mki-value ":" mki-length
+ * mki-value = 1*DIGIT
+ * mki-length = 1*3DIGIT ; range 1..128
+ *
+ * Inputs:
+ * buf - ptr to start of MKI string assumes NULL
+ * terminated string
+ * mkiValue - buffer to store the MKI value, assumes calling
+ * function has provided memory for this.
+ * mkiLen - integer to store the MKI length
+ *
+ * Outputs:
+ * Returns TRUE if syntax is correct and stores the
+ * MKI value in mkiVal and stores the length in mkiLen.
+ * Returns FALSE otherwise.
+ */
+
+tinybool
+verify_sdescriptions_mki (char *buf, char *mkiVal, uint16_t *mkiLen)
+{
+
+ char *ptr,
+ mkiValBuf[SDP_SRTP_MAX_MKI_SIZE_BYTES],
+ mkiLenBuf[MKI_BUF_LEN];
+ int idx = 0;
+ unsigned long strtoul_result;
+ char *strtoul_end;
+
+ ptr = buf;
+ /* MKI must begin with a digit */
+ if (!ptr || (!sdp_is_ascii_digit(*ptr))) {
+ return FALSE;
+ }
+
+ /* scan until we reach a non-digit or colon */
+ while (*ptr) {
+ if (*ptr == ':') {
+ /* terminate the MKI value */
+ mkiValBuf[idx] = 0;
+ ptr++;
+ break;
+ } else if ((sdp_is_ascii_digit(*ptr) && (idx < SDP_SRTP_MAX_MKI_SIZE_BYTES-1))) {
+ mkiValBuf[idx++] = *ptr;
+ } else {
+ return FALSE;
+ }
+
+ ptr++;
+ }
+
+ /* there has to be a mki length */
+ if (*ptr == 0) {
+ return FALSE;
+ }
+
+ idx = 0;
+
+ /* verify the mki length (max 3 digits) */
+ while (*ptr) {
+ if (sdp_is_ascii_digit(*ptr) && (idx < 3)) {
+ mkiLenBuf[idx++] = *ptr;
+ } else {
+ return FALSE;
+ }
+
+ ptr++;
+ }
+
+ mkiLenBuf[idx] = 0;
+
+ errno = 0;
+ strtoul_result = strtoul(mkiLenBuf, &strtoul_end, 10);
+
+ /* mki len must be between 1..128 */
+ if (errno || mkiLenBuf == strtoul_end || strtoul_result < 1 || strtoul_result > 128) {
+ *mkiLen = 0;
+ return FALSE;
+ }
+
+ *mkiLen = (uint16_t) strtoul_result;
+ sstrncpy(mkiVal, mkiValBuf, MKI_BUF_LEN);
+
+ return TRUE;
+}
+
+/*
+ * verify_srtp_lifetime
+ *
+ * Verifies the Lifetime parameter syntax.
+ *
+ * lifetime = ["2^"] 1*(DIGIT)
+ *
+ * Inputs:
+ * buf - pointer to start of lifetime string. Assumes string is
+ * NULL terminated.
+ * Outputs:
+ * Returns TRUE if syntax is correct. Returns FALSE otherwise.
+ */
+
+tinybool
+verify_sdescriptions_lifetime (char *buf)
+{
+
+ char *ptr;
+ tinybool tokenFound = FALSE;
+
+ ptr = buf;
+ if (!ptr || *ptr == 0) {
+ return FALSE;
+ }
+
+ while (*ptr) {
+ if (*ptr == '^') {
+ if (tokenFound) {
+ /* make sure we don't have multiple ^ */
+ return FALSE;
+ } else {
+ tokenFound = TRUE;
+ /* Lifetime is in power of 2 format, make sure first and second
+ * chars are 2^
+ */
+
+ if (buf[0] != '2' || buf[1] != '^') {
+ return FALSE;
+ }
+ }
+ } else if (!sdp_is_ascii_digit(*ptr)) {
+ return FALSE;
+ }
+
+ ptr++;
+
+ }
+
+ /* Make sure if the format is 2^ that there is a number after the ^. */
+ if (tokenFound) {
+ if (strlen(buf) <= 2) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * sdp_validate_maxprate
+ *
+ * This function validates that the string passed in is of the form:
+ * packet-rate = 1*DIGIT ["." 1*DIGIT]
+ */
+tinybool
+sdp_validate_maxprate(const char *string_parm)
+{
+ tinybool retval = FALSE;
+
+ if (string_parm && (*string_parm)) {
+ while (sdp_is_ascii_digit(*string_parm)) {
+ string_parm++;
+ }
+
+ if (*string_parm == '.') {
+ string_parm++;
+ while (sdp_is_ascii_digit(*string_parm)) {
+ string_parm++;
+ }
+ }
+
+ if (*string_parm == '\0') {
+ retval = TRUE;
+ } else {
+ retval = FALSE;
+ }
+ }
+
+ return retval;
+}
+
+char *sdp_findchar (const char *ptr, char *char_list)
+{
+ int i;
+
+ for (;*ptr != '\0'; ptr++) {
+ for (i=0; char_list[i] != '\0'; i++) {
+ if (*ptr == char_list[i]) {
+ return ((char *)ptr);
+ }
+ }
+ }
+ return ((char *)ptr);
+}
+
+/* Locate the next token in a line. The delim characters are passed in
+ * as a param. The token also will not go past a new line char or the
+ * end of the string. Skip any delimiters before the token.
+ */
+const char *sdp_getnextstrtok (const char *str, char *tokenstr, unsigned tokenstr_len,
+ const char *delim, sdp_result_e *result)
+{
+ const char *token_list = str;
+
+ if (!str || !tokenstr || !delim || !result) {
+ if (result) {
+ *result = SDP_FAILURE;
+ }
+ return str;
+ }
+
+ *result = next_token(&token_list, tokenstr, tokenstr_len, delim);
+
+ return token_list;
+}
+
+
+
+/* Locate the next null ("-") or numeric token in a string. The delim
+ * characters are passed in as a param. The token also will not go past
+ * a new line char or the end of the string. Skip any delimiters before
+ * the token.
+ */
+uint32_t sdp_getnextnumtok_or_null (const char *str, const char **str_end,
+ const char *delim, tinybool *null_ind,
+ sdp_result_e *result)
+{
+ const char *token_list = str;
+ char temp_token[SDP_MAX_STRING_LEN];
+ char *strtoul_end;
+ unsigned long numval;
+
+ if (null_ind) {
+ *null_ind = FALSE;
+ }
+
+ if (!str || !str_end || !delim || !null_ind || !result) {
+ if (result) {
+ *result = SDP_FAILURE;
+ }
+ return 0;
+ }
+
+ *result = next_token(&token_list, temp_token, sizeof(temp_token), delim);
+
+ if (*result != SDP_SUCCESS) {
+ return 0;
+ }
+
+ /* First see if its the null char ("-") */
+ if (temp_token[0] == '-') {
+ *null_ind = TRUE;
+ *result = SDP_SUCCESS;
+ *str_end = str;
+ return 0;
+ }
+
+ errno = 0;
+ numval = strtoul(temp_token, &strtoul_end, 10);
+
+ if (errno || strtoul_end == temp_token || numval > UINT_MAX) {
+ *result = SDP_FAILURE;
+ return 0;
+ }
+
+ *result = SDP_SUCCESS;
+ *str_end = token_list;
+ return (uint32_t) numval;
+}
+
+
+/* Locate the next numeric token in a string. The delim characters are
+ * passed in as a param. The token also will not go past a new line char
+ * or the end of the string. Skip any delimiters before the token.
+ */
+uint32_t sdp_getnextnumtok (const char *str, const char **str_end,
+ const char *delim, sdp_result_e *result)
+{
+ const char *token_list = str;
+ char temp_token[SDP_MAX_STRING_LEN];
+ char *strtoul_end;
+ unsigned long numval;
+
+ if (!str || !str_end || !delim || !result) {
+ if (result) {
+ *result = SDP_FAILURE;
+ }
+ return 0;
+ }
+
+ *result = next_token(&token_list, temp_token, sizeof(temp_token), delim);
+
+ if (*result != SDP_SUCCESS) {
+ return 0;
+ }
+
+ errno = 0;
+ numval = strtoul(temp_token, &strtoul_end, 10);
+
+ if (errno || strtoul_end == temp_token || numval > UINT_MAX) {
+ *result = SDP_FAILURE;
+ return 0;
+ }
+
+ *result = SDP_SUCCESS;
+ *str_end = token_list;
+ return (uint32_t) numval;
+}
+
+
+/*
+ * SDP Crypto Utility Functions.
+ *
+ * First a few common definitions.
+ */
+
+/*
+ * Constants
+ *
+ * crypto_string = The string used to identify the start of sensative
+ * crypto data.
+ *
+ * inline_string = The string used to identify the start of key/salt
+ * crypto data.
+ *
+ * star_string = The string used to overwrite sensative data.
+ *
+ * '*_strlen' = The length of '*_string' in bytes (not including '\0')
+ */
+static const char crypto_string[] = "X-crypto:";
+static const int crypto_strlen = sizeof(crypto_string) - 1;
+static const char inline_string[] = "inline:";
+static const int inline_strlen = sizeof(inline_string) - 1;
+/* 40 characters is the current maximum for a Base64 encoded key/salt */
+static const char star_string[] = "****************************************";
+static const int star_strlen = sizeof(star_string) - 1;
+
+/*
+ * MIN_CRYPTO_STRING_SIZE_BYTES = This value defines the minimum
+ * size of a string that could contain a key/salt. This value
+ * is used to skip out of parsing when there is no reasonable
+ * assumption that sensative data will be found. The general
+ * format of a SRTP Key Salt in SDP looks like:
+ *
+ * X-crypto:<crypto_suite_name> inline:<master_key_salt>||
+ *
+ * if <crypto_suite_name> and <master_key_salt> is at least
+ * one character and one space is used before the "inline:",
+ * then this translates to a size of (aligned by collumn from
+ * the format shown above):
+ *
+ * 9+ 1+ 1+7+ 1+ 2 = 21
+ *
+ */
+#define MIN_CRYPTO_STRING_SIZE_BYTES 21
+
+/*
+ * Utility macros
+ *
+ * CHAR_IS_WHITESPACE = macro to determine if the passed _test_char
+ * is whitespace.
+ *
+ * SKIP_WHITESPACE = Macro to advance _cptr to the next non-whitespace
+ * character. _cptr will not be advanced past _max_cptr.
+ *
+ * FIND_WHITESPACE = Macro to advance _cptr until whitespace is found.
+ * _cptr will not be advanced past _max_cptr.
+ */
+#define CHAR_IS_WHITESPACE(_test_char) \
+ ((((_test_char)==' ')||((_test_char)=='\t'))?1:0)
+
+#define SKIP_WHITESPACE(_cptr, _max_cptr) \
+ while ((_cptr)<=(_max_cptr)) { \
+ if (!CHAR_IS_WHITESPACE(*(_cptr))) break; \
+ (_cptr)++; \
+ }
+
+#define FIND_WHITESPACE(_cptr, _max_cptr) \
+ while ((_cptr)<=(_max_cptr)) { \
+ if (CHAR_IS_WHITESPACE(*(_cptr))) break; \
+ (_cptr)++; \
+ }
+
+/* Function: sdp_crypto_debug
+ * Description: Check the passed buffer for sensitive data that should
+ * not be output (such as SRTP Master Key/Salt) and output
+ * the buffer as debug. Sensitive data will be replaced
+ * with the '*' character(s). This function may be used
+ * to display very large buffers so this function ensures
+ * that buginf is not overloaded.
+ * Parameters: buffer pointer to the message buffer to filter.
+ * length_bytes size of message buffer in bytes.
+ * Returns: Nothing.
+ */
+void sdp_crypto_debug (char *buffer, ulong length_bytes)
+{
+ char *current, *start;
+ char *last = buffer + length_bytes;
+ int result;
+
+ /*
+ * For SRTP Master Key/Salt has the form:
+ * X-crypto:<crypto_suite_name> inline:<master_key_salt>||
+ * Where <master_key_salt> is the data to elide (filter).
+ */
+ for (start=current=buffer;
+ current<=last-MIN_CRYPTO_STRING_SIZE_BYTES;
+ current++) {
+ if ((*current == 'x') || (*current == 'X')) {
+ result = cpr_strncasecmp(current, crypto_string, crypto_strlen);
+ if (!result) {
+ current += crypto_strlen;
+ if (current > last) break;
+
+ /* Skip over crypto suite name */
+ FIND_WHITESPACE(current, last);
+
+ /* Skip over whitespace */
+ SKIP_WHITESPACE(current, last);
+
+ /* identify inline keyword */
+ result = cpr_strncasecmp(current, inline_string, inline_strlen);
+ if (!result) {
+ int star_count = 0;
+
+ current += inline_strlen;
+ if (current > last) break;
+
+ sdp_dump_buffer(start, current - start);
+
+ /* Hide sensitive key/salt data */
+ while (current<=last) {
+ if (*current == '|' || *current == '\n') {
+ /* Done, print the stars */
+ while (star_count > star_strlen) {
+ /*
+ * This code is only for the case where
+ * too much base64 data was supplied
+ */
+ sdp_dump_buffer((char*)star_string, star_strlen);
+ star_count -= star_strlen;
+ }
+ sdp_dump_buffer((char*)star_string, star_count);
+ break;
+ } else {
+ star_count++;
+ current++;
+ }
+ }
+ /* Update start pointer */
+ start=current;
+ }
+ }
+ }
+ }
+
+ if (last > start) {
+ /* Display remainder of buffer */
+ sdp_dump_buffer(start, last - start);
+ }
+}
+
+/*
+ * sdp_debug_msg_filter
+ *
+ * DESCRIPTION
+ * Check the passed message buffer for sensitive data that should
+ * not be output (such as SRTP Master Key/Salt). Sensitive data
+ * will be replaced with the '*' character(s).
+ *
+ * PARAMETERS
+ * buffer: pointer to the message buffer to filter.
+ *
+ * length_bytes: size of message buffer in bytes.
+ *
+ * RETURN VALUE
+ * The buffer modified.
+ */
+char * sdp_debug_msg_filter (char *buffer, ulong length_bytes)
+{
+ char *current;
+ char *last = buffer + length_bytes;
+ int result;
+
+ SDP_PRINT("\n%s:%d: Eliding sensitive data from debug output",
+ __FILE__, __LINE__);
+ /*
+ * For SRTP Master Key/Salt has the form:
+ * X-crypto:<crypto_suite_name> inline:<master_key_salt>||
+ * Where <master_key_salt> is the data to elide (filter).
+ */
+ for (current=buffer;
+ current<=last-MIN_CRYPTO_STRING_SIZE_BYTES;
+ current++) {
+ if ((*current == 'x') || (*current == 'X')) {
+ result = cpr_strncasecmp(current, crypto_string, crypto_strlen);
+ if (!result) {
+ current += crypto_strlen;
+ if (current > last) break;
+
+ /* Skip over crypto suite name */
+ FIND_WHITESPACE(current, last);
+
+ /* Skip over whitespace */
+ SKIP_WHITESPACE(current, last);
+
+ /* identify inline keyword */
+ result = cpr_strncasecmp(current, inline_string, inline_strlen);
+ if (!result) {
+ current += inline_strlen;
+ if (current > last) break;
+
+ /* Hide sensitive key/salt data */
+ while (current<=last) {
+ if (*current == '|' || *current == '\n') {
+ /* Done */
+ break;
+ } else {
+ *current = '*';
+ current++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return buffer;
+}
+
+
+/* Function: sdp_checkrange
+ * Description: This checks the range of a ulong value to make sure its
+ * within the range of 0 and 4Gig. stroul cannot be used since
+ * for values greater greater than 4G, stroul will either wrap
+ * around or return ULONG_MAX.
+ * Parameters: sdp_p Pointer to the sdp structure
+ * num The number to check the range for
+ * u_val This variable get populated with the ulong value
+ * if the number is within the range.
+ * Returns: tinybool - returns TRUE if the number passed is within the
+ * range, FALSE otherwise
+ */
+tinybool sdp_checkrange (sdp_t *sdp_p, char *num, ulong *u_val)
+{
+ ulong l_val;
+ char *endP = NULL;
+ *u_val = 0;
+
+ if (!num || !*num) {
+ return FALSE;
+ }
+
+ if (*num == '-') {
+ if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+ SDPLogError(logTag, "%s ERROR: Parameter value is a negative number: %s",
+ sdp_p->debug_str, num);
+ }
+ return FALSE;
+ }
+
+ l_val = strtoul(num, &endP, 10);
+ if (*endP == '\0') {
+
+ if (l_val > 4294967295UL) {
+ if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+ SDPLogError(logTag, "%s ERROR: Parameter value: %s is greater than 4294967295",
+ sdp_p->debug_str, num);
+ }
+ return FALSE;
+ }
+
+ if (l_val == 4294967295UL) {
+ /*
+ * On certain platforms where ULONG_MAX is equivalent to
+ * 4294967295, strtoul will return ULONG_MAX even if the the
+ * value of the string is greater than 4294967295. To detect
+ * that scenario we make an explicit check here.
+ */
+ if (strcmp("4294967295", num)) {
+ if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+ SDPLogError(logTag, "%s ERROR: Parameter value: %s is greater than 4294967295",
+ sdp_p->debug_str, num);
+ }
+ return FALSE;
+ }
+ }
+ }
+ *u_val = l_val;
+ return TRUE;
+}
+
+#undef CHAR_IS_WHITESPACE
+#undef SKIP_WHITESPACE
+#undef FIND_WHITESPACE