diff --git a/README.md b/README.md index 9a0cd5444..60612500c 100644 --- a/README.md +++ b/README.md @@ -986,10 +986,22 @@ Optionally included keys are: Similar to `SDES` but controls OSRTP behaviour. Default behaviour is to pass through OSRTP negotiations. Supported options: - - `offer` - - When processing a non-OSRTP offer, convert it to an OSRTP offer. Will result - in RTP/SRTP transcoding if the OSRTP offer is accepted. + - `offer` or `offer-RFC` + + When processing a non-OSRTP offer, convert it to an OSRTP offer. Will + result in RTP/SRTP transcoding if the OSRTP offer is accepted. The + transport protocol should be a non-SRTP (plain RTP) protocol such as + RTP/AVP. + + - `offer-legacy` + + Convert a regular offer to a legacy, non-RFC "best effort" SRTP offer, + which involves duplicating each SDP media section in the output, + advertised once as plain RTP and once as SRTP. The transport protocol + should be set to an SRTP protocol such as RTP/SAVP. To enable full + interoperability with endpoints which support this usage, the flag + `accept-legacy` (see below) should also be given in all signalling + exchanges. - `accept-RFC` diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 5e9cded6a..5529ff0b6 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -591,7 +591,12 @@ INLINE void ng_osrtp_option(struct sdp_ng_flags *out, str *s, void *dummy) { out->osrtp_accept_rfc = 1; out->osrtp_accept_legacy = 1; break; + case CSH_LOOKUP("offer-legacy"): + out->osrtp_offer_legacy = 1; + break; case CSH_LOOKUP("offer"): + case CSH_LOOKUP("offer-RFC"): + case CSH_LOOKUP("offer-rfc"): out->osrtp_offer = 1; break; default: diff --git a/daemon/sdp.c b/daemon/sdp.c index 02a3b81bb..77a53e181 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -1558,38 +1558,39 @@ static void sp_free(void *p) { // associating offer/answer media sections directly with each other, instead of requiring // the indexing to be in order and instead of requiring all sections between monologue and sdp_media // lists to be matching. -static void legacy_osrtp_accept(struct stream_params *sp, GQueue *streams, GList *media_link, +// returns: discard this `sp` yes/no +static bool legacy_osrtp_accept(struct stream_params *sp, GQueue *streams, GList *media_link, struct sdp_ng_flags *flags, unsigned int *num) { if (!streams->tail) - return; + return false; if (!media_link || !media_link->prev) - return; + return false; struct stream_params *last = streams->tail->data; if (!flags->osrtp_accept_legacy) - return; + return false; // protocols must be known if (!sp->protocol) - return; + return false; if (!last->protocol) - return; + return false; // types must match if (sp->type_id != last->type_id) - return; + return false; // we must be looking at a SRTP media section if (!sp->protocol->rtp) - return; + return false; if (!sp->protocol->srtp) - return; + return false; // previous one must be a plain RTP section if (!last->protocol->rtp) - return; + return false; if (last->protocol->srtp) - return; + return false; // is this a non-rejected SRTP section? if (sp->rtp_endpoint.port) { @@ -1602,7 +1603,19 @@ static void legacy_osrtp_accept(struct stream_params *sp, GQueue *streams, GList prev_media->legacy_osrtp = 1; sp->index--; (*num)--; + return false; } + + // or is it a rejected SRTP with a non-rejected RTP counterpart? + if (!sp->rtp_endpoint.port && last->rtp_endpoint.port) { + // just throw the rejected SRTP section away + struct sdp_media *media = media_link->data; + media->legacy_osrtp = 1; + sp_free(sp); + return true; + } + + return false; } /* XXX split this function up */ @@ -1730,7 +1743,8 @@ int sdp_streams(const GQueue *sessions, GQueue *streams, struct sdp_ng_flags *fl sp->protocol = &transport_protocols[sp->protocol->osrtp_proto]; } - legacy_osrtp_accept(sp, streams, k, flags, &num); + if (legacy_osrtp_accept(sp, streams, k, flags, &num)) + continue; // a=mid attr = attr_get_by_id(&media->attributes, ATTR_MID); @@ -2454,6 +2468,8 @@ static void insert_dtls(GString *s, struct call_media *media, struct dtls_connec const char *actpass; struct call *call = media->call; + if (!media->protocol || !media->protocol->srtp) + return; if (!call->dtls_cert || !MEDIA_ISSET(media, DTLS) || MEDIA_ISSET(media, PASSTHRU)) return; @@ -2558,6 +2574,8 @@ static void insert_crypto1(GString *s, struct call_media *media, struct crypto_p g_string_append(s, "\r\n"); } static void insert_crypto(GString *s, struct call_media *media, struct sdp_ng_flags *flags) { + if (!media->protocol || !media->protocol->srtp) + return; for (GList *l = media->sdes_out.head; l; l = l->next) insert_crypto1(s, media, l->data, flags); } @@ -2791,7 +2809,7 @@ static const char *replace_sdp_media_section(struct sdp_chopper *chop, struct ca is_active = false; next: - print_sdp_media_section(chop->output, call_media, sdp_media,flags, rtp_ps_link, is_active, + print_sdp_media_section(chop->output, call_media, sdp_media, flags, rtp_ps_link, is_active, attr_get_by_id(&sdp_media->attributes, ATTR_END_OF_CANDIDATES)); return NULL; @@ -2940,20 +2958,34 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu if (!rtp_ps_link) goto error; - // generate rejected m= line for accepted legacy OSRTP - if (MEDIA_ISSET(call_media, LEGACY_OSRTP) - && call_media->protocol - && call_media->protocol->srtp) - { - chopper_append_c(chop, "m="); - chopper_append_str(chop, &call_media->type); + if (call_media->protocol && call_media->protocol->srtp) { const struct transport_protocol *prtp = &transport_protocols[call_media->protocol->rtp_proto]; - chopper_append_c(chop, " 0 "); - chopper_append_c(chop, prtp->name); - chopper_append_c(chop, " "); - chopper_append_str(chop, &call_media->format_str); - chopper_append_c(chop, "\r\n"); + if (MEDIA_ISSET(call_media, LEGACY_OSRTP)) { + // generate rejected m= line for accepted legacy OSRTP + chopper_append_c(chop, "m="); + chopper_append_str(chop, &call_media->type); + chopper_append_c(chop, " 0 "); + chopper_append_c(chop, prtp->name); + chopper_append_c(chop, " "); + chopper_append_str(chop, &call_media->format_str); + chopper_append_c(chop, "\r\n"); + } + else if (flags->osrtp_offer_legacy && flags->opmode == OP_OFFER) { + // generate duplicate plain RTP media section for OSRTP offer: + // save current chopper state, save actual protocol, + // print SDP section, restore chopper and protocl + struct sdp_chopper chop_copy = *chop; + const struct transport_protocol *proto = call_media->protocol; + call_media->protocol = prtp; + err = replace_sdp_media_section(chop, call_media, sdp_media, + rtp_ps_link, flags, + keep_zero_address); + *chop = chop_copy; + call_media->protocol = proto; + if (err) + goto error; + } } err = replace_sdp_media_section(chop, call_media, sdp_media, rtp_ps_link, flags, diff --git a/include/call_interfaces.h b/include/call_interfaces.h index e0770fd0b..86bc96317 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -141,6 +141,7 @@ struct sdp_ng_flags { osrtp_accept_legacy:1, osrtp_accept_rfc:1, osrtp_offer:1, + osrtp_offer_legacy:1, reset:1, egress:1, siprec:1, diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index f049bed0c..abb8f4088 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -259,6 +259,134 @@ SDP +new_call; + +offer('add legacy OSRTP offer, reject', + { flags => [ 'OSRTP-offer-legacy' ], 'transport-protocol' => 'RTP/SAVP' }, < [ 'OSRTP-accept-legacy' ] }, < [ 'OSRTP-offer-legacy' ], 'transport-protocol' => 'RTP/SAVP' }, < [ 'OSRTP-accept-legacy' ] }, <