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_s... 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_v... 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);}