/* 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/. */
for (i=0; i < type_count; i++) { if (!cpr_strncasecmp(tmp, types[i].name, types[i].strlen)) { return i;
}
} return unknown_value;
}
/* * Helper function for adding nv-pair where value is string.
*/ staticvoid sdp_append_name_and_string(flex_string *fs, constchar *name, constchar *value,
tinybool semicolon)
{
flex_string_sprintf(fs, "%s%s=%s",
semicolon ? ";" : "",
name,
value);
}
/* * Helper function for adding nv-pair where value is unsigned.
*/ staticvoid sdp_append_name_and_unsigned(flex_string *fs, constchar *name, unsignedint value,
tinybool semicolon)
{
flex_string_sprintf(fs, "%s%s=%u",
semicolon ? ";" : "",
name,
value);
}
/* Function: sdp_parse_attribute * Description: Figure out the type of attribute and call the appropriate * parsing routine. If parsing errors are encountered, * warnings will be printed and the attribute will be ignored. * Unrecognized/invalid attributes do not cause overall parsing * errors. All errors detected are noted as warnings. * Parameters: sdp_p The SDP handle returned by sdp_init_description. * level The level to check for the attribute. * ptr Pointer to the attribute string to parse.
*/
sdp_result_e sdp_parse_attribute (sdp_t *sdp_p, uint16_t level, constchar *ptr)
{ int i;
uint8_t xcpar_flag = FALSE;
sdp_result_e result;
sdp_mca_t *mca_p=NULL;
sdp_attr_t *attr_p;
sdp_attr_t *next_attr_p;
sdp_attr_t *prev_attr_p = NULL; char tmp[SDP_MAX_STRING_LEN];
/* Validate the level */ if (level != SDP_SESSION_LEVEL) {
mca_p = sdp_find_media_level(sdp_p, level); if (mca_p == NULL) { return (SDP_FAILURE);
}
}
/* Find the attribute type. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), ": \t", &result); if (ptr == NULL) {
sdp_parse_error(sdp_p, "%s No attribute type specified, parse failed.", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
} if (ptr[0] == ':') { /* Skip the ':' char for parsing attribute parameters. */
ptr++;
} if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s No attribute type specified, parse failed.", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* If this is an X-cpar or cpar attribute, set the flag. The attribute
* type will be changed by the parse. */ if ((attr_p->type == SDP_ATTR_X_CPAR) ||
(attr_p->type == SDP_ATTR_CPAR)) {
xcpar_flag = TRUE;
}
/* Parse the attribute. */
result = sdp_attr[attr_p->type].parse_func(sdp_p, attr_p, ptr); if (result != SDP_SUCCESS) {
sdp_free_attr(attr_p); /* Return success so the parse won't fail. We don't want to * fail on errors with attributes but just ignore them.
*/ return (SDP_SUCCESS);
}
/* If this was an X-cpar/cpar attribute, it was hooked into the X-cap/cdsc * structure, so we're finished.
*/ if (xcpar_flag == TRUE) { return (result);
}
/* Add the attribute in the appropriate place. */ if (level == SDP_SESSION_LEVEL) { for (next_attr_p = sdp_p->sess_attrs_p; next_attr_p != NULL;
prev_attr_p = next_attr_p,
next_attr_p = next_attr_p->next_p) {
; /* Empty for */
} if (prev_attr_p == NULL) {
sdp_p->sess_attrs_p = attr_p;
} else {
prev_attr_p->next_p = attr_p;
}
} else { for (next_attr_p = mca_p->media_attrs_p; next_attr_p != NULL;
prev_attr_p = next_attr_p,
next_attr_p = next_attr_p->next_p) {
; /* Empty for */
} if (prev_attr_p == NULL) {
mca_p->media_attrs_p = attr_p;
} else {
prev_attr_p->next_p = attr_p;
}
}
return (result);
}
/* Build all of the attributes defined for the specified level. */
sdp_result_e sdp_build_attribute (sdp_t *sdp_p, uint16_t level, flex_string *fs)
{
sdp_attr_t *attr_p;
sdp_mca_t *mca_p=NULL;
sdp_result_e result;
if (level == SDP_SESSION_LEVEL) {
attr_p = sdp_p->sess_attrs_p;
} else {
mca_p = sdp_find_media_level(sdp_p, level); if (mca_p == NULL) { return (SDP_FAILURE);
}
attr_p = mca_p->media_attrs_p;
} /* Re-initialize the current capability number for this new level. */
sdp_p->cur_cap_num = 1;
/* Build all of the attributes for this level. Note that if there
* is a problem building an attribute, we don't fail but just ignore it.*/ while (attr_p != NULL) { if (attr_p->type >= SDP_MAX_ATTR_TYPES) { if (sdp_p->debug_flag[SDP_DEBUG_WARNINGS]) {
SDPLogDebug(logTag, "%s Invalid attribute type to build (%u)",
sdp_p->debug_str, (unsigned)attr_p->type);
}
} else {
result = sdp_attr[attr_p->type].build_func(sdp_p, attr_p, fs);
if (result != SDP_SUCCESS) {
SDPLogError(logTag, "%s error building attribute %d", __FUNCTION__, result); return result;
}
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Built a=%s attribute line", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type));
}
}
attr_p = attr_p->next_p;
}
/* * sdp_parse_attr_maxprate * * This function parses maxprate attribute lines. The ABNF for this a= * line is: * max-p-rate-def = "a" "=" "maxprate" ":" packet-rate CRLF * packet-rate = 1*DIGIT ["." 1*DIGIT] * * Returns: * SDP_INVALID_PARAMETER - If we are unable to parse the string OR if * packet-rate is not in the right format as per * the ABNF. * * SDP_SUCCESS - If we are able to successfully parse the a= line.
*/
sdp_result_e sdp_parse_attr_maxprate (sdp_t *sdp_p, sdp_attr_t *attr_p, constchar *ptr)
{
sdp_result_e result;
if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No string token found for %s attribute",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
} else { if (!sdp_validate_maxprate(attr_p->attr.string_val)) {
sdp_parse_error(sdp_p, "%s is not a valid maxprate value.",
attr_p->attr.string_val);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* * sdp_attr_fmtp_no_value * Helper function for sending the warning when a parameter value is * missing. *
*/ staticvoid sdp_attr_fmtp_no_value(sdp_t *sdp, constchar *param_name)
{
sdp_parse_error(sdp, "%s Warning: No %s value specified for fmtp attribute",
sdp->debug_str, param_name);
sdp->conf_p->num_invalid_param++;
}
/* * sdp_attr_fmtp_invalid_value * Helper function for sending the warning when a parameter value is * incorrect. *
*/ staticvoid sdp_attr_fmtp_invalid_value(sdp_t *sdp, constchar *param_name, constchar* param_value)
{
sdp_parse_error(sdp, "%s Warning: Invalid %s: %s specified for fmtp attribute",
sdp->debug_str, param_name, param_value);
sdp->conf_p->num_invalid_param++;
}
/* * sdp_verify_attr_fmtp_telephone_event * Helper function for verifying the telephone-event fmtp format
*/ static sdp_result_e sdp_verify_attr_fmtp_telephone_event(char *fmtpVal)
{
size_t len = fmtpVal ? strlen(fmtpVal) : 0;
// make sure the basics are good: // - at least 1 character // - no illegal chars // - first char is a number if (len < 1
|| strspn(fmtpVal, "0123456789,-") != len
|| PL_strstr(fmtpVal, ",,")
|| fmtpVal[len-1] == ','
|| !('0' <= fmtpVal[0] && fmtpVal[0] <= '9')) { return SDP_INVALID_PARAMETER;
}
// Now that we've passed the basic sanity test, copy the string so we // can tokenize and check the format of the tokens without disturbing // the input string. char dtmf_tones[SDP_MAX_STRING_LEN+1];
PL_strncpyz(dtmf_tones, fmtpVal, sizeof(dtmf_tones));
while (temp != NULL) {
len = strlen(temp); if (len > 5) { // an example of a max size token is "11-15", so if the // token is longer than 5 it is bad return SDP_INVALID_PARAMETER;
}
// case where we have 1 or 2 characters, example 4 or 23 if (len < 3 && strspn(temp, "0123456789") != len) { return SDP_INVALID_PARAMETER;
} elseif (len >= 3) { // case where we have 3-5 characters, ex 3-5, 2-33, or 10-20
sdp_result_e result1 = SDP_SUCCESS;
sdp_result_e result2 = SDP_SUCCESS;
uint8_t low_val;
uint8_t high_val;
low_val = (uint8_t)sdp_getnextnumtok(temp, (constchar **)&temp, "-", &result1);
high_val = (uint8_t)sdp_getnextnumtok(temp, (constchar **)&temp, "-", &result2); if (temp[0] // we don't want to find a second hyphen
|| result1 != SDP_SUCCESS
|| result2 != SDP_SUCCESS) { return SDP_INVALID_PARAMETER;
}
/* Note: The fmtp attribute formats currently handled are: * fmtp:<payload type> <event>,<event>... * fmtp:<payload_type> [annexa=yes/no] [annexb=yes/no] [bitrate=<value>] * [QCIF =<value>] [CIF =<value>] [MaxBR = <value>] one or more * Other FMTP params as per H.263, H.263+, H.264 codec support. * Note -"value" is a numeric value > 0 and each event is a * single number or a range separated by a '-'. * Example: fmtp:101 1,3-15,20 * Video codecs have annexes that can be listed in the following legal formats: * a) a=fmtp:34 param1=token;D;I;J;K=1;N=2;P=1,3 * b) a=fmtp:34 param1=token;D;I;J;K=1;N=2;P=1,3;T * c) a=fmtp:34 param1=token;D;I;J *
*/
sdp_result_e sdp_get_fmtp_tok(sdp_t *sdp_p, constchar** fmtp_ptr, constchar* fmtp_name, char* buf,
size_t buf_size, char** tok)
{
sdp_result_e result1 = SDP_SUCCESS;
/* * set default value of packetization mode and level-asymmetry-allowed. If * remote sdp does not specify any value for these two parameters, then the * default value will be assumed for remote sdp. If remote sdp does specify * any value for these parameters, then default value will be overridden.
*/
fmtp_p->packetization_mode = SDP_DEFAULT_PACKETIZATION_MODE_VALUE;
fmtp_p->level_asymmetry_allowed = SDP_DEFAULT_LEVEL_ASYMMETRY_ALLOWED_VALUE;
temp=PL_strtok_r(tok, ":", &strtok_state); if (temp) {
iter=1; /* get par width and par height for the aspect ratio */ while (temp != NULL) {
errno = 0;
strtoul_result = strtoul(temp, &strtoul_end, 10);
} elseif (cpr_strncasecmp(tmp,sdp_fmtp_codec_param[47].name,
sdp_fmtp_codec_param[47].strlen) == 0) {
result1 = sdp_get_fmtp_tok(sdp_p, &fmtp_ptr, "maxcodedaudiobandwidth", tmp, sizeof(tmp), &tok); // this one is different for some reason. Most others don't increment // the num_invalid_param field. (mjf) if (result1 == SDP_INVALID_PARAMETER) { sdp_p->conf_p->num_invalid_param++; } if (result1 != SDP_SUCCESS) { SDP_FREE(temp_ptr); return result1; }
if (event_id % SDP_NE_BITS_PER_WORD) {
mask <<= 1;
} else { /* crossed a bitmap word boundary */
mask = SDP_NE_BIT_0; if (!range_start && !range_end && !fmtp_p->bmap[mapword]) { /* no events in this word, skip to the last event id
* in this bitmap word. */
event_id += SDP_NE_BITS_PER_WORD - 1; continue;
}
}
if (fmtp_p->bmap[mapword] & mask) { if (!range_start) {
range_start = TRUE;
min = max = (uint8_t)event_id;
} else {
max = (uint8_t)event_id;
}
range_end = (max == fmtp_p->maxval);
} else { /* If we were in the middle of a range, then we've hit the
* end. If we weren't, there is no end to hit. */
range_end = range_start;
}
/* If this is the end of the range, print it to the string. */ if (range_end) {
range_start = range_end = FALSE;
flex_string_sprintf(fs, "%u", min);
if (min != max) {
flex_string_sprintf(fs, "-%u", max);
}
/* Check qos status type */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No curr attr type specified.",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
attr_p->attr.curr.status_type = SDP_QOS_STATUS_TYPE_UNKNOWN; for (i=0; i < SDP_MAX_QOS_STATUS_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_qos_status_type[i].name,
sdp_qos_status_type[i].strlen) == 0) {
attr_p->attr.curr.status_type = (sdp_qos_status_types_e)i;
}
}
/* Find the qos direction. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No qos direction specified.",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
attr_p->attr.curr.direction = SDP_QOS_DIR_UNKNOWN; for (i=0; i < SDP_MAX_QOS_DIR; i++) { if (cpr_strncasecmp(tmp, sdp_qos_direction[i].name,
sdp_qos_direction[i].strlen) == 0) {
attr_p->attr.curr.direction = (sdp_qos_dir_e)i;
}
} if (attr_p->attr.curr.direction == SDP_QOS_DIR_UNKNOWN) {
sdp_parse_error(sdp_p, "%s Warning: QOS direction unrecognized (%s)",
sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=%s, type %s status type %s, direction %s",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
sdp_get_curr_type_name(attr_p->attr.curr.type),
sdp_get_qos_status_type_name(attr_p->attr.curr.status_type),
sdp_get_qos_direction_name(attr_p->attr.curr.direction));
}
/* Find the strength tag. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No qos strength tag specified.",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
attr_p->attr.des.strength = SDP_QOS_STRENGTH_UNKNOWN; for (i=0; i < SDP_MAX_QOS_STRENGTH; i++) { if (cpr_strncasecmp(tmp, sdp_qos_strength[i].name,
sdp_qos_strength[i].strlen) == 0) {
attr_p->attr.des.strength = (sdp_qos_strength_e)i;
}
} if (attr_p->attr.des.strength == SDP_QOS_STRENGTH_UNKNOWN) {
sdp_parse_error(sdp_p, "%s Warning: QOS strength tag unrecognized (%s)",
sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Check qos status type */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No des attr type specified.",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
attr_p->attr.des.status_type = SDP_QOS_STATUS_TYPE_UNKNOWN; for (i=0; i < SDP_MAX_QOS_STATUS_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_qos_status_type[i].name,
sdp_qos_status_type[i].strlen) == 0) {
attr_p->attr.des.status_type = (sdp_qos_status_types_e)i;
}
}
/* Find the qos direction. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No qos direction specified.",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
attr_p->attr.des.direction = SDP_QOS_DIR_UNKNOWN; for (i=0; i < SDP_MAX_QOS_DIR; i++) { if (cpr_strncasecmp(tmp, sdp_qos_direction[i].name,
sdp_qos_direction[i].strlen) == 0) {
attr_p->attr.des.direction = (sdp_qos_dir_e)i;
}
} if (attr_p->attr.des.direction == SDP_QOS_DIR_UNKNOWN) {
sdp_parse_error(sdp_p, "%s Warning: QOS direction unrecognized (%s)",
sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=%s, type %s strength %s status type %s, direction %s",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
sdp_get_des_type_name(attr_p->attr.des.type),
sdp_get_qos_strength_name(attr_p->attr.qos.strength),
sdp_get_qos_status_type_name(attr_p->attr.des.status_type),
sdp_get_qos_direction_name(attr_p->attr.des.direction));
}
/* Check qos status type */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No conf attr type specified.",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
attr_p->attr.conf.status_type = SDP_QOS_STATUS_TYPE_UNKNOWN; for (i=0; i < SDP_MAX_QOS_STATUS_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_qos_status_type[i].name,
sdp_qos_status_type[i].strlen) == 0) {
attr_p->attr.conf.status_type = (sdp_qos_status_types_e)i;
}
}
/* Find the qos direction. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No qos direction specified.",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
attr_p->attr.conf.direction = SDP_QOS_DIR_UNKNOWN; for (i=0; i < SDP_MAX_QOS_DIR; i++) { if (cpr_strncasecmp(tmp, sdp_qos_direction[i].name,
sdp_qos_direction[i].strlen) == 0) {
attr_p->attr.conf.direction = (sdp_qos_dir_e)i;
}
} if (attr_p->attr.conf.direction == SDP_QOS_DIR_UNKNOWN) {
sdp_parse_error(sdp_p, "%s Warning: QOS direction unrecognized (%s)",
sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=%s, type %s status type %s, direction %s",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
sdp_get_conf_type_name(attr_p->attr.conf.type),
sdp_get_qos_status_type_name(attr_p->attr.conf.status_type),
sdp_get_qos_direction_name(attr_p->attr.conf.direction));
}
/* * Parse a rtpmap or a sprtmap. Both formats use the same structure * the only difference being the keyword "rtpmap" vs "sprtmap". The * rtpmap field in the sdp_attr_t is used to store both mappings.
*/
sdp_result_e sdp_parse_attr_transport_map (sdp_t *sdp_p, sdp_attr_t *attr_p, constchar *ptr)
{
sdp_result_e result;
/* Find the payload type number. */
attr_p->attr.transport_map.payload_num =
(uint16_t)sdp_getnextnumtok(ptr, &ptr, " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: Invalid payload type specified for %s attribute.",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Find the encoding name. */
ptr = sdp_getnextstrtok(ptr, attr_p->attr.transport_map.encname, sizeof(attr_p->attr.transport_map.encname), "/ \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No encoding name specified in %s attribute.",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Find the clockrate. */
attr_p->attr.transport_map.clockrate =
sdp_getnextnumtok(ptr, &ptr, "/ \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No clockrate specified for " "%s attribute, set to default of 8000.",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
attr_p->attr.transport_map.clockrate = 8000;
}
/* Find the number of channels, if specified. This is optional. */ if (*ptr == '/') { /* If a '/' exists, expect something valid beyond it. */
attr_p->attr.transport_map.num_chan =
(uint16_t)sdp_getnextnumtok(ptr, &ptr, "/ \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: Invalid number of channels parameter" " for rtpmap attribute.", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
}
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=%s, payload type %u, encoding name %s, " "clockrate %u", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type),
attr_p->attr.transport_map.payload_num,
attr_p->attr.transport_map.encname,
attr_p->attr.transport_map.clockrate); if (attr_p->attr.transport_map.num_chan != 1) {
SDP_PRINT("/%u", attr_p->attr.transport_map.num_chan);
}
}
return (SDP_SUCCESS);
}
/* * Build a rtpmap or a sprtmap. Both formats use the same structure * the only difference being the keyword "rtpmap" vs "sprtmap". The * rtpmap field in the sdp_attr_t is used for both mappings.
*/
sdp_result_e sdp_build_attr_transport_map (sdp_t *sdp_p, sdp_attr_t *attr_p,
flex_string *fs)
{ if (attr_p->attr.transport_map.num_chan == 1) {
flex_string_sprintf(fs, "a=%s:%u %s/%u\r\n",
sdp_attr[attr_p->type].name,
attr_p->attr.transport_map.payload_num,
attr_p->attr.transport_map.encname,
attr_p->attr.transport_map.clockrate);
} else {
flex_string_sprintf(fs, "a=%s:%u %s/%u/%u\r\n",
sdp_attr[attr_p->type].name,
attr_p->attr.transport_map.payload_num,
attr_p->attr.transport_map.encname,
attr_p->attr.transport_map.clockrate,
attr_p->attr.transport_map.num_chan);
}
/* Set the capability pointer to NULL for now in case we encounter * an error in parsing.
*/
attr_p->attr.cap_p = NULL; /* Set the capability valid flag to FALSE in case we encounter an * error. If we do, we don't want to process any X-cpar/cpar attributes
* from this point until we process the next valid X-cap/cdsc attr. */
sdp_p->cap_valid = FALSE;
/* Allocate resource for new capability. Note that the capability * uses the same structure used for media lines.
*/
cap_p = sdp_alloc_mca(sdp_p->parse_line); if (cap_p == NULL) {
sdp_p->conf_p->num_no_resource++; return (SDP_NO_RESOURCE);
}
/* Find the capability number. We don't need to store this since we
* calculate it for ourselves as we need to. But it must be specified. */
(void)sdp_getnextnumtok(ptr, &ptr, "/ \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: Capability not specified for %s, " "unable to parse.", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type));
SDP_FREE(cap_p);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Find the media type. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s No media type specified for %s attribute, " "unable to parse.",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
SDP_FREE(cap_p);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
cap_p->media = SDP_MEDIA_UNSUPPORTED; for (i=0; i < SDP_MAX_MEDIA_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_media[i].name, sdp_media[i].strlen) == 0) {
cap_p->media = (sdp_media_e)i; break;
}
} if (cap_p->media == SDP_MEDIA_UNSUPPORTED) {
sdp_parse_error(sdp_p, "%s Warning: Media type unsupported (%s).",
sdp_p->debug_str, tmp);
SDP_FREE(cap_p);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Find the transport protocol type. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s No transport protocol type specified, " "unable to parse.", sdp_p->debug_str);
SDP_FREE(cap_p);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
cap_p->transport = SDP_TRANSPORT_UNSUPPORTED; for (i=0; i < SDP_MAX_TRANSPORT_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_transport[i].name,
sdp_transport[i].strlen) == 0) {
cap_p->transport = (sdp_transport_e)i; break;
}
} if (cap_p->transport == SDP_TRANSPORT_UNSUPPORTED) {
sdp_parse_error(sdp_p, "%s Warning: Transport protocol type unsupported (%s).",
sdp_p->debug_str, tmp);
SDP_FREE(cap_p);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Find payload formats. AAL2 X-cap lines allow multiple * transport/profile types per line, so these are handled differently.
*/ if ((cap_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
(cap_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
(cap_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) { /* Capability processing is not currently defined for AAL2 types
* with multiple profiles. We don't process. */
sdp_parse_error(sdp_p, "%s Warning: AAL2 profiles unsupported with " "%s attributes.", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type));
SDP_FREE(cap_p);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
} else { /* Transport is a non-AAL2 type. Parse payloads normally. */
sdp_parse_payload_types(sdp_p, cap_p, ptr); if (cap_p->num_payloads == 0) {
SDP_FREE(cap_p);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
}
attr_p->attr.cap_p = cap_p; /* * This capability attr is valid. We can now handle X-cpar or * cpar attrs.
*/
sdp_p->cap_valid = TRUE;
sdp_p->last_cap_inst++;
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed %s media type %s, Transport %s, " "Num payloads %u", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type),
sdp_get_media_name(cap_p->media),
sdp_get_transport_name(cap_p->transport),
cap_p->num_payloads);
} return (SDP_SUCCESS);
}
/* Get a pointer to the capability structure. */
cap_p = attr_p->attr.cap_p;
if (cap_p == NULL) {
SDPLogError(logTag, "%s Invalid %s attribute, unable to build.",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; /* Return success so build won't fail. */ return (SDP_SUCCESS);
}
/* Validate params for this capability line */ if ((cap_p->media >= SDP_MAX_MEDIA_TYPES) ||
(cap_p->transport >= SDP_MAX_TRANSPORT_TYPES)) {
SDPLogDebug(logTag, logTag, "%s Media or transport type invalid for %s " "attribute, unable to build.", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; /* Return success so build won't fail. */ return (SDP_SUCCESS);
}
/* If the X-cap line has AAL2 profiles, build them differently. */ if ((cap_p->transport == SDP_TRANSPORT_AAL2_ITU) ||
(cap_p->transport == SDP_TRANSPORT_AAL2_ATMF) ||
(cap_p->transport == SDP_TRANSPORT_AAL2_CUSTOM)) {
profile_p = cap_p->media_profiles_p; for (i=0; i < profile_p->num_profiles; i++) {
flex_string_sprintf(fs, "%s",
sdp_get_transport_name(profile_p->profile[i]));
flex_string_append(fs, "\r\n"); if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Built m= media line", sdp_p->debug_str);
} return SDP_SUCCESS;
}
/* Build the transport name */
flex_string_sprintf(fs, "%s", sdp_get_transport_name(cap_p->transport));
/* Build the format lists */ for (i=0; i < cap_p->num_payloads; i++) { if (cap_p->payload_indicator[i] == SDP_PAYLOAD_ENUM) {
flex_string_sprintf(fs, " %s",
sdp_get_payload_name((sdp_payload_e)cap_p->payload_type[i]));
} else {
flex_string_sprintf(fs, " %u", cap_p->payload_type[i]);
}
}
flex_string_append(fs, "\r\n");
/* Increment the current capability number for the next X-cap/cdsc attr. */
sdp_p->cur_cap_num += cap_p->num_payloads;
sdp_p->last_cap_type = attr_p->type;
/* Build any X-cpar/cpar attributes associated with this X-cap/cdsc line. */ return sdp_build_attr_cpar(sdp_p, cap_p->media_attrs_p, fs);
}
/* Make sure we've processed a valid X-cap/cdsc attr prior to this and
* if so, get the cap pointer. */ if (sdp_p->cap_valid == TRUE) {
sdp_attr_e cap_type;
if (attr_p->type == SDP_ATTR_CPAR) {
cap_type = SDP_ATTR_CDSC;
} else { /* Default to X-CAP for everything else */
cap_type = SDP_ATTR_X_CAP;
}
/* * Ensure there is no mixed syntax like CDSC followed by X-CPAR * or X-CAP followed by CPAR.
*/ if (((cap_attr_p->type == SDP_ATTR_CDSC) &&
(attr_p->type == SDP_ATTR_X_CPAR)) ||
( (cap_attr_p->type == SDP_ATTR_X_CAP) &&
(attr_p->type == SDP_ATTR_CPAR)) ) {
sdp_parse_error(sdp_p, "%s Warning: %s attribute inconsistent with " "prior %s attribute", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type),
sdp_get_attr_name(cap_attr_p->type));
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
cap_p = cap_attr_p->attr.cap_p;
/* a= is the only token we handle in an X-cpar/cpar attribute. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), "= \t", &result);
if ((result != SDP_SUCCESS) || (tmp[0] != 'a') || (tmp[1] != '\0')) {
sdp_parse_error(sdp_p, "%s Warning: Invalid token type in %s " "attribute, unable to parse", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
} /*sa_ignore NO_NULL_CHK *{ptr is valid since the pointer was checked earlier and the * function would have exited if NULL.}
*/ if (*ptr == '=') {
ptr++;
}
/* Find the attribute type. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), ": \t", &result); /*sa_ignore NO_NULL_CHK *{ptr is valid since the pointer was checked earlier and the * function would have exited if NULL.}
*/ if (ptr[0] == ':') { /* Skip the ':' char for parsing attribute parameters. */
ptr++;
} if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s No attribute type specified for %s attribute, unable to parse.",
sdp_p->debug_str,
sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Reset the type of the attribute from X-cpar/cpar to whatever the
* specified type is. */
attr_p->type = SDP_ATTR_INVALID;
attr_p->next_p = NULL; for (i=0; i < SDP_MAX_ATTR_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_attr[i].name, sdp_attr[i].strlen) == 0) {
attr_p->type = (sdp_attr_e)i;
}
} if (attr_p->type == SDP_ATTR_INVALID) {
sdp_parse_error(sdp_p, "%s Warning: Unrecognized attribute (%s) for %s attribute, unable to parse.",
sdp_p->debug_str, tmp,
sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* We don't allow recursion with the capability attributes. */ if ((attr_p->type == SDP_ATTR_X_SQN) ||
(attr_p->type == SDP_ATTR_X_CAP) ||
(attr_p->type == SDP_ATTR_X_CPAR) ||
(attr_p->type == SDP_ATTR_SQN) ||
(attr_p->type == SDP_ATTR_CDSC) ||
(attr_p->type == SDP_ATTR_CPAR)) {
sdp_parse_error(sdp_p, "%s Warning: Invalid attribute (%s) for %s" " attribute, unable to parse.", sdp_p->debug_str, tmp,
sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Parse the attribute. */
result = sdp_attr[attr_p->type].parse_func(sdp_p, attr_p, ptr); if (result != SDP_SUCCESS) { return (result);
}
/* Hook the attribute into the capability structure. */ if (cap_p->media_attrs_p == NULL) {
cap_p->media_attrs_p = attr_p;
} else { for (prev_attr_p = cap_p->media_attrs_p;
prev_attr_p->next_p != NULL;
prev_attr_p = prev_attr_p->next_p) {
; /* Empty for */
}
prev_attr_p->next_p = attr_p;
}
/* Determine whether to use cpar or X-cpar */ if (sdp_p->last_cap_type == SDP_ATTR_CDSC) {
cpar_name = sdp_get_attr_name(SDP_ATTR_CPAR);
} else { /* * Default to X-CPAR if anything else. This is the backward * compatible value.
*/
cpar_name = sdp_get_attr_name(SDP_ATTR_X_CPAR);
}
while (attr_p != NULL) { if (attr_p->type >= SDP_MAX_ATTR_TYPES) {
SDPLogDebug(logTag, "%s Invalid attribute type to build (%u)",
sdp_p->debug_str, (unsigned)attr_p->type);
} else {
flex_string_sprintf(fs, "a=%s: ", cpar_name);
result = sdp_attr[attr_p->type].build_func(sdp_p, attr_p, fs);
if (result == SDP_SUCCESS) { if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Built %s a=%s attribute line",
sdp_p->debug_str, cpar_name,
sdp_get_attr_name(attr_p->type));
}
}
}
attr_p = attr_p->next_p;
}
rtcp_p->port = (uint16_t)sdp_getnextnumtok(ptr, &ptr, " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: could not parse port for rtcp attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++;
return SDP_INVALID_PARAMETER;
}
/* The rest is optional, although it is all-or-nothing */
(void)sdp_getnextstrtok(ptr, nettype, sizeof(nettype), " \t", &result); if (result == SDP_EMPTY_TOKEN) { /* Nothing after the port */ return SDP_SUCCESS;
}
ptr = sdp_getnextstrtok(ptr, rtcp_p->addr, sizeof(rtcp_p->addr), " \t",
&result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: could not parse addr for rtcp attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++;
return SDP_INVALID_PARAMETER;
}
return SDP_SUCCESS;
}
sdp_result_e sdp_build_attr_rtcp (sdp_t *sdp_p, sdp_attr_t *attr_p,
flex_string *fs)
{ /* We should not be serializing SDP anyway, but we need this function until
* Bug 1112737 is resolved. */ return SDP_FAILURE;
}
/* Find the media direction role. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), ": \t", &result);
if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No role parameter specified for " "comediadir attribute.", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
attr_p->attr.comediadir.role = SDP_MEDIADIR_ROLE_UNSUPPORTED; for (i=0; i < SDP_MAX_MEDIADIR_ROLES; i++) { if (cpr_strncasecmp(tmp, sdp_mediadir_role[i].name,
sdp_mediadir_role[i].strlen) == 0) {
type_found = TRUE;
attr_p->attr.comediadir.role = (sdp_mediadir_role_e)i; break;
}
} if (attr_p->attr.comediadir.role == SDP_MEDIADIR_ROLE_UNSUPPORTED) {
sdp_parse_error(sdp_p, "%s Warning: Invalid role type specified for " "comediadir attribute (%s).", sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* If the role is passive, we don't expect any more params. */ if (attr_p->attr.comediadir.role == SDP_MEDIADIR_ROLE_PASSIVE) { if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=%s, passive",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
} return (SDP_SUCCESS);
}
/* Find the connection information if present */ /* parse to get the nettype */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No network type specified in comediadir " "attribute.", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_SUCCESS); /* as the optional parameters are not there */
}
attr_p->attr.comediadir.conn_info.nettype = SDP_NT_UNSUPPORTED; for (i=0; i < SDP_MAX_NETWORK_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_nettype[i].name,
sdp_nettype[i].strlen) == 0) {
type_found = TRUE;
} if (type_found == TRUE) { if (sdp_p->conf_p->nettype_supported[i] == TRUE) {
attr_p->attr.comediadir.conn_info.nettype = (sdp_nettype_e)i;
}
type_found = FALSE;
}
} if (attr_p->attr.comediadir.conn_info.nettype == SDP_NT_UNSUPPORTED) {
sdp_parse_error(sdp_p, "%s Warning: ConnInfo in Comediadir: network type " "unsupported (%s).", sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++;
}
/* Find the comedia address type. */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No address type specified in comediadir" " attribute.", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++;
}
attr_p->attr.comediadir.conn_info.addrtype = SDP_AT_UNSUPPORTED; for (i=0; i < SDP_MAX_ADDR_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_addrtype[i].name,
sdp_addrtype[i].strlen) == 0) {
type_found = TRUE;
} if (type_found == TRUE) { if (sdp_p->conf_p->addrtype_supported[i] == TRUE) {
attr_p->attr.comediadir.conn_info.addrtype = (sdp_addrtype_e)i;
}
type_found = FALSE;
}
} if (attr_p->attr.comediadir.conn_info.addrtype == SDP_AT_UNSUPPORTED) {
sdp_parse_error(sdp_p, "%s Warning: Conninfo address type unsupported " "(%s).", sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++;
}
/* Find the conninfo address. */
ptr = sdp_getnextstrtok(ptr, attr_p->attr.comediadir.conn_info.conn_addr, sizeof(attr_p->attr.comediadir.conn_info.conn_addr), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No conninfo address specified in " "comediadir attribute.", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++;
}
/* Find the src port info , if any */
attr_p->attr.comediadir.src_port = sdp_getnextnumtok(ptr, &ptr, " \t",
&result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No src port specified in " "comediadir attribute.", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++;
}
/* * sdp_parse_context_crypto_suite * * This routine parses the crypto suite pointed to by str, stores the crypto suite value into the * srtp context suite component of the LocalConnectionOptions pointed to by lco_node_ptr and stores * pointer to the next crypto parameter in tmp_ptr
*/
tinybool sdp_parse_context_crypto_suite(char * str, sdp_attr_t *attr_p, sdp_t *sdp_p) { /* * Three crypto_suites are defined: (Notice no SPACE between "crypto:" and the <crypto-suite> * AES_CM_128_HMAC_SHA1_80 * AES_CM_128_HMAC_SHA1_32 * F8_128_HMAC_SHA1_80
*/
int i;
/* Check crypto suites */ for(i=0; i<SDP_SRTP_MAX_NUM_CRYPTO_SUITES; i++) { if (!cpr_strcasecmp(sdp_srtp_crypto_suite_array[i].crypto_suite_str, str)) {
attr_p->attr.srtp_context.suite = sdp_srtp_crypto_suite_array[i].crypto_suite_val;
attr_p->attr.srtp_context.master_key_size_bytes =
sdp_srtp_crypto_suite_array[i].key_size_bytes;
attr_p->attr.srtp_context.master_salt_size_bytes =
sdp_srtp_crypto_suite_array[i].salt_size_bytes; returnTRUE; /* There is a succesful match so exit */
}
} /* couldn't find a matching crypto suite */
sdp_parse_error(sdp_p, "%s No Matching crypto suite for SRTP Context(%s)-'X-crypto:v1' expected",
sdp_p->debug_str, str);
returnFALSE;
}
sdp_result_e sdp_build_attr_srtpcontext (sdp_t *sdp_p, sdp_attr_t *attr_p,
flex_string *fs)
{ #define MAX_BASE64_ENCODE_SIZE_BYTES 60 int output_len = MAX_BASE64_ENCODE_SIZE_BYTES; int key_size = attr_p->attr.srtp_context.master_key_size_bytes; int salt_size = attr_p->attr.srtp_context.master_salt_size_bytes; unsignedchar base64_encoded_data[MAX_BASE64_ENCODE_SIZE_BYTES]; unsignedchar base64_encoded_input[MAX_BASE64_ENCODE_SIZE_BYTES];
base64_result_t status;
output_len = MAX_BASE64_ENCODE_SIZE_BYTES;
/* Append master and salt keys */
memcpy(base64_encoded_input,
attr_p->attr.srtp_context.master_key,
key_size );
memcpy(base64_encoded_input + key_size,
attr_p->attr.srtp_context.master_salt,
salt_size );
if ((status = base64_encode(base64_encoded_input, key_size + salt_size,
base64_encoded_data, &output_len)) != BASE64_SUCCESS) { if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
SDPLogError(logTag, "%s Error: Failure to Base64 Encoded data (%s) ",
sdp_p->debug_str, BASE64_RESULT_TO_STRING(status));
} return (SDP_INVALID_PARAMETER);
}
/* * sdp_parse_attr_mptime * This function parses the a=mptime sdp line. This parameter consists of * one or more numbers or hyphens ("-"). The first parameter must be a * number. The number of parameters must match the number of formats specified * on the m= line. This function is liberal in that it does not match against * the m= line or require a number for the first parameter.
*/
sdp_result_e sdp_parse_attr_mptime (
sdp_t *sdp_p,
sdp_attr_t *attr_p, constchar *ptr)
{
uint16_t i; /* loop counter for parameters */
sdp_result_e result; /* value returned by this function */
tinybool null_ind; /* true if a parameter is "-" */
/* * Scan the input line up to the maximum number of parameters supported. * Look for numbers or hyphens and store the resulting values. Hyphens * are stored as zeros.
*/ for (i=0; i<SDP_MAX_PAYLOAD_TYPES; i++) {
attr_p->attr.mptime.intervals[i] =
(ushort)sdp_getnextnumtok_or_null(ptr,&ptr," \t",&null_ind,&result); if (result != SDP_SUCCESS) { break;
}
attr_p->attr.mptime.num_intervals++;
}
/* * At least one parameter must be supplied. If not, return an error * and optionally log the failure.
*/ if (attr_p->attr.mptime.num_intervals == 0) {
sdp_parse_error(sdp_p, "%s Warning: No intervals specified for %s attr.",
sdp_p->debug_str, sdp_attr[attr_p->type].name);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* * Here is some debugging code that helps us track what data * is received and parsed.
*/ if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=%s, num intervals %u, intervals: ",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type),
attr_p->attr.mptime.num_intervals); for (i=0; i < attr_p->attr.mptime.num_intervals; i++) {
SDP_PRINT("%u ", attr_p->attr.mptime.intervals[i]);
}
}
return SDP_SUCCESS;
}
/* * sdp_build_attr_mptime * This function builds the a=mptime sdp line. It reads the selected attribute * from the sdp structure. Parameters with a value of zero are replaced by * hyphens.
*/
sdp_result_e sdp_build_attr_mptime (
sdp_t *sdp_p,
sdp_attr_t *attr_p,
flex_string *fs)
{ int i;
/* * Run the list of mptime parameter values and write each one * to the sdp line. Replace zeros with hyphens.
*/ for (i=0; i < attr_p->attr.mptime.num_intervals; i++) { if (i > 0) {
flex_string_append(fs, " ");
}
/* Find the filter mode */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No src filter attribute value specified for " "a=source-filter line", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
} for (i = 0; i < SDP_MAX_FILTER_MODE; i++) { if (cpr_strncasecmp(tmp, sdp_src_filter_mode_val[i].name,
sdp_src_filter_mode_val[i].strlen) == 0) {
attr_p->attr.source_filter.mode = (sdp_src_filter_mode_e)i; break;
}
} if (attr_p->attr.source_filter.mode == SDP_FILTER_MODE_NOT_PRESENT) { /* No point continuing */
sdp_parse_error(sdp_p, "%s Warning: Invalid src filter mode for a=source-filter " "line", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Find the network type */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
} for (i = 0; i < SDP_MAX_NETWORK_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_nettype[i].name,
sdp_nettype[i].strlen) == 0) { if (sdp_p->conf_p->nettype_supported[i] == TRUE) {
attr_p->attr.source_filter.nettype = (sdp_nettype_e)i;
}
}
} if (attr_p->attr.source_filter.nettype == SDP_NT_UNSUPPORTED) {
sdp_parse_error(sdp_p, "%s Warning: Network type unsupported " "(%s) for a=source-filter", sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Find the address type */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
} for (i = 0; i < SDP_MAX_ADDR_TYPES; i++) { if (cpr_strncasecmp(tmp, sdp_addrtype[i].name,
sdp_addrtype[i].strlen) == 0) { if (sdp_p->conf_p->addrtype_supported[i] == TRUE) {
attr_p->attr.source_filter.addrtype = (sdp_addrtype_e)i;
}
}
} if (attr_p->attr.source_filter.addrtype == SDP_AT_UNSUPPORTED) { if (strncmp(tmp, "*", 1) == 0) {
attr_p->attr.source_filter.addrtype = SDP_AT_FQDN;
} else {
sdp_parse_error(sdp_p, "%s Warning: Address type unsupported " "(%s) for a=source-filter", sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
}
/* Find the destination addr */
ptr = sdp_getnextstrtok(ptr, attr_p->attr.source_filter.dest_addr, sizeof(attr_p->attr.source_filter.dest_addr), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s No filter destination address specified for " "a=source-filter", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
/* Find the list of source address to apply the filter */ for (i = 0; i < SDP_MAX_SRC_ADDR_LIST; i++) {
ptr = sdp_getnextstrtok(ptr, attr_p->attr.source_filter.src_list[i], sizeof(attr_p->attr.source_filter.src_list[i]), " \t", &result); if (result != SDP_SUCCESS) { break;
}
attr_p->attr.source_filter.num_src_addr++;
} if (attr_p->attr.source_filter.num_src_addr == 0) {
sdp_parse_error(sdp_p, "%s Warning: No source list provided " "for a=source-filter", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
for (i = 0; i < attr_p->attr.source_filter.num_src_addr; i++) {
flex_string_append(fs, " ");
flex_string_append(fs, attr_p->attr.source_filter.src_list[i]);
}
/* * store_sdescriptions_mki_or_lifetime * * Verifies the syntax of the MKI or lifetime parameter and stores * it in the sdescriptions attribute struct. * * Inputs: * buf - pointer to MKI or lifetime string assumes string is null * terminated. * attr_p - pointer to attribute struct * * Outputs: * Return TRUE all is good otherwise FALSE for error.
*/
/* MKI has a colon */ if (strstr(buf, ":")) {
result = verify_sdescriptions_mki(buf, mkiValue, &mkiLen); if (result) {
attr_p->attr.srtp_context.mki_size_bytes = mkiLen;
sstrncpy((char*)attr_p->attr.srtp_context.mki, mkiValue,
SDP_SRTP_MAX_MKI_SIZE_BYTES);
}
} else {
result = verify_sdescriptions_lifetime(buf); if (result) {
sstrncpy((char*)attr_p->attr.srtp_context.master_key_lifetime, buf,
SDP_SRTP_MAX_LIFETIME_BYTES);
}
}
return result;
}
/* * sdp_parse_sdescriptions_key_param * * This routine parses the srtp key-params pointed to by str. * * key-params = <key-method> ":" <key-info> * key-method = "inline" / key-method-ext [note V9 only supports 'inline'] * key-info = srtp-key-info * srtp-key-info = key-salt ["|" lifetime] ["|" mki] * key-salt = 1*(base64) ; binary key and salt values * ; concatenated together, and then * ; base64 encoded [section 6.8 of * ; RFC2046] * * lifetime = ["2^"] 1*(DIGIT) * mki = mki-value ":" mki-length * mki-value = 1*DIGIT * mki-length = 1*3DIGIT ; range 1..128. * * Inputs: str - pointer to beginning of key-params and assumes * null terminated string.
*/
ptr = str; if (cpr_strncasecmp(ptr, "inline:", 7) != 0) {
sdp_parse_error(sdp_p, "%s Could not find keyword inline", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; returnFALSE;
}
/* advance pass the inline key word */
ptr = ptr + 7;
ptr = sdp_getnextstrtok(ptr, buf, sizeof(buf), "|", &result); while (result == SDP_SUCCESS) { /* the fist time this loop executes, the key is gotten */ if (keyFound == FALSE) {
keyFound = TRUE;
len = SDP_MAX_STRING_LEN; /* The key is base64 encoded composed of the master key concatenated with the * master salt.
*/
status = base64_decode((unsignedchar *)buf, strlen(buf),
(unsignedchar *)base64decodeData, &len);
/* Used only for MGCP */
SDP_SRTP_CONTEXT_SET_MASTER_KEY
(attr_p->attr.srtp_context.selection_flags);
SDP_SRTP_CONTEXT_SET_MASTER_SALT
(attr_p->attr.srtp_context.selection_flags);
/* if we haven't reached the end of line, get the next token */
ptr = sdp_getnextstrtok(ptr, buf, sizeof(buf), "|", &result);
}
/* if we didn't find the key, error out */ if (keyFound == FALSE) {
sdp_parse_error(sdp_p, "%s Could not find sdescriptions key", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; returnFALSE;
}
returnTRUE;
}
/* * sdp_build_attr_sdescriptions * * Builds a=crypto line for attribute type SDP_ATTR_SDESCRIPTIONS. * * a=crypto:tag 1*WSP crypto-suite 1*WSP key-params * * Where key-params = inline: <key|salt> ["|"lifetime] ["|" MKI:length] * The key and salt is base64 encoded and lifetime and MKI/length are optional.
*/
/* * sdp_parse_attr_srtp * * Parses Session Description for Protocol Security Descriptions * version 2 or version 9. Grammar is of the form: * * a=crypto:<tag> <crypto-suite> <key-params> [<session-params>] * * Note session-params is not supported and will not be parsed. * Version 2 does not contain a tag. * * Inputs: * sdp_p - pointer to sdp handle * attr_p - pointer to attribute structure * ptr - pointer to string to be parsed * vtype - version type
*/
char tmp[SDP_MAX_STRING_LEN];
sdp_result_e result = SDP_FAILURE; int k = 0;
/* initialize only the optional parameters */
attr_p->attr.srtp_context.master_key_lifetime[0] = 0;
attr_p->attr.srtp_context.mki[0] = 0;
/* used only for MGCP */
SDP_SRTP_CONTEXT_SET_ENCRYPT_AUTHENTICATE
(attr_p->attr.srtp_context.selection_flags);
/* get the tag only if we are version 9 */ if (vtype == SDP_ATTR_SDESCRIPTIONS) {
attr_p->attr.srtp_context.tag =
sdp_getnextnumtok(ptr, &ptr, " \t", &result);
if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Could not find sdescriptions tag",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
}
/* get the crypto suite */
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Could not find sdescriptions crypto suite", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
ptr = sdp_getnextstrtok(ptr, tmp, sizeof(tmp), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Could not find sdescriptions key params", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
if (!sdp_parse_sdescriptions_key_param(tmp, attr_p, sdp_p)) {
sdp_parse_error(sdp_p, "%s Failed to parse key-params", sdp_p->debug_str); return (SDP_INVALID_PARAMETER);
}
/* if there are session parameters, scan the session parameters * into tmp until we reach end of line. Currently the sdp parser * does not parse session parameters but if they are present, * we store them for the application.
*/ /*sa_ignore NO_NULL_CHK *{ptr is valid since the pointer was checked earlier and the * function would have exited if NULL.}
*/ while (*ptr && *ptr != '\n' && *ptr != '\r' && k < SDP_MAX_STRING_LEN) {
tmp[k++] = *ptr++;
}
/* Tack on any information that cannot otherwise be represented by
* the sdp_fmtp_fb_t structure. */ if (attr_p->attr.rtcp_fb.extra[0]) {
flex_string_sprintf(fs, " %s", attr_p->attr.rtcp_fb.extra);
}
/* Line ending */
flex_string_sprintf(fs, "\r\n");
return SDP_SUCCESS;
}
sdp_result_e sdp_parse_attr_rtcp_fb (sdp_t *sdp_p,
sdp_attr_t *attr_p, constchar *ptr)
{
sdp_result_e result = SDP_SUCCESS;
sdp_fmtp_fb_t *rtcp_fb_p = &(attr_p->attr.rtcp_fb); int i;
/* Set up attribute fields */
rtcp_fb_p->payload_num = 0;
rtcp_fb_p->feedback_type = SDP_RTCP_FB_UNKNOWN;
rtcp_fb_p->extra[0] = '\0';
/* Skip WS (just in case) */ while (*ptr == ' ' || *ptr == '\t') {
ptr++;
}
/* Look for the special "*" payload type */ if (*ptr == '*') {
rtcp_fb_p->payload_num = SDP_ALL_PAYLOADS;
ptr++;
} else { /* If the pt is not '*', parse it out as an integer */
rtcp_fb_p->payload_num = (uint16_t)sdp_getnextnumtok(ptr, &ptr, " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: could not parse payload type for rtcp-fb attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++;
return SDP_INVALID_PARAMETER;
}
}
/* Read feedback type */
i = find_token_enum("rtcp-fb attribute", sdp_p, &ptr, sdp_rtcp_fb_type_val,
SDP_MAX_RTCP_FB, SDP_RTCP_FB_UNKNOWN); if (i < 0) {
sdp_parse_error(sdp_p, "%s Warning: could not parse feedback type for rtcp-fb attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
}
rtcp_fb_p->feedback_type = (sdp_rtcp_fb_type_e) i;
switch(rtcp_fb_p->feedback_type) { case SDP_RTCP_FB_ACK:
i = find_token_enum("rtcp-fb ack type", sdp_p, &ptr,
sdp_rtcp_fb_ack_type_val,
SDP_MAX_RTCP_FB_ACK, SDP_RTCP_FB_ACK_UNKNOWN); if (i < 0) {
sdp_parse_error(sdp_p, "%s Warning: could not parse ack type for rtcp-fb attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
}
rtcp_fb_p->param.ack = (sdp_rtcp_fb_ack_type_e) i; break;
case SDP_RTCP_FB_CCM:
i = find_token_enum("rtcp-fb ccm type", sdp_p, &ptr,
sdp_rtcp_fb_ccm_type_val,
SDP_MAX_RTCP_FB_CCM, SDP_RTCP_FB_CCM_UNKNOWN); if (i < 0) {
sdp_parse_error(sdp_p, "%s Warning: could not parse ccm type for rtcp-fb attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
}
rtcp_fb_p->param.ccm = (sdp_rtcp_fb_ccm_type_e) i;
/* TODO -- We don't currently parse tmmbr parameters or vbcm submessage types. If we decide to support these modes of operation, we probably want to add parsing code for them. For the time being, they'll just end up parsed into "extra" Bug 1097169.
*/ break;
case SDP_RTCP_FB_NACK: /* Skip any remaining WS -- see http://code.google.com/p/webrtc/issues/detail?id=1922 */ while (*ptr == ' ' || *ptr == '\t') {
ptr++;
} /* Check for empty string */ if (*ptr == '\r' || *ptr == '\n') {
rtcp_fb_p->param.nack = SDP_RTCP_FB_NACK_BASIC; break;
}
i = find_token_enum("rtcp-fb nack type", sdp_p, &ptr,
sdp_rtcp_fb_nack_type_val,
SDP_MAX_RTCP_FB_NACK, SDP_RTCP_FB_NACK_UNKNOWN); if (i < 0) {
sdp_parse_error(sdp_p, "%s Warning: could not parse nack type for rtcp-fb attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
}
rtcp_fb_p->param.nack = (sdp_rtcp_fb_nack_type_e) i; break;
case SDP_RTCP_FB_TRR_INT:
rtcp_fb_p->param.trr_int = sdp_getnextnumtok(ptr, &ptr, " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: could not parse trr-int value for rtcp-fb " "attribute", sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
} break;
case SDP_RTCP_FB_REMB: /* No additional tokens to parse after goog-remb */ break;
case SDP_RTCP_FB_TRANSPORT_CC: /* No additional tokens to parse after transport-cc */ break;
case SDP_RTCP_FB_UNKNOWN: /* Handled by "extra", below */ break;
default: /* This is an internal error, not a parsing error */
SDPLogError(logTag, "%s Error: Invalid rtcp-fb enum (%d)",
sdp_p->debug_str, attr_p->attr.rtcp_fb.feedback_type); return SDP_FAILURE;
}
/* Skip any remaining WS */ while (*ptr == ' ' || *ptr == '\t') {
ptr++;
}
/* Just store the rest of the line in "extra" -- this will return
a failure result if there is no more text, but that's fine. */
ptr = sdp_getnextstrtok(ptr, rtcp_fb_p->extra, sizeof(rtcp_fb_p->extra), "\r\n", &result);
return SDP_SUCCESS;
}
sdp_result_e sdp_build_attr_setup(sdp_t *sdp_p,
sdp_attr_t *attr_p,
flex_string *fs)
{ switch (attr_p->attr.setup) { case SDP_SETUP_ACTIVE: case SDP_SETUP_PASSIVE: case SDP_SETUP_ACTPASS: case SDP_SETUP_HOLDCONN:
flex_string_sprintf(fs, "a=%s:%s\r\n",
sdp_attr[attr_p->type].name,
sdp_setup_type_val[attr_p->attr.setup].name); break; default:
SDPLogError(logTag, "%s Error: Invalid setup enum (%d)",
sdp_p->debug_str, attr_p->attr.setup); return SDP_FAILURE;
}
return SDP_SUCCESS;
}
sdp_result_e sdp_parse_attr_setup(sdp_t *sdp_p,
sdp_attr_t *attr_p, constchar *ptr)
{ int i = find_token_enum("setup attribute", sdp_p, &ptr,
sdp_setup_type_val,
SDP_MAX_SETUP, SDP_SETUP_UNKNOWN);
if (i < 0) {
sdp_parse_error(sdp_p, "%s Warning: could not parse setup attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
}
attr_p->attr.setup = (sdp_setup_type_e) i;
switch (attr_p->attr.setup) { case SDP_SETUP_ACTIVE: case SDP_SETUP_PASSIVE: case SDP_SETUP_ACTPASS: case SDP_SETUP_HOLDCONN: /* All these values are OK */ break; case SDP_SETUP_UNKNOWN:
sdp_parse_error(sdp_p, "%s Warning: Unknown setup attribute",
sdp_p->debug_str); return SDP_INVALID_PARAMETER; default: /* This is an internal error, not a parsing error */
SDPLogError(logTag, "%s Error: Invalid setup enum (%d)",
sdp_p->debug_str, attr_p->attr.setup); return SDP_FAILURE;
}
sdp_result_e sdp_parse_attr_connection(sdp_t *sdp_p,
sdp_attr_t *attr_p, constchar *ptr)
{ int i = find_token_enum("connection attribute", sdp_p, &ptr,
sdp_connection_type_val,
SDP_MAX_CONNECTION, SDP_CONNECTION_UNKNOWN);
if (i < 0) {
sdp_parse_error(sdp_p, "%s Warning: could not parse connection attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
}
switch (attr_p->attr.connection) { case SDP_CONNECTION_NEW: case SDP_CONNECTION_EXISTING: /* All these values are OK */ break; case SDP_CONNECTION_UNKNOWN:
sdp_parse_error(sdp_p, "%s Warning: Unknown connection attribute",
sdp_p->debug_str); return SDP_INVALID_PARAMETER; default: /* This is an internal error, not a parsing error */
SDPLogError(logTag, "%s Error: Invalid connection enum (%d)",
sdp_p->debug_str, attr_p->attr.connection); return SDP_FAILURE;
} return SDP_SUCCESS;
}
ptr = sdp_getnextstrtok(ptr, attr_p->attr.extmap.uri, sizeof(attr_p->attr.extmap.uri), " \t", &result); if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: No uri specified in %s attribute.",
sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
while (*ptr == ' ' || *ptr == '\t') {
++ptr;
}
/* Grab everything that follows, even if it contains whitespace */
ptr = sdp_getnextstrtok(ptr, attr_p->attr.extmap.extension_attributes, sizeof(attr_p->attr.extmap.extension_attributes), "\r\n", &result);
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=%s, id %u, direction %s, " "uri %s, extension %s", sdp_p->debug_str,
sdp_get_attr_name(attr_p->type),
attr_p->attr.extmap.id,
SDP_DIRECTION_PRINT(attr_p->attr.extmap.media_direction),
attr_p->attr.extmap.uri,
attr_p->attr.extmap.extension_attributes);
}
if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: Bad msid-semantic attribute; " "missing semantic",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
}
for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { /* msid-id can be up to 64 characters long, plus null terminator */ char temp[65];
ptr = sdp_getnextstrtok(ptr, temp, sizeof(temp), " \t", &result);
if ((result != SDP_SUCCESS) && (result != SDP_EMPTY_TOKEN)) {
sdp_parse_error(sdp_p, "%s Warning: Bad msid-semantic attribute",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
}
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=msid-semantic, %s", sdp_p->debug_str,
attr_p->attr.msid_semantic.semantic); for (i = 0; i < SDP_MAX_MEDIA_STREAMS; ++i) { if (!attr_p->attr.msid_semantic.msids[i]) { break;
}
if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p, "%s Warning: Bad ssrc attribute, cannot parse ssrc",
sdp_p->debug_str);
sdp_p->conf_p->num_invalid_param++; return SDP_INVALID_PARAMETER;
}
/* Skip any remaining WS */ while (*ptr == ' ' || *ptr == '\t') {
ptr++;
}
/* Just store the rest of the line in "attribute" -- this will return
a failure result if there is no more text, but that's fine. */
ptr = sdp_getnextstrtok(ptr,
attr_p->attr.ssrc.attribute, sizeof(attr_p->attr.ssrc.attribute), "\r\n",
&result);
if (attr_p->attr.ssrc_group.num_ssrcs == 0) {
sdp_parse_error(sdp_p, "%s Warning: Ssrc group must contain at least one ssrc (%s).",
sdp_p->debug_str, tmp);
sdp_p->conf_p->num_invalid_param++; return (SDP_INVALID_PARAMETER);
}
if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
SDP_PRINT("%s Parsed a=ssrc-group, semantic %s", sdp_p->debug_str,
sdp_get_ssrc_group_attr_name(attr_p->attr.ssrc_group.semantic)); for (i = 0; i < attr_p->attr.ssrc_group.num_ssrcs; ++i) {
SDP_PRINT("%s ... ssrc %u", sdp_p->debug_str,
attr_p->attr.ssrc_group.ssrcs[i]);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.