I'm using Freeswitch ESL (Event Socket Library) in Node.js When I receive an invite to SIP.js from a classic sip endpoint the SDP content is regular RTP. I need to be able to connect RTP endpoint to SRTP endpoint using Freeswitch in the middle. In order to do so we need to be able create an endpoint on Freeswitch without SDP (3PCC INVITE) but we want this endpoint to return SRTP SDP and not RTP SDP.
Main question: Does Freeswitch supports generating SRTP offer from an incoming 3PCC INVITE?
I believe this is the part to handle 3PCC INVITE in Freeswitch 1.8.5: https://github.com/signalwire/freeswitch/blob/v1.8.5/src/mod/endpoints/mod_sofia/sofia.c
if (sofia_test_pflag(profile, PFLAG_3PCC)) {
if (switch_channel_test_flag(channel, CF_PROXY_MODE) || switch_channel_test_flag(channel, CF_PROXY_MEDIA)) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "No SDP in INVITE and 3pcc=yes cannot work with bypass or proxy media, hanging up.\n");
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "3PCC DISABLED");
switch_channel_hangup(channel, SWITCH_CAUSE_MANDATORY_IE_MISSING);
} else {
switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOSDP");
switch_core_media_choose_port(tech_pvt->session, SWITCH_MEDIA_TYPE_AUDIO, 0);
switch_core_media_prepare_codecs(session, 1);
switch_channel_set_state(channel, CS_HIBERNATE);
switch_core_media_gen_local_sdp(session, SDP_TYPE_REQUEST, NULL, 0, NULL, 0);
sofia_set_flag_locked(tech_pvt, TFLAG_3PCC);
if (sofia_use_soa(tech_pvt)) {
nua_respond(tech_pvt->nh, SIP_200_OK,
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SOATAG_USER_SDP_STR(tech_pvt->mparams.local_sdp_str),
SOATAG_REUSE_REJECTED(1),
SOATAG_AUDIO_AUX("cn telephone-event"),
TAG_IF(sofia_test_pflag(profile, PFLAG_DISABLE_100REL), NUTAG_INCLUDE_EXTRA_SDP(1)), TAG_END());
} else {
nua_respond(tech_pvt->nh, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTACT_STR(tech_pvt->reply_contact),
SIPTAG_CONTENT_TYPE_STR("application/sdp"), SIPTAG_PAYLOAD_STR(tech_pvt->mparams.local_sdp_str), TAG_END());
}
}
}
Maybe the same pattern they used for mod_verto can be used: https://github.com/signalwire/freeswitch/blob/v1.8.5/src/mod/endpoints/mod_verto/mod_verto.c
This function switch_core_media_gen_local_sdp seems to be the one to create the RTP SDP: https://github.com/signalwire/freeswitch/blob/v1.8.5/src/switch_core_media.c How can we change this to SRTP SDP instead?
static const char *get_media_profile_name(switch_core_session_t *session, int secure)
{
switch_assert(session);
if (switch_channel_test_flag(session->channel, CF_AVPF)) {
if (switch_channel_test_flag(session->channel, CF_DTLS) || secure) {
if (switch_channel_test_flag(session->channel, CF_AVPF_MOZ)) {
return "UDP/TLS/RTP/SAVPF";
} else {
return "RTP/SAVPF";
}
} else {
if (switch_channel_test_flag(session->channel, CF_AVPF_MOZ)) {
return "UDP/AVPF";
} else {
return "RTP/AVPF";
}
}
}
if (secure) {
return "RTP/SAVP";
}
return "RTP/AVP";
}
There are 3 conditions to enable SRTP:
session->channel->flags['CF_AVPF']
(session->channel->flags['CF_DTLS'] || secure)
session->channel->flags['CF_AVPF_MOZ']
The conditions to put CF_AVPF flag are calling switch_core_session_set_ice or:
if (is_outbound || switch_channel_test_flag(session->channel, CF_RECOVERING) ||
switch_channel_test_flag(session->channel, CF_3PCC)) {
if (!switch_channel_test_flag(session->channel, CF_AVPF) &&
switch_true(switch_channel_get_variable(session->channel, "media_webrtc"))) {
switch_channel_set_flag(session->channel, CF_AVPF);
switch_channel_set_flag(session->channel, CF_ICE);
smh->mparams->rtcp_audio_interval_msec = SWITCH_RTCP_AUDIO_INTERVAL_MSEC;
smh->mparams->rtcp_video_interval_msec = SWITCH_RTCP_VIDEO_INTERVAL_MSEC;
}
if (switch_true(switch_channel_get_variable(session->channel, "add_ice_candidates"))) {
switch_channel_set_flag(session->channel, CF_ICE);
}
if ( switch_rtp_has_dtls() && dtls_ok(session)) {
if (switch_channel_test_flag(session->channel, CF_AVPF) ||
switch_true(switch_channel_get_variable(smh->session->channel, "rtp_use_dtls"))) {
switch_channel_set_flag(smh->session->channel, CF_DTLS);
switch_channel_set_flag(smh->session->channel, CF_SECURE);
generate_local_fingerprint(smh, SWITCH_MEDIA_TYPE_AUDIO);
}
}
switch_core_session_parse_crypto_prefs(session);
switch_core_session_check_outgoing_crypto(session);
}
The conditions to put CF_DTLS flag are the same as above (CF_AVPF) or calling check_ice:
if (switch_rtp_has_dtls() && dtls_ok(smh->session) && !strcasecmp(attr->a_name, "fingerprint") && !zstr(attr->a_value)) {
char *p;
engine->remote_dtls_fingerprint.type = switch_core_session_strdup(smh->session, attr->a_value);
if ((p = strchr(engine->remote_dtls_fingerprint.type, ' '))) {
*p++ = '\0';
if (switch_channel_test_flag(smh->session->channel, CF_REINVITE) && !switch_channel_test_flag(smh->session->channel, CF_RECOVERING) &&
!zstr(engine->remote_dtls_fingerprint.str) && !strcmp(engine->remote_dtls_fingerprint.str, p)) {
engine->new_dtls = 0;
} else {
switch_set_string(engine->remote_dtls_fingerprint.str, p);
engine->new_dtls = 1;
engine->new_ice = 1;
}
}
generate_local_fingerprint(smh, type);
switch_channel_set_flag(smh->session->channel, CF_DTLS);
}
There is also the possibility to send true for the secure parameter but the conditions are many.
The conditions to put CF_AVPF_MOZ flag:
if (m->m_proto_name && !strcasecmp(m->m_proto_name, "UDP/TLS/RTP/SAVPF")) {
switch_channel_set_flag(session->channel, CF_AVPF_MOZ);
}
if (m->m_proto_name && !strcasecmp(m->m_proto_name, "UDP/RTP/AVPF")) {
switch_channel_set_flag(session->channel, CF_AVPF_MOZ);
}