From 677147c10b1e20d62723bddd85d838ed182c20d4 Mon Sep 17 00:00:00 2001 From: Andrey Volk Date: Tue, 30 Apr 2024 00:04:34 +0300 Subject: [PATCH 1/2] Preserve local sdp's order of media lines in soa_sdp_expand_media(). Add sdp_media_exists() API. --- libsofia-sip-ua/sdp/sdp.c | 14 ++++++++++ libsofia-sip-ua/sdp/sofia-sip/sdp.h | 3 +++ libsofia-sip-ua/soa/soa_static.c | 41 +++++++++++++++++++++-------- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/libsofia-sip-ua/sdp/sdp.c b/libsofia-sip-ua/sdp/sdp.c index 8ed84ff35..e56cb2d87 100644 --- a/libsofia-sip-ua/sdp/sdp.c +++ b/libsofia-sip-ua/sdp/sdp.c @@ -1845,6 +1845,20 @@ unsigned sdp_media_count_with(sdp_session_t const *sdp, return count; } +/** Find matching media line in SDP. */ +sdp_media_t** sdp_media_exists(sdp_session_t *sdp, + sdp_media_t const *m0) +{ + sdp_media_t **m; + + if (sdp != NULL) + for (m = &sdp->sdp_media; *m; m = &(*m)->m_next) + if (sdp_media_match_with(*m, m0)) + return m; + + return NULL; +} + /** Return true if media uses RTP */ int sdp_media_uses_rtp(sdp_media_t const *m) { diff --git a/libsofia-sip-ua/sdp/sofia-sip/sdp.h b/libsofia-sip-ua/sdp/sofia-sip/sdp.h index ffc68d6e2..c8e37aaf1 100644 --- a/libsofia-sip-ua/sdp/sofia-sip/sdp.h +++ b/libsofia-sip-ua/sdp/sofia-sip/sdp.h @@ -496,6 +496,9 @@ SOFIAPUBFUN unsigned sdp_media_count(sdp_session_t const *sdp, SOFIAPUBFUN unsigned sdp_media_count_with(sdp_session_t const *sdp, sdp_media_t const *m0); +SOFIAPUBFUN sdp_media_t** sdp_media_exists(sdp_session_t *sdp, + sdp_media_t const *m0); + /** Return true if media uses RTP */ SOFIAPUBFUN int sdp_media_uses_rtp(sdp_media_t const *m); diff --git a/libsofia-sip-ua/soa/soa_static.c b/libsofia-sip-ua/soa/soa_static.c index c80d45ce3..39cf75a92 100644 --- a/libsofia-sip-ua/soa/soa_static.c +++ b/libsofia-sip-ua/soa/soa_static.c @@ -301,23 +301,42 @@ sdp_session_t *soa_sdp_expand_media(su_home_t *home, sdp_session_t const *truncated, sdp_session_t const *complete) { + sdp_session_t *tmp_truncated; sdp_session_t *expanded; - sdp_media_t **m0; - sdp_media_t * const *m1; + sdp_media_t **mE; + sdp_media_t **mT; + sdp_media_t * const *mC; + /* Truncated list that we will be reducing */ + tmp_truncated = sdp_session_dup(home, truncated); + /* New resulting list */ expanded = sdp_session_dup(home, truncated); if (expanded) { - for (m0 = &expanded->sdp_media, m1 = &complete->sdp_media; - *m1; - m1 = &(*m1)->m_next) { - if (!*m0) { - *m0 = soa_sdp_make_rejected_media(home, *m1, expanded, 0); - if (!*m0) - return NULL; + expanded->sdp_media = NULL; /* Empty the list of medias */ + mE = &expanded->sdp_media; /* Pointer to the beginning of the new list */ + + /* Loop through all items in the complete list to preserve complete list's order */ + for (mC = &complete->sdp_media; *mC; mC = &(*mC)->m_next) { + /* Find the corresponding media in the truncated list */ + if ((mT = sdp_media_exists(tmp_truncated, *mC))) { + /* Copy the corresponding media from the truncated list to the new list */ + *mE = sdp_media_dup(home, *mT, expanded); + if (!*mE) + return NULL; + + /* Remove corresponding media from the truncated list */ + *mT = (*mT)->m_next; + } else { + /* If the corresponding media was not found in the truncated list, add a rejected media */ + *mE = soa_sdp_make_rejected_media(home, *mC, expanded, 0); + if (!*mE) + return NULL; + } + + /* Prepare pointer of the new list for a new item */ + mE = &(*mE)->m_next; } - m0 = &(*m0)->m_next; - } } return expanded; From 88e1759093749bcb7fbab164f0beb6c93e226573 Mon Sep 17 00:00:00 2001 From: Jakub Karolczyk Date: Thu, 2 May 2024 12:17:42 +0000 Subject: [PATCH 2/2] Add unit-tests --- libsofia-sip-ua/soa/test_soa.c | 310 +++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) diff --git a/libsofia-sip-ua/soa/test_soa.c b/libsofia-sip-ua/soa/test_soa.c index b937b533e..705098c7b 100644 --- a/libsofia-sip-ua/soa/test_soa.c +++ b/libsofia-sip-ua/soa/test_soa.c @@ -1723,6 +1723,313 @@ int test_media_reject(struct context *ctx) END(); } +int test_media_reject_savp(struct context *ctx) +{ + BEGIN(); + int n; + + soa_session_t *ss; + + char const *local = NONE; + isize_t locallen = (isize_t)-1; + + sdp_session_t const *local_sdp; + sdp_media_t *local_sdp_media; + + char const offer[] = + "v=0\r\n" + "o=left 219498671 2 IN IP4 127.0.0.2\r\n" + "c=IN IP4 127.0.0.2\r\n" + "m=audio 5008 RTP/SAVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 GSM/8000\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:4Kc4qJNVWAJRh9gS1MSEtmlUkIWgIzSE4DuG6KU+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=audio 5008 RTP/AVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 GSM/8000\r\n" + "a=sendrecv\r\n" + "m=video 6008 RTP/SAVP 96\r\n" + "a=rtpmap:96 VP8/90000\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:4Kc4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=video 6008 RTP/AVP 96\r\n" + "a=rtpmap:96 VP8/90000\r\n" + "a=sendrecv\r\n" + "m=text 7008 RTP/SAVP 99\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:5Yn4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=text 7008 RTP/AVP 99\r\n" + "a=sendrecv\r\n" + ; + + char const answer[] = + "v=0\r\n" + "o=right 219498672 2 IN IP4 127.0.0.3\r\n" + "c=IN IP4 127.0.0.2\r\n" + "m=audio 8008 RTP/AVP 8\r\n" + "a=rtpmap:8 PCMU/8000\r\n" + "a=sendrecv\r\n" + "m=video 9008 RTP/AVP 98\r\n" + "a=rtpmap:98 VP8/90000\r\n" + "a=sendrecv\r\n" + "m=text 10008 RTP/AVP 103\r\n" + "a=sendrecv\r\n" + ; + + TEST_1(ss = soa_create("static", ctx->root, ctx)); + + TEST(soa_set_user_sdp(ss, 0, offer, strlen(offer)), 1); + + n = soa_generate_offer(ss, 1, test_completed); TEST(n, 0); + n = soa_set_remote_sdp(ss, NULL, answer, strlen(answer)); TEST(n, 1); + n = soa_process_answer(ss, test_completed); TEST(n, 0); + + TEST_1(soa_is_complete(ss)); + TEST(soa_activate(ss, NULL), 0); + + TEST(soa_is_audio_active(ss), SOA_ACTIVE_SENDRECV); + TEST(soa_is_remote_audio_active(ss), SOA_ACTIVE_SENDRECV); + + TEST(soa_is_video_active(ss), SOA_ACTIVE_SENDRECV); + TEST(soa_is_remote_video_active(ss), SOA_ACTIVE_SENDRECV); + + n = soa_get_local_sdp(ss, &local_sdp, &local, &locallen); TEST(n, 1); + + for (local_sdp_media = local_sdp->sdp_media; local_sdp_media; local_sdp_media = local_sdp_media->m_next) { + printf("test_media_reject_savp(): LOCAL Media type: [%d] Port: [%ld], Proto name: [%s] Rejected: [%d]\r\n", local_sdp_media->m_type, local_sdp_media->m_port, local_sdp_media->m_proto_name, local_sdp_media->m_rejected); + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/SAVP")) { + TEST_1(local_sdp_media->m_rejected && !local_sdp_media->m_port); + } + + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/AVP")) { + TEST_1(!local_sdp_media->m_rejected && local_sdp_media->m_port > 0); + } + } + + TEST_VOID(soa_terminate(ss, NULL)); + + TEST_VOID(soa_destroy(ss)); + + END(); +} + +int test_media_reject_avp(struct context *ctx) +{ + BEGIN(); + int n; + + soa_session_t *ss; + + char const *local = NONE; + isize_t locallen = (isize_t)-1; + + sdp_session_t const *local_sdp; + sdp_media_t *local_sdp_media; + + char const offer[] = + "v=0\r\n" + "o=left 219498671 2 IN IP4 127.0.0.2\r\n" + "c=IN IP4 127.0.0.2\r\n" + "m=audio 5008 RTP/SAVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 GSM/8000\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:4Kc4qJNVWAJRh9gS1MSEtmlUkIWgIzSE4DuG6KU+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=audio 5008 RTP/AVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 GSM/8000\r\n" + "a=sendrecv\r\n" + "m=video 6008 RTP/SAVP 96\r\n" + "a=rtpmap:96 VP8/90000\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:4Kc4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=video 6008 RTP/AVP 96\r\n" + "a=rtpmap:96 VP8/90000\r\n" + "a=sendrecv\r\n" + "m=text 7008 RTP/SAVP 99\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:5Yn4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=text 7008 RTP/AVP 99\r\n" + "a=sendrecv\r\n" + ; + + char const answer[] = + "v=0\r\n" + "o=right 219498672 2 IN IP4 127.0.0.3\r\n" + "c=IN IP4 127.0.0.2\r\n" + "m=audio 8008 RTP/SAVP 8\r\n" + "a=rtpmap:8 PCMU/8000\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:5Yn4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=video 9008 RTP/SAVP 98\r\n" + "a=rtpmap:98 VP8/90000\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:5Yn4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=text 10008 RTP/SAVP 103\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:5Yn4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + ; + + TEST_1(ss = soa_create("static", ctx->root, ctx)); + + TEST(soa_set_user_sdp(ss, 0, offer, strlen(offer)), 1); + + n = soa_generate_offer(ss, 1, test_completed); TEST(n, 0); + n = soa_set_remote_sdp(ss, NULL, answer, strlen(answer)); TEST(n, 1); + n = soa_process_answer(ss, test_completed); TEST(n, 0); + + TEST_1(soa_is_complete(ss)); + TEST(soa_activate(ss, NULL), 0); + + TEST(soa_is_audio_active(ss), SOA_ACTIVE_SENDRECV); + TEST(soa_is_remote_audio_active(ss), SOA_ACTIVE_SENDRECV); + + TEST(soa_is_video_active(ss), SOA_ACTIVE_SENDRECV); + TEST(soa_is_remote_video_active(ss), SOA_ACTIVE_SENDRECV); + + n = soa_get_local_sdp(ss, &local_sdp, &local, &locallen); TEST(n, 1); + + for (local_sdp_media = local_sdp->sdp_media; local_sdp_media; local_sdp_media = local_sdp_media->m_next) { + printf("test_media_reject_avp(): LOCAL Media type: [%d] Port: [%ld], Proto name: [%s] Rejected: [%d]\r\n", local_sdp_media->m_type, local_sdp_media->m_port, local_sdp_media->m_proto_name, local_sdp_media->m_rejected); + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/AVP")) { + TEST_1(local_sdp_media->m_rejected && !local_sdp_media->m_port); + } + + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/SAVP")) { + TEST_1(!local_sdp_media->m_rejected && local_sdp_media->m_port > 0); + } + } + + TEST_VOID(soa_terminate(ss, NULL)); + + TEST_VOID(soa_destroy(ss)); + + END(); +} + +int test_media_reject_savp_avp_mix(struct context *ctx) +{ + BEGIN(); + int n; + + soa_session_t *ss; + + char const *local = NONE; + isize_t locallen = (isize_t)-1; + + sdp_session_t const *local_sdp; + sdp_media_t *local_sdp_media; + + char const offer[] = + "v=0\r\n" + "o=left 219498671 2 IN IP4 127.0.0.2\r\n" + "c=IN IP4 127.0.0.2\r\n" + "m=audio 5008 RTP/SAVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 GSM/8000\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:4Kc4qJNVWAJRh9gS1MSEtmlUkIWgIzSE4DuG6KU+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=audio 5008 RTP/AVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 GSM/8000\r\n" + "a=sendrecv\r\n" + "m=video 6008 RTP/SAVP 96\r\n" + "a=rtpmap:96 VP8/90000\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:4Kc4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=video 6008 RTP/AVP 96\r\n" + "a=rtpmap:96 VP8/90000\r\n" + "a=sendrecv\r\n" + "m=text 7008 RTP/SAVP 99\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:5Yn4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=text 7008 RTP/AVP 99\r\n" + "a=sendrecv\r\n" + ; + + char const answer[] = + "v=0\r\n" + "o=right 219498672 2 IN IP4 127.0.0.3\r\n" + "c=IN IP4 127.0.0.2\r\n" + "m=audio 8008 RTP/AVP 8\r\n" + "a=rtpmap:8 PCMU/8000\r\n" + "a=sendrecv\r\n" + "m=video 9008 RTP/SAVP 98\r\n" + "a=rtpmap:98 VP8/90000\r\n" + "a=crypto:1 AEAD_AES_256_GCM_8 inline:5Yn4qJNVWAJRh9gD1NDEtmlUkIWgIzSE4DuG5TO+QUOAW6XRqinW92SGqPY=\r\n" + "a=sendrecv\r\n" + "m=text 10008 RTP/AVP 103\r\n" + "a=sendrecv\r\n" + ; + + TEST_1(ss = soa_create("static", ctx->root, ctx)); + + TEST(soa_set_user_sdp(ss, 0, offer, strlen(offer)), 1); + + n = soa_generate_offer(ss, 1, test_completed); TEST(n, 0); + n = soa_set_remote_sdp(ss, NULL, answer, strlen(answer)); TEST(n, 1); + n = soa_process_answer(ss, test_completed); TEST(n, 0); + + TEST_1(soa_is_complete(ss)); + TEST(soa_activate(ss, NULL), 0); + + TEST(soa_is_audio_active(ss), SOA_ACTIVE_SENDRECV); + TEST(soa_is_remote_audio_active(ss), SOA_ACTIVE_SENDRECV); + + TEST(soa_is_video_active(ss), SOA_ACTIVE_SENDRECV); + TEST(soa_is_remote_video_active(ss), SOA_ACTIVE_SENDRECV); + + n = soa_get_local_sdp(ss, &local_sdp, &local, &locallen); TEST(n, 1); + + for (local_sdp_media = local_sdp->sdp_media; local_sdp_media; local_sdp_media = local_sdp_media->m_next) { + printf("test_media_reject_savp_avp_mix(): LOCAL Media type: [%d] Port: [%ld], Proto name: [%s] Rejected: [%d]\r\n", local_sdp_media->m_type, local_sdp_media->m_port, local_sdp_media->m_proto_name, local_sdp_media->m_rejected); + /* Check audio */ + if (local_sdp_media->m_type == 2) { + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/SAVP")) { + TEST_1(local_sdp_media->m_rejected && !local_sdp_media->m_port); + } + + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/AVP")) { + TEST_1(!local_sdp_media->m_rejected && local_sdp_media->m_port > 0); + } + } + + /* Check video */ + if (local_sdp_media->m_type == 3) { + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/AVP")) { + TEST_1(local_sdp_media->m_rejected && !local_sdp_media->m_port); + } + + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/SAVP")) { + TEST_1(!local_sdp_media->m_rejected && local_sdp_media->m_port > 0); + } + } + + /* Check text */ + if (local_sdp_media->m_type == 10) { + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/SAVP")) { + TEST_1(local_sdp_media->m_rejected && !local_sdp_media->m_port); + } + + if (!strcasecmp(local_sdp_media->m_proto_name, "RTP/AVP")) { + TEST_1(!local_sdp_media->m_rejected && local_sdp_media->m_port > 0); + } + } + } + + TEST_VOID(soa_terminate(ss, NULL)); + + TEST_VOID(soa_destroy(ss)); + + END(); +} int test_media_replace2(struct context *ctx) { @@ -2712,6 +3019,9 @@ int main(int argc, char *argv[]) retval |= test_media_replace(ctx); SINGLE_FAILURE_CHECK(); retval |= test_media_removal(ctx); SINGLE_FAILURE_CHECK(); retval |= test_media_reject(ctx); SINGLE_FAILURE_CHECK(); + retval |= test_media_reject_savp(ctx); SINGLE_FAILURE_CHECK(); + retval |= test_media_reject_avp(ctx); SINGLE_FAILURE_CHECK(); + retval |= test_media_reject_savp_avp_mix(ctx); SINGLE_FAILURE_CHECK(); retval |= test_media_replace2(ctx); SINGLE_FAILURE_CHECK(); retval |= test_media_mode(ctx); SINGLE_FAILURE_CHECK();