diff --git a/src/core/parser/sdp/sdp.c b/src/core/parser/sdp/sdp.c index c3c94c37daa..dc584e1c8b0 100644 --- a/src/core/parser/sdp/sdp.c +++ b/src/core/parser/sdp/sdp.c @@ -38,9 +38,6 @@ #define SDP_USE_PKG_MEM 0 #define SDP_USE_SHM_MEM 1 -#define HOLD_IP_STR "0.0.0.0" -#define HOLD_IP_LEN 7 - /** * Creates and initialize a new sdp_info structure */ @@ -371,7 +368,8 @@ static int parse_sdp_session(str *sdp_body, int session_num, str *cnt_disp, sdp_ sdp_session_cell_t *session; sdp_stream_cell_t *stream; sdp_payload_attr_t *payload_attr; - int parse_payload_attr; + sdp_ice_opt_t *ice_opt; /* media lvl ice options */ + int parse_payload_attr, ice_trickle = 0; str fmtp_string; str remote_candidates = {"a:remote-candidates:", 20}; @@ -582,10 +580,12 @@ static int parse_sdp_session(str *sdp_body, int session_num, str *cnt_disp, sdp_ payload_attr = (sdp_payload_attr_t*)get_sdp_payload4payload(stream, &rtp_payload); set_sdp_payload_fmtp(payload_attr, &fmtp_string); } else if (parse_payload_attr && extract_candidate(&tmpstr1, stream) == 0) { - a1p += 2; + a1p += 2; + } else if (parse_payload_attr && extract_ice_option(&tmpstr1, stream) == 0) { + a1p += 2; } else if (parse_payload_attr && extract_field(&tmpstr1, &stream->remote_candidates, remote_candidates) == 0) { - a1p += 2; + a1p += 2; } else if (extract_accept_types(&tmpstr1, &stream->accept_types) == 0) { a1p = stream->accept_types.s + stream->accept_types.len; } else if (extract_accept_wrapped_types(&tmpstr1, &stream->accept_wrapped_types) == 0) { @@ -606,14 +606,49 @@ static int parse_sdp_session(str *sdp_body, int session_num, str *cnt_disp, sdp_ /* Let's detect if the media is on hold by checking * the good old "0.0.0.0" connection address */ if (!stream->is_on_hold) { + /* But, exclude the cases with ICE trickle re-negotiation (RFC8840), + * which are not the on hold (RFC2543) case actually */ + ice_opt = stream->ice_opt; + while(ice_opt) + { + if (ice_opt->option.len == ICE_OPT_TRICKLE_LEN && + strncmp(ice_opt->option.s, ICE_OPT_TRICKLE_STR, ICE_OPT_TRICKLE_LEN)==0) { + ice_trickle = 1; + ice_opt = NULL; /* break */ + } else { + ice_opt = ice_opt->next; + } + } + + /* SDP stream level */ if (stream->ip_addr.s && stream->ip_addr.len) { - if (stream->ip_addr.len == HOLD_IP_LEN && - strncmp(stream->ip_addr.s, HOLD_IP_STR, HOLD_IP_LEN)==0) - stream->is_on_hold = RFC2543_HOLD; + if (stream->pf == AF_INET && + stream->ip_addr.len == HOLD_IP_LEN && + strncmp(stream->ip_addr.s, HOLD_IP_STR, HOLD_IP_LEN)==0) { + + /* make sure it's not ICE trickle re-negotiation */ + if (ice_trickle && + stream->port.len==HOLD_PORT_ICE_TRICKLE_LEN && /* port=9 */ + strncmp(stream->port.s,HOLD_PORT_ICE_TRICKLE_STR,HOLD_PORT_ICE_TRICKLE_LEN)==0) + LM_DBG("Not a zeroed on-hold (RFC2543), because is ICE re-negotiaion (RFC8840)\n"); + else + stream->is_on_hold = RFC2543_HOLD; + } + + /* SDP session level */ } else if (session->ip_addr.s && session->ip_addr.len) { - if (session->ip_addr.len == HOLD_IP_LEN && - strncmp(session->ip_addr.s, HOLD_IP_STR, HOLD_IP_LEN)==0) - stream->is_on_hold = RFC2543_HOLD; + if (session->pf == AF_INET && + session->ip_addr.len == HOLD_IP_LEN && + strncmp(session->ip_addr.s, HOLD_IP_STR, HOLD_IP_LEN)==0) { + + /* make sure it's not ICE trickle re-negotiation */ + if (ice_trickle && + stream->port.len==HOLD_PORT_ICE_TRICKLE_LEN && /* port=9 */ + strncmp(stream->port.s,HOLD_PORT_ICE_TRICKLE_STR,HOLD_PORT_ICE_TRICKLE_LEN)==0) + LM_DBG("Not a zeroed on-hold (RFC2543), because is ICE re-negotiaion (RFC8840)\n"); + else + stream->is_on_hold = RFC2543_HOLD; + } } } ++stream_num; @@ -818,7 +853,8 @@ void free_sdp(sdp_info_t** _sdp) sdp_session_cell_t *session, *l_session; sdp_stream_cell_t *stream, *l_stream; sdp_payload_attr_t *payload, *l_payload; - sdp_ice_attr_t *tmp; + sdp_ice_attr_t *ice_attr, *l_ice_attr; + sdp_ice_opt_t *ice_opt, *l_ice_opt; LM_DBG("_sdp = %p\n", _sdp); if (sdp == NULL) return; @@ -841,10 +877,17 @@ void free_sdp(sdp_info_t** _sdp) if (l_stream->p_payload_attr) { pkg_free(l_stream->p_payload_attr); } - while (l_stream->ice_attr) { - tmp = l_stream->ice_attr->next; - pkg_free(l_stream->ice_attr); - l_stream->ice_attr = tmp; + ice_attr = l_stream->ice_attr; + while (ice_attr) { + l_ice_attr = ice_attr; + ice_attr = ice_attr->next; + pkg_free(l_ice_attr); + } + ice_opt = l_stream->ice_opt; + while (ice_opt) { + l_ice_opt = ice_opt; + ice_opt = ice_opt->next; + pkg_free(l_ice_opt); } pkg_free(l_stream); } @@ -858,7 +901,8 @@ void free_sdp(sdp_info_t** _sdp) void print_sdp_stream(sdp_stream_cell_t *stream, int log_level) { sdp_payload_attr_t *payload; - sdp_ice_attr_t *ice_attr; + sdp_ice_attr_t *ice_attr; + sdp_ice_opt_t *ice_opt; LOG(log_level , "....stream[%d]:%p=>%p {%p} '%.*s' '%.*s:%.*s:%.*s' '%.*s' [%d] '%.*s' '%.*s:%.*s' (%d)=>%p (%d)=>%p '%.*s' '%.*s' '%.*s' '%.*s' '%.*s' '%.*s' '%.*s'\n", stream->stream_num, stream, stream->next, @@ -876,8 +920,9 @@ void print_sdp_stream(sdp_stream_cell_t *stream, int log_level) stream->path.len, stream->path.s, stream->max_size.len, stream->max_size.s, stream->accept_types.len, stream->accept_types.s, - stream->accept_wrapped_types.len, stream->accept_wrapped_types.s, - stream->remote_candidates.len, stream->remote_candidates.s); + stream->accept_wrapped_types.len, stream->accept_wrapped_types.s, + stream->remote_candidates.len, stream->remote_candidates.s); + payload = stream->payload_attr; while (payload) { LOG(log_level, "......payload[%d]:%p=>%p p_payload_attr[%d]:%p '%.*s' '%.*s' '%.*s' '%.*s' '%.*s'\n", @@ -890,12 +935,19 @@ void print_sdp_stream(sdp_stream_cell_t *stream, int log_level) payload->fmtp_string.len, payload->fmtp_string.s); payload=payload->next; } + ice_attr = stream->ice_attr; while (ice_attr) { - LOG(log_level, "......'%.*s' %u\n", - ice_attr->foundation.len, ice_attr->foundation.s, - ice_attr->component_id); - ice_attr = ice_attr->next; + LOG(log_level, "......ice candidate foundation '%.*s' component id '%u'\n", + ice_attr->foundation.len, ice_attr->foundation.s, + ice_attr->component_id); + ice_attr = ice_attr->next; + } + + ice_opt = stream->ice_opt; + while (ice_opt) { + LOG(log_level, "......ice option '%.*s'\n", ice_opt->option.len, ice_opt->option.s); + ice_opt = ice_opt->next; } } @@ -948,6 +1000,8 @@ void free_cloned_sdp_stream(sdp_stream_cell_t *_stream) { sdp_stream_cell_t *stream, *l_stream; sdp_payload_attr_t *payload, *l_payload; + sdp_ice_attr_t *ice_attr, *l_ice_attr; + sdp_ice_opt_t *ice_opt, *l_ice_opt; stream = _stream; while (stream) { @@ -962,6 +1016,18 @@ void free_cloned_sdp_stream(sdp_stream_cell_t *_stream) if (l_stream->p_payload_attr) { shm_free(l_stream->p_payload_attr); } + ice_attr = l_stream->ice_attr; + while (ice_attr) { + l_ice_attr = ice_attr; + ice_attr = ice_attr->next; + shm_free(l_ice_attr); + } + ice_opt = l_stream->ice_opt; + while (ice_opt) { + l_ice_opt = ice_opt; + ice_opt = ice_opt->next; + shm_free(l_ice_opt); + } shm_free(l_stream); } } @@ -1053,10 +1119,114 @@ sdp_payload_attr_t * clone_sdp_payload_attr(sdp_payload_attr_t *attr) return clone_attr; } +sdp_ice_attr_t * clone_sdp_ice_attr(sdp_ice_attr_t *ice_attr) +{ + sdp_ice_attr_t * clone_ice_attr; + int len; + char *p; + + if (ice_attr == NULL) return NULL; + + len = sizeof(sdp_ice_attr_t) + + ice_attr->foundation.len + + ice_attr->transport.len + + ice_attr->connection_addr.len + + ice_attr->port.len + + ice_attr->candidate_type.len; + + clone_ice_attr = (sdp_ice_attr_t*)shm_malloc(len); + if (clone_ice_attr == NULL) { + SHM_MEM_ERROR; + return NULL; + } + memset(clone_ice_attr, 0, len); + + p = (char*)(clone_ice_attr); /* beginning of the struct */ + + /* foundation */ + if (ice_attr->foundation.len) { + clone_ice_attr->foundation.s = p; + clone_ice_attr->foundation.len = ice_attr->foundation.len; + memcpy( p, ice_attr->foundation.s, ice_attr->foundation.len); + p += ice_attr->foundation.len; + } + + /* skip component_id and just copy it int to int directly */ + p++; + clone_ice_attr->component_id = ice_attr->component_id; + + /* transport */ + if (ice_attr->transport.len) { + clone_ice_attr->transport.s = p; + clone_ice_attr->transport.len = ice_attr->transport.len; + memcpy( p, ice_attr->transport.s, ice_attr->transport.len); + p += ice_attr->transport.len; + } + + /* connection_addr */ + if (ice_attr->connection_addr.len) { + clone_ice_attr->connection_addr.s = p; + clone_ice_attr->connection_addr.len = ice_attr->connection_addr.len; + memcpy( p, ice_attr->connection_addr.s, ice_attr->connection_addr.len); + p += ice_attr->connection_addr.len; + } + + /* port */ + if (ice_attr->port.len) { + clone_ice_attr->port.s = p; + clone_ice_attr->port.len = ice_attr->port.len; + memcpy( p, ice_attr->port.s, ice_attr->port.len); + p += ice_attr->port.len; + } + + /* candidate_type */ + if (ice_attr->candidate_type.len) { + clone_ice_attr->candidate_type.s = p; + clone_ice_attr->candidate_type.len = ice_attr->candidate_type.len; + memcpy( p, ice_attr->candidate_type.s, ice_attr->candidate_type.len); + p += ice_attr->candidate_type.len; + } + + /* candidateType */ + clone_ice_attr->candidateType = ice_attr->candidateType; + + return clone_ice_attr; +} + +sdp_ice_opt_t * clone_sdp_opt_attr(sdp_ice_opt_t *ice_opt) +{ + sdp_ice_opt_t * clone_ice_opt; + int len; + char *p; + + if (ice_opt == NULL) return NULL; + len = sizeof(sdp_ice_opt_t) + ice_opt->option.len; + + clone_ice_opt = (sdp_ice_opt_t*)shm_malloc(len); + if (clone_ice_opt == NULL) { + SHM_MEM_ERROR; + return NULL; + } + memset(clone_ice_opt, 0, len); + p = (char*)(clone_ice_opt); /* beginning of the struct */ + + /* ice option */ + if (ice_opt->option.len) { + clone_ice_opt->option.s = p; + clone_ice_opt->option.len = ice_opt->option.len; + memcpy( p, ice_opt->option.s, ice_opt->option.len); + } + + return clone_ice_opt; +} + sdp_stream_cell_t * clone_sdp_stream_cell(sdp_stream_cell_t *stream) { sdp_stream_cell_t *clone_stream; sdp_payload_attr_t *clone_payload_attr, *payload_attr; + sdp_ice_attr_t *clone_ice_attr, *tmp_ice_attr, *prev_ice_attr; + sdp_ice_opt_t *clone_ice_opt, *tmp_ice_opt, *prev_ice_opt; + int len, i; char *p; @@ -1076,7 +1246,10 @@ sdp_stream_cell_t * clone_sdp_stream_cell(sdp_stream_cell_t *stream) stream->payloads.len + stream->bw_type.len + stream->bw_width.len + - stream->rtcp_port.len; + stream->rtcp_port.len + + stream->raw_stream.len + + stream->remote_candidates.len; + clone_stream = (sdp_stream_cell_t*)shm_malloc(len); if (clone_stream == NULL) { SHM_MEM_ERROR; @@ -1095,15 +1268,76 @@ sdp_stream_cell_t * clone_sdp_stream_cell(sdp_stream_cell_t *stream) clone_payload_attr->next = payload_attr; payload_attr = clone_payload_attr; } - clone_stream->payload_attr = payload_attr; + clone_stream->payload_attr = payload_attr; clone_stream->payloads_num = stream->payloads_num; + if (clone_stream->payloads_num) { if (NULL == init_p_payload_attr(clone_stream, SDP_USE_SHM_MEM)) { goto error; } } + /* clone ICE candidate attributes */ + if (stream->ice_attrs_num) { + tmp_ice_attr = stream->ice_attr; + clone_ice_attr = clone_sdp_ice_attr(tmp_ice_attr); + + if (clone_ice_attr == NULL) { + LM_ERR("unable to clone ice attributes for component[%d]\n", + tmp_ice_attr->component_id); + goto error; + } + clone_stream->ice_attr = clone_ice_attr; + prev_ice_attr = clone_ice_attr; + tmp_ice_attr = stream->ice_attr->next; + + clone_ice_attr->next = NULL; + + for (i=1; iice_attrs_num; i++) { + clone_ice_attr = clone_sdp_ice_attr(tmp_ice_attr); + + if (clone_ice_attr == NULL) { + LM_ERR("unable to clone ice attributes for component[%d]\n", + tmp_ice_attr->component_id); + goto error; + } + prev_ice_attr->next = clone_ice_attr; + prev_ice_attr = clone_ice_attr; + tmp_ice_attr = stream->ice_attr->next; + } + } + clone_stream->ice_attrs_num = stream->ice_attrs_num; + + /* clone media level ICE options */ + if (stream->ice_opt_num) { + tmp_ice_opt = stream->ice_opt; + clone_ice_opt = clone_sdp_opt_attr(tmp_ice_opt); + + if (clone_ice_opt == NULL) { + LM_ERR("unable to clone ice option for option[%d]\n", i); + goto error; + } + clone_stream->ice_opt = clone_ice_opt; + prev_ice_opt = clone_ice_opt; + tmp_ice_opt = stream->ice_opt->next; + + clone_ice_opt->next = NULL; + + for (i=1; iice_opt_num; i++) { + clone_ice_opt = clone_sdp_opt_attr(tmp_ice_opt); + + if (clone_ice_opt == NULL) { + LM_ERR("unable to clone ice option for option[%d]\n", i); + goto error; + } + prev_ice_opt->next = clone_ice_opt; + prev_ice_opt = clone_ice_opt; + tmp_ice_opt = stream->ice_opt->next; + } + } + clone_stream->ice_opt_num = stream->ice_opt_num; + clone_stream->stream_num = stream->stream_num; clone_stream->pf = stream->pf; @@ -1206,7 +1440,9 @@ sdp_session_cell_t * clone_sdp_session_cell(sdp_session_cell_t *session) session->ip_addr.len + session->o_ip_addr.len + session->bw_type.len + - session->bw_width.len; + session->bw_width.len + + session->sendrecv_mode.len; + clone_session = (sdp_session_cell_t*)shm_malloc(len); if (clone_session == NULL) { SHM_MEM_ERROR; diff --git a/src/core/parser/sdp/sdp.h b/src/core/parser/sdp/sdp.h index d8c1b15b66b..417cb2b4c01 100644 --- a/src/core/parser/sdp/sdp.h +++ b/src/core/parser/sdp/sdp.h @@ -36,6 +36,16 @@ #define RFC2543_HOLD 1 #define RFC3264_HOLD 2 +#define HOLD_IP_STR "0.0.0.0" +#define HOLD_IP_LEN 7 + +#define HOLD_PORT_ICE_TRICKLE_STR "9" +#define HOLD_PORT_ICE_TRICKLE_LEN 1 + +#define ICE_OPTIONS "a=ice-options:" +#define ICE_OPT_TRICKLE_STR "trickle" +#define ICE_OPT_TRICKLE_LEN 7 + typedef struct sdp_payload_attr { struct sdp_payload_attr *next; int payload_num; /**< payload index inside stream */ @@ -46,15 +56,20 @@ typedef struct sdp_payload_attr { str fmtp_string; } sdp_payload_attr_t; +typedef struct sdp_ice_opt { + struct sdp_ice_opt *next; + str option; /* for e.g. 'trickle', 'rtp+ecn' */ +} sdp_ice_opt_t; + typedef struct sdp_ice_attr { - struct sdp_ice_attr *next; - str foundation; - unsigned int component_id; - str transport; - str connection_addr; - str port; - str candidate_type; - int candidateType; /* ICE_HOST/ICE_SRFLX/ICE_PRFLX/ICE_RELAY/ICE_UNKNOWN */ + struct sdp_ice_attr *next; + str foundation; + unsigned int component_id; + str transport; + str connection_addr; + str port; + str candidate_type; + int candidateType; /* ICE_HOST/ICE_SRFLX/ICE_PRFLX/ICE_RELAY/ICE_UNKNOWN */ } sdp_ice_attr_t; typedef struct sdp_stream_cell { @@ -88,10 +103,11 @@ typedef struct sdp_stream_cell { str raw_stream; /**< fast access to raw stream string */ struct sdp_payload_attr **p_payload_attr; /**< fast access pointers to payloads */ struct sdp_payload_attr *payload_attr; - int ice_attrs_num; /**< number of ICE attrs inside a stream */ - /* add fast access pointers to ice attributes if you need them */ - sdp_ice_attr_t *ice_attr; - str remote_candidates; /**< ICE a:remote-candidates */ + int ice_attrs_num; /* number of candidate ICE attrs inside a stream */ + sdp_ice_attr_t *ice_attr; /* add fast access pointers to ice candidate attributes */ + int ice_opt_num; /* number of media level ICE options inside the stream */ + sdp_ice_opt_t *ice_opt; /* add fast access pointers to media level ICE options */ + str remote_candidates; /* ICE a:remote-candidates */ } sdp_stream_cell_t; typedef struct sdp_session_cell { diff --git a/src/core/parser/sdp/sdp_helpr_funcs.c b/src/core/parser/sdp/sdp_helpr_funcs.c index 9b7a442e790..9fae1f7448f 100644 --- a/src/core/parser/sdp/sdp_helpr_funcs.c +++ b/src/core/parser/sdp/sdp_helpr_funcs.c @@ -279,6 +279,26 @@ static inline sdp_ice_attr_t *add_sdp_ice(sdp_stream_cell_t* _stream) return ice_attr; } +static inline sdp_ice_opt_t *add_sdp_ice_opt(sdp_stream_cell_t* _stream) +{ + sdp_ice_opt_t *ice_opt; + int len; + + len = sizeof(sdp_ice_opt_t); + ice_opt = (sdp_ice_opt_t *)pkg_malloc(len); + if (ice_opt == NULL) { + PKG_MEM_ERROR; + return NULL; + } + memset( ice_opt, 0, len); + + /* Insert the new ice option */ + ice_opt->next = _stream->ice_opt; + _stream->ice_opt = ice_opt; + _stream->ice_opt_num++; + + return ice_opt; +} int extract_candidate(str *body, sdp_stream_cell_t *stream) { @@ -343,6 +363,46 @@ int extract_field(str *body, str *value, str field) return 0; } +int extract_ice_option(str *body, sdp_stream_cell_t *stream) +{ + sdp_ice_opt_t *ice_opt; + + char * ptr_src; + int max_options = 10; /* protection - max options can be listed in one line */ + int length = 0; /* each option length */ + + /* a=ice-options: */ + if ((body->len < 14) || (strncasecmp(body->s, ICE_OPTIONS, 14) != 0)) + return -1; + + ptr_src = body->s + 14; + if (ptr_src == 32) ptr_src++; /* if starts with a space, skip it */ + + /* identify all existing ICE options, if they are listed in one row */ + while (*ptr_src && *ptr_src != '\r' && *ptr_src != '\n' && max_options-->0) + { + while (*ptr_src != 32 && *ptr_src && *ptr_src != '\r' && *ptr_src != '\n') + { + length++; + ptr_src++; + } + + ice_opt = add_sdp_ice_opt(stream); + if (ice_opt == NULL) { + LM_ERR("failed to add ice option\n"); + return -1; + } + + ice_opt->option.s = ptr_src-length; + ice_opt->option.len = length; + trim_len(ice_opt->option.len, ice_opt->option.s, ice_opt->option); + + length = 0; + if (*ptr_src == 32) ptr_src++; /* skip space */ + } + + return 0; +} int extract_ptime(str *body, str *ptime) { diff --git a/src/core/parser/sdp/sdp_helpr_funcs.h b/src/core/parser/sdp/sdp_helpr_funcs.h index 406f57b1659..3c4c7d78a26 100644 --- a/src/core/parser/sdp/sdp_helpr_funcs.h +++ b/src/core/parser/sdp/sdp_helpr_funcs.h @@ -52,6 +52,7 @@ int extract_mediaip(str *body, str *mediaip, int *pf, char *line); int extract_media_attr(str *body, str *mediamedia, str *mediaport, str *mediatransport, str *mediapayload, int *is_rtp); int extract_bwidth(str *body, str *bwtype, str *bwwitdth); int extract_candidate(str *body, sdp_stream_cell_t *stream); +int extract_ice_option(str *body, sdp_stream_cell_t *stream); int extract_sess_version(str* oline, str* sess_version); /* RFC3605 attributes */