[asterisk-commits] file: trunk r373234 - in /trunk: ./ channels/ channels/sip/include/ configs/ ...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Sep 20 13:27:36 CDT 2012
Author: file
Date: Thu Sep 20 13:27:28 2012
New Revision: 373234
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=373234
Log:
Add support for DTLS-SRTP to res_rtp_asterisk and chan_sip.
As mentioned on the review for this, WebRTC has moved towards choosing
DTLS-SRTP as the mechanism for key exchange for SRTP. This commit adds
support for this but makes it available for normal SIP clients as well.
Testing has been done to ensure that this introduces no regressions with
existing behavior and also that it functions as expected.
Review: https://reviewboard.asterisk.org/r/2113/
........
Merged revisions 373229 from http://svn.asterisk.org/svn/asterisk/branches/11
Modified:
trunk/ (props changed)
trunk/channels/chan_sip.c
trunk/channels/sip/include/sip.h
trunk/configs/sip.conf.sample
trunk/configure
trunk/configure.ac
trunk/include/asterisk/autoconfig.h.in
trunk/include/asterisk/rtp_engine.h
trunk/main/rtp_engine.c
trunk/res/res_rtp_asterisk.c
Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-11-merged' - no diff available.
Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=373234&r1=373233&r2=373234
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Thu Sep 20 13:27:28 2012
@@ -1387,11 +1387,13 @@
static int process_sdp_c(const char *c, struct ast_sockaddr *addr);
static int process_sdp_a_sendonly(const char *a, int *sendonly);
static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
+static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec);
static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec);
static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec);
static int process_sdp_a_image(const char *a, struct sip_pvt *p);
static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
+static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf);
static void start_ice(struct ast_rtp_instance *instance);
static void add_codec_to_sdp(const struct sip_pvt *p, struct ast_format *codec,
struct ast_str **m_buf, struct ast_str **a_buf,
@@ -4928,6 +4930,8 @@
ast_string_field_free_memory(peer);
peer->caps = ast_format_cap_destroy(peer->caps);
+
+ ast_rtp_dtls_cfg_free(&peer->dtls_cfg);
}
/*! \brief Update peer data in database (if used) */
@@ -5515,6 +5519,29 @@
*to_sock = *from_sock;
}
+/*! \brief Initialize DTLS-SRTP support on an RTP instance */
+static int dialog_initialize_dtls_srtp(const struct sip_pvt *dialog, struct ast_rtp_instance *rtp, struct sip_srtp **srtp)
+{
+ struct ast_rtp_engine_dtls *dtls;
+
+ if (!dialog->dtls_cfg.enabled) {
+ return 0;
+ }
+
+ if (!ast_rtp_engine_srtp_is_registered()) {
+ ast_log(LOG_ERROR, "No SRTP module loaded, can't setup SRTP session.\n");
+ return -1;
+ }
+
+ if (!(dtls = ast_rtp_instance_get_dtls(rtp)) ||
+ dtls->set_configuration(rtp, &dialog->dtls_cfg) ||
+ !(*srtp = sip_srtp_alloc())) {
+ return -1;
+ }
+
+ return 0;
+}
+
/*! \brief Initialize RTP portion of a dialog
* \return -1 on failure, 0 on success
*/
@@ -5536,6 +5563,10 @@
ice->stop(dialog->rtp);
}
+ if (dialog_initialize_dtls_srtp(dialog, dialog->rtp, &dialog->srtp)) {
+ return -1;
+ }
+
if (ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT_ALWAYS) ||
(ast_test_flag(&dialog->flags[1], SIP_PAGE2_VIDEOSUPPORT) && (ast_format_cap_has_type(dialog->caps, AST_FORMAT_TYPE_VIDEO)))) {
if (!(dialog->vrtp = ast_rtp_instance_new(dialog->engine, sched, &bindaddr_tmp, NULL))) {
@@ -5546,6 +5577,10 @@
ice->stop(dialog->vrtp);
}
+ if (dialog_initialize_dtls_srtp(dialog, dialog->vrtp, &dialog->vsrtp)) {
+ return -1;
+ }
+
ast_rtp_instance_set_timeout(dialog->vrtp, dialog->rtptimeout);
ast_rtp_instance_set_hold_timeout(dialog->vrtp, dialog->rtpholdtimeout);
ast_rtp_instance_set_keepalive(dialog->vrtp, dialog->rtpkeepalive);
@@ -5560,6 +5595,10 @@
if (!ast_test_flag(&dialog->flags[2], SIP_PAGE3_ICE_SUPPORT) && (ice = ast_rtp_instance_get_ice(dialog->trtp))) {
ice->stop(dialog->trtp);
+ }
+
+ if (dialog_initialize_dtls_srtp(dialog, dialog->trtp, &dialog->tsrtp)) {
+ return -1;
}
/* Do not timeout text as its not constant*/
@@ -5617,6 +5656,8 @@
dialog->amaflags = peer->amaflags;
ast_string_field_set(dialog, engine, peer->engine);
+
+ ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &dialog->dtls_cfg);
dialog->rtptimeout = peer->rtptimeout;
dialog->rtpholdtimeout = peer->rtpholdtimeout;
@@ -6262,6 +6303,8 @@
p->peercaps = ast_format_cap_destroy(p->peercaps);
p->redircaps = ast_format_cap_destroy(p->redircaps);
p->prefcaps = ast_format_cap_destroy(p->prefcaps);
+
+ ast_rtp_dtls_cfg_free(&p->dtls_cfg);
if (p->last_device_state_info) {
ao2_ref(p->last_device_state_info, -1);
@@ -9577,6 +9620,16 @@
processed = TRUE;
}
+ if (process_sdp_a_dtls(value, p, p->rtp)) {
+ processed = TRUE;
+ }
+ if (process_sdp_a_dtls(value, p, p->vrtp)) {
+ processed = TRUE;
+ }
+ if (process_sdp_a_dtls(value, p, p->trtp)) {
+ processed = TRUE;
+ }
+
break;
}
@@ -9594,8 +9647,9 @@
int image = FALSE;
int text = FALSE;
int processed_crypto = FALSE;
- char protocol[6] = {0,};
+ char protocol[18] = {0,};
int x;
+ struct ast_rtp_engine_dtls *dtls;
numberofports = 0;
len = -1;
@@ -9614,50 +9668,60 @@
/* Check for 'audio' media offer */
if (strncmp(m, "audio ", 6) == 0) {
- if ((sscanf(m, "audio %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
- (sscanf(m, "audio %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
+ if ((sscanf(m, "audio %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+ (sscanf(m, "audio %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
codecs = m + len;
/* produce zero-port m-line since it may be needed later
- * length is "m=audio 0 RTP/" + protocol + " " + codecs + "\0" */
- if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+ * length is "m=audio 0 " + protocol + " " + codecs + "\0" */
+ if (!(offer->decline_m_line = ast_malloc(10 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
res = -1;
goto process_sdp_cleanup;
}
/* guaranteed to be exactly the right length */
- sprintf(offer->decline_m_line, "m=audio 0 RTP/%s %s", protocol, codecs);
+ sprintf(offer->decline_m_line, "m=audio 0 %s %s", protocol, codecs);
if (x == 0) {
ast_log(LOG_WARNING, "Ignoring audio media offer because port number is zero\n");
continue;
}
+ if (has_media_stream(p, SDP_AUDIO)) {
+ ast_log(LOG_WARNING, "Declining non-primary audio stream: %s\n", m);
+ continue;
+ }
+
/* Check number of ports offered for stream */
if (numberofports > 1) {
ast_log(LOG_WARNING, "%d ports offered for audio media, not supported by Asterisk. Will try anyway...\n", numberofports);
}
- if (!strcmp(protocol, "SAVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+ if ((!strcmp(protocol, "RTP/SAVPF") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received SAVPF profle in audio offer but AVPF is not enabled: %s\n", m);
continue;
- } else if (!strcmp(protocol, "SAVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+ } else if ((!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVP")) && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received SAVP profile in audio offer but AVPF is enabled: %s\n", m);
continue;
- } else if (!strcmp(protocol, "SAVP") || !strcmp(protocol, "SAVPF")) {
+ } else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
secure_audio = 1;
- } else if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+
+ if (p->srtp) {
+ ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK);
+ }
+ } else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
+ secure_audio = 1;
+ } else if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received AVPF profile in audio offer but AVPF is not enabled: %s\n", m);
continue;
- } else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+ } else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received AVP profile in audio offer but AVPF is enabled: %s\n", m);
continue;
- } else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
+ } else if ((!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) &&
+ (!(dtls = ast_rtp_instance_get_dtls(p->rtp)) || !dtls->active(p->rtp))) {
+ ast_log(LOG_WARNING, "Received UDP/TLS in audio offer but DTLS is not enabled: %s\n", m);
+ continue;
+ } else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
ast_log(LOG_WARNING, "Unknown RTP profile in audio offer: %s\n", m);
- continue;
- }
-
- if (has_media_stream(p, SDP_AUDIO)) {
- ast_log(LOG_WARNING, "Declining non-primary audio stream: %s\n", m);
continue;
}
@@ -9686,18 +9750,18 @@
}
/* Check for 'video' media offer */
else if (strncmp(m, "video ", 6) == 0) {
- if ((sscanf(m, "video %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
- (sscanf(m, "video %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
+ if ((sscanf(m, "video %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+ (sscanf(m, "video %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
codecs = m + len;
/* produce zero-port m-line since it may be needed later
- * length is "m=video 0 RTP/" + protocol + " " + codecs + "\0" */
- if (!(offer->decline_m_line = ast_malloc(14 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+ * length is "m=video 0 " + protocol + " " + codecs + "\0" */
+ if (!(offer->decline_m_line = ast_malloc(10 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
res = -1;
goto process_sdp_cleanup;
}
/* guaranteed to be exactly the right length */
- sprintf(offer->decline_m_line, "m=video 0 RTP/%s %s", protocol, codecs);
+ sprintf(offer->decline_m_line, "m=video 0 %s %s", protocol, codecs);
if (x == 0) {
ast_log(LOG_WARNING, "Ignoring video stream offer because port number is zero\n");
@@ -9709,27 +9773,33 @@
ast_log(LOG_WARNING, "%d ports offered for video stream, not supported by Asterisk. Will try anyway...\n", numberofports);
}
- if (!strcmp(protocol, "SAVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+ if (has_media_stream(p, SDP_VIDEO)) {
+ ast_log(LOG_WARNING, "Declining non-primary video stream: %s\n", m);
+ continue;
+ }
+
+ if ((!strcmp(protocol, "RTP/SAVPF") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received SAVPF profle in video offer but AVPF is not enabled: %s\n", m);
continue;
- } else if (!strcmp(protocol, "SAVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+ } else if ((!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVP")) && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received SAVP profile in video offer but AVPF is enabled: %s\n", m);
continue;
- } else if (!strcmp(protocol, "SAVP") || !strcmp(protocol, "SAVPF")) {
+ } else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
secure_video = 1;
- } else if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+
+ if (p->vsrtp || (p->vsrtp = sip_srtp_alloc())) {
+ ast_set_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK);
+ }
+ } else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
+ secure_video = 1;
+ } else if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received AVPF profile in video offer but AVPF is not enabled: %s\n", m);
continue;
- } else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+ } else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received AVP profile in video offer but AVPF is enabled: %s\n", m);
continue;
- } else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
+ } else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
ast_log(LOG_WARNING, "Unknown RTP profile in video offer: %s\n", m);
- continue;
- }
-
- if (has_media_stream(p, SDP_VIDEO)) {
- ast_log(LOG_WARNING, "Declining non-primary video stream: %s\n", m);
continue;
}
@@ -9758,18 +9828,18 @@
}
/* Check for 'text' media offer */
else if (strncmp(m, "text ", 5) == 0) {
- if ((sscanf(m, "text %30u/%30u RTP/%5s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
- (sscanf(m, "text %30u RTP/%5s %n", &x, protocol, &len) == 2 && len > 0)) {
+ if ((sscanf(m, "text %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
+ (sscanf(m, "text %30u %17s %n", &x, protocol, &len) == 2 && len > 0)) {
codecs = m + len;
/* produce zero-port m-line since it may be needed later
- * length is "m=text 0 RTP/" + protocol + " " + codecs + "\0" */
- if (!(offer->decline_m_line = ast_malloc(13 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
+ * length is "m=text 0 " + protocol + " " + codecs + "\0" */
+ if (!(offer->decline_m_line = ast_malloc(9 + strlen(protocol) + 1 + strlen(codecs) + 1))) {
ast_log(LOG_WARNING, "Failed to allocate memory for SDP offer declination\n");
res = -1;
goto process_sdp_cleanup;
}
/* guaranteed to be exactly the right length */
- sprintf(offer->decline_m_line, "m=text 0 RTP/%s %s", protocol, codecs);
+ sprintf(offer->decline_m_line, "m=text 0 %s %s", protocol, codecs);
if (x == 0) {
ast_log(LOG_WARNING, "Ignoring text stream offer because port number is zero\n");
@@ -9781,13 +9851,13 @@
ast_log(LOG_WARNING, "%d ports offered for text stream, not supported by Asterisk. Will try anyway...\n", numberofports);
}
- if (!strcmp(protocol, "AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+ if (!strcmp(protocol, "RTP/AVPF") && !ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received AVPF profile in text offer but AVPF is not enabled: %s\n", m);
continue;
- } else if (!strcmp(protocol, "AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+ } else if (!strcmp(protocol, "RTP/AVP") && ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
ast_log(LOG_WARNING, "Received AVP profile in text offer but AVPF is enabled: %s\n", m);
continue;
- } else if (strcmp(protocol, "AVP") && strcmp(protocol, "AVPF")) {
+ } else if (strcmp(protocol, "RTP/AVP") && strcmp(protocol, "RTP/AVPF")) {
ast_log(LOG_WARNING, "Unknown RTP profile in text offer: %s\n", m);
continue;
}
@@ -9923,6 +9993,8 @@
if (audio) {
if (process_sdp_a_ice(value, p, p->rtp)) {
processed = TRUE;
+ } else if (process_sdp_a_dtls(value, p, p->rtp)) {
+ processed = TRUE;
} else if (process_sdp_a_sendonly(value, &sendonly)) {
processed = TRUE;
} else if (!processed_crypto && process_crypto(p, p->rtp, &p->srtp, value)) {
@@ -9936,6 +10008,8 @@
else if (video) {
if (process_sdp_a_ice(value, p, p->vrtp)) {
processed = TRUE;
+ } else if (process_sdp_a_dtls(value, p, p->vrtp)) {
+ processed = TRUE;
} else if (!processed_crypto && process_crypto(p, p->vrtp, &p->vsrtp, value)) {
processed_crypto = TRUE;
processed = TRUE;
@@ -9947,7 +10021,7 @@
else if (text) {
if (process_sdp_a_ice(value, p, p->trtp)) {
processed = TRUE;
- } if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec)) {
+ } else if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec)) {
processed = TRUE;
} else if (!processed_crypto && process_crypto(p, p->trtp, &p->tsrtp, value)) {
processed_crypto = TRUE;
@@ -10461,6 +10535,56 @@
} else if (!strcasecmp(a, "ice-lite")) {
ice->ice_lite(instance);
found = TRUE;
+ }
+
+ return found;
+}
+
+static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance)
+{
+ struct ast_rtp_engine_dtls *dtls;
+ int found = FALSE;
+ char value[256], hash[6];
+
+ if (!instance || !p->dtls_cfg.enabled || !(dtls = ast_rtp_instance_get_dtls(instance))) {
+ return found;
+ }
+
+ if (sscanf(a, "setup: %255s", value) == 1) {
+ found = TRUE;
+
+ if (!strcasecmp(value, "active")) {
+ dtls->set_setup(instance, AST_RTP_DTLS_SETUP_ACTIVE);
+ } else if (!strcasecmp(value, "passive")) {
+ dtls->set_setup(instance, AST_RTP_DTLS_SETUP_PASSIVE);
+ } else if (!strcasecmp(value, "actpass")) {
+ dtls->set_setup(instance, AST_RTP_DTLS_SETUP_ACTPASS);
+ } else if (!strcasecmp(value, "holdconn")) {
+ dtls->set_setup(instance, AST_RTP_DTLS_SETUP_HOLDCONN);
+ } else {
+ ast_log(LOG_WARNING, "Unsupported setup attribute value '%s' received on dialog '%s'\n",
+ value, p->callid);
+ }
+ } else if (sscanf(a, "connection: %255s", value) == 1) {
+ found = TRUE;
+
+ if (!strcasecmp(value, "new")) {
+ dtls->reset(instance);
+ } else if (!strcasecmp(value, "existing")) {
+ /* Since they want to just use what already exists we go on as if nothing happened */
+ } else {
+ ast_log(LOG_WARNING, "Unsupported connection attribute value '%s' received on dialog '%s'\n",
+ value, p->callid);
+ }
+ } else if (sscanf(a, "fingerprint: %5s %255s", hash, value) == 2) {
+ found = TRUE;
+
+ if (!strcasecmp(hash, "sha-1")) {
+ dtls->set_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1, value);
+ } else {
+ ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s' received on dialog '%s'\n",
+ hash, p->callid);
+ }
}
return found;
@@ -12053,6 +12177,49 @@
ice->start(instance);
}
+/*! \brief Add DTLS attributes to SDP */
+static void add_dtls_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf)
+{
+ struct ast_rtp_engine_dtls *dtls;
+ const char *fingerprint;
+
+ if (!instance || !(dtls = ast_rtp_instance_get_dtls(instance)) || !dtls->active(instance)) {
+ return;
+ }
+
+ switch (dtls->get_connection(instance)) {
+ case AST_RTP_DTLS_CONNECTION_NEW:
+ ast_str_append(a_buf, 0, "a=connection:new\r\n");
+ break;
+ case AST_RTP_DTLS_CONNECTION_EXISTING:
+ ast_str_append(a_buf, 0, "a=connection:existing\r\n");
+ break;
+ default:
+ break;
+ }
+
+ switch (dtls->get_setup(instance)) {
+ case AST_RTP_DTLS_SETUP_ACTIVE:
+ ast_str_append(a_buf, 0, "a=setup:active\r\n");
+ break;
+ case AST_RTP_DTLS_SETUP_PASSIVE:
+ ast_str_append(a_buf, 0, "a=setup:passive\r\n");
+ break;
+ case AST_RTP_DTLS_SETUP_ACTPASS:
+ ast_str_append(a_buf, 0, "a=setup:actpass\r\n");
+ break;
+ case AST_RTP_DTLS_SETUP_HOLDCONN:
+ ast_str_append(a_buf, 0, "a=setup:holdconn\r\n");
+ break;
+ default:
+ break;
+ }
+
+ if ((fingerprint = dtls->get_fingerprint(instance, AST_RTP_DTLS_HASH_SHA1))) {
+ ast_str_append(a_buf, 0, "a=fingerprint:SHA-1 %s\r\n", fingerprint);
+ }
+}
+
/*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
static void add_codec_to_sdp(const struct sip_pvt *p,
struct ast_format *format,
@@ -12323,6 +12490,11 @@
srtp->crypto = sdp_crypto_setup();
}
+ if (p->dtls_cfg.enabled) {
+ /* If DTLS-SRTP is enabled the key details will be pulled from TLS */
+ return;
+ }
+
/* set the key length based on INVITE or settings */
if (ast_test_flag(srtp, SRTP_CRYPTO_TAG_80)) {
taglen = 80;
@@ -12341,12 +12513,18 @@
}
}
-static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure)
-{
- if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
- return secure ? "SAVPF" : "AVPF";
+static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure, struct ast_rtp_instance *instance)
+{
+ struct ast_rtp_engine_dtls *dtls;
+
+ if ((dtls = ast_rtp_instance_get_dtls(instance)) && dtls->active(instance)) {
+ return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "UDP/TLS/RTP/SAVPF" : "UDP/TLS/RTP/SAVP";
} else {
- return secure ? "SAVP" : "AVP";
+ if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
+ return secure ? "RTP/SAVPF" : "RTP/AVPF";
+ } else {
+ return secure ? "RTP/SAVP" : "RTP/AVP";
+ }
}
}
@@ -12509,8 +12687,8 @@
Video is handled differently than audio since we can not transcode. */
if (needvideo) {
get_crypto_attrib(p, p->vsrtp, &v_a_crypto);
- ast_str_append(&m_video, 0, "m=video %d RTP/%s", ast_sockaddr_port(&vdest),
- get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
+ ast_str_append(&m_video, 0, "m=video %d %s", ast_sockaddr_port(&vdest),
+ get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->vrtp));
/* Build max bitrate string */
if (p->maxcallbitrate)
@@ -12519,8 +12697,12 @@
ast_verbose("Video is at %s\n", ast_sockaddr_stringify(&vdest));
}
- if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
- add_ice_to_sdp(p->vrtp, &a_video);
+ if (!doing_directmedia) {
+ if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
+ add_ice_to_sdp(p->vrtp, &a_video);
+ }
+
+ add_dtls_to_sdp(p->vrtp, &a_video);
}
}
@@ -12530,14 +12712,18 @@
if (sipdebug_text)
ast_verbose("Lets set up the text sdp\n");
get_crypto_attrib(p, p->tsrtp, &t_a_crypto);
- ast_str_append(&m_text, 0, "m=text %d RTP/%s", ast_sockaddr_port(&tdest),
- get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
+ ast_str_append(&m_text, 0, "m=text %d %s", ast_sockaddr_port(&tdest),
+ get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->trtp));
if (debug) { /* XXX should I use tdest below ? */
ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
}
- if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
- add_ice_to_sdp(p->trtp, &a_text);
+ if (!doing_directmedia) {
+ if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
+ add_ice_to_sdp(p->trtp, &a_text);
+ }
+
+ add_dtls_to_sdp(p->trtp, &a_text);
}
}
@@ -12547,8 +12733,8 @@
peer doesn't have to ast_gethostbyname() us */
get_crypto_attrib(p, p->srtp, &a_crypto);
- ast_str_append(&m_audio, 0, "m=audio %d RTP/%s", ast_sockaddr_port(&dest),
- get_sdp_rtp_profile(p, a_crypto ? 1 : 0));
+ ast_str_append(&m_audio, 0, "m=audio %d %s", ast_sockaddr_port(&dest),
+ get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->rtp));
/* Now, start adding audio codecs. These are added in this order:
- First what was requested by the calling channel
@@ -12635,8 +12821,12 @@
if (min_text_packet_size)
ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size);
- if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
- add_ice_to_sdp(p->rtp, &a_audio);
+ if (!doing_directmedia) {
+ if (ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) {
+ add_ice_to_sdp(p->rtp, &a_audio);
+ }
+
+ add_dtls_to_sdp(p->rtp, &a_audio);
}
if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 ||
@@ -17354,6 +17544,8 @@
set_t38_capabilities(p);
}
+ ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &p->dtls_cfg);
+
/* Copy SIP extensions profile to peer */
/* XXX is this correct before a successful auth ? */
if (p->sipoptions)
@@ -17399,6 +17591,8 @@
ast_set_flag(&p->flags[0], SIP_CALL_LIMIT);
ast_string_field_set(p, peername, peer->name);
ast_string_field_set(p, authname, peer->name);
+
+ ast_rtp_dtls_cfg_copy(&peer->dtls_cfg, &p->dtls_cfg);
if (sipmethod == SIP_INVITE) {
/* destroy old channel vars and copy in new ones. */
@@ -30009,8 +30203,13 @@
ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_USE_AVPF);
} else if (!strcasecmp(v->name, "icesupport")) {
ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_ICE_SUPPORT);
- }
- }
+ } else {
+ ast_rtp_dtls_cfg_parse(&peer->dtls_cfg, v->name, v->value);
+ }
+ }
+
+ /* Apply the encryption tag length to the DTLS configuration, in case DTLS is in use */
+ peer->dtls_cfg.suite = (ast_test_flag(&peer->flags[2], SIP_PAGE3_SRTP_TAG_32) ? AST_AES_CM_128_HMAC_SHA1_32 : AST_AES_CM_128_HMAC_SHA1_80);
/* These apply to devstate lookups */
if (realtime && !strcasecmp(v->name, "lastms")) {
@@ -32201,6 +32400,8 @@
static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_srtp **srtp, const char *a)
{
+ struct ast_rtp_engine_dtls *dtls;
+
/* If no RTP instance exists for this media stream don't bother processing the crypto line */
if (!rtp) {
ast_debug(3, "Received offer with crypto line for media stream that is not enabled\n");
@@ -32230,6 +32431,11 @@
}
ast_set_flag(*srtp, SRTP_CRYPTO_OFFER_OK);
+
+ if ((dtls = ast_rtp_instance_get_dtls(rtp))) {
+ dtls->stop(rtp);
+ p->dtls_cfg.enabled = 0;
+ }
return TRUE;
}
Modified: trunk/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/include/sip.h?view=diff&rev=373234&r1=373233&r2=373234
==============================================================================
--- trunk/channels/sip/include/sip.h (original)
+++ trunk/channels/sip/include/sip.h Thu Sep 20 13:27:28 2012
@@ -36,6 +36,7 @@
#include "asterisk/security_events.h"
#include "asterisk/features.h"
#include "asterisk/http_websocket.h"
+#include "asterisk/rtp_engine.h"
#ifndef FALSE
#define FALSE 0
@@ -1212,6 +1213,8 @@
struct ast_cc_config_params *cc_params;
struct sip_epa_entry *epa_entry;
int fromdomainport; /*!< Domain port to show in from field */
+
+ struct ast_rtp_dtls_cfg dtls_cfg;
};
/*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission
@@ -1361,6 +1364,8 @@
enum sip_peer_type type; /*!< Distinguish between "user" and "peer" types. This is used solely for CLI and manager commands */
unsigned int disallowed_methods;
struct ast_cc_config_params *cc_params;
+
+ struct ast_rtp_dtls_cfg dtls_cfg;
};
/*!
Modified: trunk/configs/sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/sip.conf.sample?view=diff&rev=373234&r1=373233&r2=373234
==============================================================================
--- trunk/configs/sip.conf.sample (original)
+++ trunk/configs/sip.conf.sample Thu Sep 20 13:27:28 2012
@@ -1240,6 +1240,38 @@
; maxforwards
; encryption
; description ; Used to provide a description of the peer in console output
+; dtlsenable
+; dtlsverify
+; dtlsrekey
+; dtlscertfile
+; dtlsprivatekey
+; dtlscipher
+; dtlscafile
+; dtlscapath
+; dtlssetup
+;
+
+;------------------------------------------------------------------------------
+; DTLS-SRTP CONFIGURATION
+;
+; DTLS-SRTP support is available if the underlying RTP engine in use supports it.
+;
+; dtlsenable = yes ; Enable or disable DTLS-SRTP support
+; dtlsverify = yes ; Verify that the provided peer certificate is valid
+; dtlsrekey = 60 ; Interval at which to renegotiate the TLS session and rekey the SRTP session
+; ; If this is not set or the value provided is 0 rekeying will be disabled
+; dtlscertfile = file ; Path to certificate file to present
+; dtlsprivatekey = file ; Path to private key for certificate file
+; dtlscipher = <SSL cipher string> ; Cipher to use for TLS negotiation
+; ; A list of valid SSL cipher strings can be found at:
+; ; http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
+; dtlscafile = file ; Path to certificate authority certificate
+; dtlscapath = path ; Path to a directory containing certificate authority certificates
+; dtlssetup = actpass ; Whether we are willing to accept connections, connect to the other party, or both.
+; ; Valid options are active (we want to connect to the other party), passive (we want to
+; ; accept connections only), and actpass (we will do both). This value will be used in
+; ; the outgoing SDP when offering and for incoming SDP offers when the remote party sends
+; ; actpass
;[sip_proxy]
; For incoming calls only. Example: FWD (Free World Dialup)
Modified: trunk/configure.ac
URL: http://svnview.digium.com/svn/asterisk/trunk/configure.ac?view=diff&rev=373234&r1=373233&r2=373234
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Thu Sep 20 13:27:28 2012
@@ -387,6 +387,7 @@
AST_EXT_LIB_SETUP_OPTIONAL([COROSYNC_CFG_STATE_TRACK], [A callback only in corosync 1.x], [COROSYNC], [cfg])
AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography], [crypto])
+AST_EXT_LIB_SETUP_OPTIONAL([OPENSSL_SRTP], [OpenSSL SRTP Extension Support], [CRYPTO], [crypto])
AST_EXT_LIB_SETUP([DAHDI], [DAHDI], [dahdi])
AST_EXT_LIB_SETUP([FFMPEG], [Ffmpeg and avcodec], [avcodec])
AST_EXT_LIB_SETUP([GSM], [External GSM], [gsm], [, use 'internal' GSM otherwise])
@@ -2103,7 +2104,8 @@
if test "$PBX_OPENSSL" = "1";
then
- AST_CHECK_OSPTK([4], [0], [0])
+ AST_CHECK_OSPTK([4], [0], [0])
+ AST_EXT_LIB_CHECK([OPENSSL_SRTP], [ssl], [SSL_CTX_set_tlsext_use_srtp], [openssl/ssl.h], [-lcrypto])
fi
AST_EXT_LIB_CHECK([SRTP], [srtp], [srtp_init], [srtp/srtp.h])
Modified: trunk/include/asterisk/autoconfig.h.in
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/autoconfig.h.in?view=diff&rev=373234&r1=373233&r2=373234
==============================================================================
--- trunk/include/asterisk/autoconfig.h.in (original)
+++ trunk/include/asterisk/autoconfig.h.in Thu Sep 20 13:27:28 2012
@@ -526,6 +526,9 @@
/* Define to 1 if you have the OpenSSL Secure Sockets Layer library. */
#undef HAVE_OPENSSL
+
+/* Define to 1 if CRYPTO has the OpenSSL SRTP Extension Support feature. */
+#undef HAVE_OPENSSL_SRTP
/* Define this to indicate the ${OSPTK_DESCRIP} library */
#undef HAVE_OSPTK
Modified: trunk/include/asterisk/rtp_engine.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/rtp_engine.h?view=diff&rev=373234&r1=373233&r2=373234
==============================================================================
--- trunk/include/asterisk/rtp_engine.h (original)
+++ trunk/include/asterisk/rtp_engine.h Thu Sep 20 13:27:28 2012
@@ -351,6 +351,61 @@
struct ao2_container *(*get_local_candidates)(struct ast_rtp_instance *instance);
/*! Callback for telling the ICE support that it is talking to an ice-lite implementation */
void (*ice_lite)(struct ast_rtp_instance *instance);
+};
+
+/*! \brief DTLS setup types */
+enum ast_rtp_dtls_setup {
+ AST_RTP_DTLS_SETUP_ACTIVE, /*!< Endpoint is willing to inititate connections */
+ AST_RTP_DTLS_SETUP_PASSIVE, /*!< Endpoint is willing to accept connections */
+ AST_RTP_DTLS_SETUP_ACTPASS, /*!< Endpoint is willing to both accept and initiate connections */
+ AST_RTP_DTLS_SETUP_HOLDCONN, /*!< Endpoint does not want the connection to be established right now */
+};
+
+/*! \brief DTLS connection states */
+enum ast_rtp_dtls_connection {
+ AST_RTP_DTLS_CONNECTION_NEW, /*!< Endpoint wants to use a new connection */
+ AST_RTP_DTLS_CONNECTION_EXISTING, /*!< Endpoint wishes to use existing connection */
+};
+
+/*! \brief DTLS fingerprint hashes */
+enum ast_rtp_dtls_hash {
+ AST_RTP_DTLS_HASH_SHA1, /*!< SHA-1 fingerprint hash */
+};
+
+/*! \brief DTLS configuration structure */
+struct ast_rtp_dtls_cfg {
+ unsigned int enabled:1; /*!< Whether DTLS support is enabled or not */
+ unsigned int verify:1; /*!< Whether to request and verify a client certificate when acting as server */
+ unsigned int rekey; /*!< Interval at which to renegotiate and rekey - defaults to 0 (off) */
+ enum ast_rtp_dtls_setup default_setup; /*!< Default setup type to use for outgoing */
+ enum ast_srtp_suite suite; /*!< Crypto suite in use */
+ char *certfile; /*!< Certificate file */
+ char *pvtfile; /*!< Private key file */
+ char *cipher; /*!< Cipher to use */
+ char *cafile; /*!< Certificate authority file */
+ char *capath; /*!< Path to certificate authority */
+};
+
+/*! \brief Structure that represents the optional DTLS SRTP support within an RTP engine */
+struct ast_rtp_engine_dtls {
+ /*! Set the configuration of the DTLS support on the instance */
+ int (*set_configuration)(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg);
+ /*! Get if the DTLS SRTP support is active or not */
+ int (*active)(struct ast_rtp_instance *instance);
+ /*! Stop and terminate DTLS SRTP support */
+ void (*stop)(struct ast_rtp_instance *instance);
+ /*! Reset the connection and start fresh */
+ void (*reset)(struct ast_rtp_instance *instance);
+ /*! Get the current connection state */
+ enum ast_rtp_dtls_connection (*get_connection)(struct ast_rtp_instance *instance);
+ /*! Get the current setup state */
+ enum ast_rtp_dtls_setup (*get_setup)(struct ast_rtp_instance *instance);
+ /*! Set the remote setup state */
+ void (*set_setup)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup);
+ /*! Set the remote fingerprint */
+ void (*set_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint);
+ /*! Get the local fingerprint */
+ const char *(*get_fingerprint)(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash);
};
/*! Structure that represents an RTP stack (engine) */
@@ -426,6 +481,8 @@
int (*sendcng)(struct ast_rtp_instance *instance, int level);
/*! Callback to pointer for optional ICE support */
struct ast_rtp_engine_ice *ice;
+ /*! Callback to pointer for optional DTLS SRTP support */
+ struct ast_rtp_engine_dtls *dtls;
/*! Linked list information */
AST_RWLIST_ENTRY(ast_rtp_engine) entry;
};
@@ -2014,6 +2071,43 @@
*/
struct ast_rtp_engine_ice *ast_rtp_instance_get_ice(struct ast_rtp_instance *instance);
+/*!
+ * \brief Obtain a pointer to the DTLS support present on an RTP instance
+ *
+ * \param instance the RTP instance
+ *
+ * \retval DTLS support if present
+ * \retval NULL if no DTLS support available
+ */
+struct ast_rtp_engine_dtls *ast_rtp_instance_get_dtls(struct ast_rtp_instance *instance);
+
+/*!
+ * \brief Parse DTLS related configuration options
+ *
+ * \param dtls_cfg a DTLS configuration structure
+ * \param name name of the configuration option
+ * \param value value of the configuration option
+ *
+ * \retval 0 if handled
+ * \retval -1 if not handled
+ */
+int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, const char *value);
+
+/*!
+ * \brief Copy contents of a DTLS configuration structure
+ *
+ * \param src_cfg source DTLS configuration structure
+ * \param dst_cfg destination DTLS configuration structure
+ */
+void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rtp_dtls_cfg *dst_cfg);
+
+/*!
+ * \brief Free contents of a DTLS configuration structure
+ *
+ * \param dtls_cfg a DTLS configuration structure
+ */
+void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
Modified: trunk/main/rtp_engine.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/rtp_engine.c?view=diff&rev=373234&r1=373233&r2=373234
==============================================================================
--- trunk/main/rtp_engine.c (original)
+++ trunk/main/rtp_engine.c Thu Sep 20 13:27:28 2012
@@ -2058,6 +2058,74 @@
return instance->engine->ice;
}
+struct ast_rtp_engine_dtls *ast_rtp_instance_get_dtls(struct ast_rtp_instance *instance)
+{
+ return instance->engine->dtls;
+}
+
+int ast_rtp_dtls_cfg_parse(struct ast_rtp_dtls_cfg *dtls_cfg, const char *name, const char *value)
+{
+ if (!strcasecmp(name, "dtlsenable")) {
+ dtls_cfg->enabled = ast_true(value) ? 1 : 0;
+ } else if (!strcasecmp(name, "dtlsverify")) {
+ dtls_cfg->verify = ast_true(value) ? 1 : 0;
+ } else if (!strcasecmp(name, "dtlsrekey")) {
+ if (sscanf(value, "%30u", &dtls_cfg->rekey) != 1) {
+ return -1;
+ }
+ } else if (!strcasecmp(name, "dtlscertfile")) {
+ ast_free(dtls_cfg->certfile);
+ dtls_cfg->certfile = ast_strdup(value);
+ } else if (!strcasecmp(name, "dtlsprivatekey")) {
+ ast_free(dtls_cfg->pvtfile);
+ dtls_cfg->pvtfile = ast_strdup(value);
+ } else if (!strcasecmp(name, "dtlscipher")) {
+ ast_free(dtls_cfg->cipher);
+ dtls_cfg->cipher = ast_strdup(value);
+ } else if (!strcasecmp(name, "dtlscafile")) {
+ ast_free(dtls_cfg->cafile);
+ dtls_cfg->cafile = ast_strdup(value);
+ } else if (!strcasecmp(name, "dtlscapath") || !strcasecmp(name, "dtlscadir")) {
+ ast_free(dtls_cfg->capath);
+ dtls_cfg->capath = ast_strdup(value);
+ } else if (!strcasecmp(name, "dtlssetup")) {
+ if (!strcasecmp(value, "active")) {
+ dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_ACTIVE;
+ } else if (!strcasecmp(value, "passive")) {
+ dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_PASSIVE;
+ } else if (!strcasecmp(value, "actpass")) {
+ dtls_cfg->default_setup = AST_RTP_DTLS_SETUP_ACTPASS;
+ }
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rtp_dtls_cfg *dst_cfg)
+{
+ dst_cfg->enabled = src_cfg->enabled;
+ dst_cfg->verify = src_cfg->verify;
+ dst_cfg->rekey = src_cfg->rekey;
+ dst_cfg->suite = src_cfg->suite;
+ dst_cfg->certfile = ast_strdup(src_cfg->certfile);
+ dst_cfg->pvtfile = ast_strdup(src_cfg->pvtfile);
+ dst_cfg->cipher = ast_strdup(src_cfg->cipher);
+ dst_cfg->cafile = ast_strdup(src_cfg->cafile);
+ dst_cfg->capath = ast_strdup(src_cfg->capath);
+ dst_cfg->default_setup = src_cfg->default_setup;
+}
+
+void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg)
+{
+ ast_free(dtls_cfg->certfile);
+ ast_free(dtls_cfg->pvtfile);
+ ast_free(dtls_cfg->cipher);
+ ast_free(dtls_cfg->cafile);
+ ast_free(dtls_cfg->capath);
+}
+
[... 719 lines stripped ...]
More information about the asterisk-commits
mailing list