summaryrefslogtreecommitdiff
path: root/res/res_sip_sdp_rtp.c
diff options
context:
space:
mode:
Diffstat (limited to 'res/res_sip_sdp_rtp.c')
-rw-r--r--res/res_sip_sdp_rtp.c188
1 files changed, 173 insertions, 15 deletions
diff --git a/res/res_sip_sdp_rtp.c b/res/res_sip_sdp_rtp.c
index b0c8ae31c..bc150ed4a 100644
--- a/res/res_sip_sdp_rtp.c
+++ b/res/res_sip_sdp_rtp.c
@@ -47,6 +47,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/causes.h"
#include "asterisk/sched.h"
#include "asterisk/acl.h"
+#include "asterisk/sdp_srtp.h"
#include "asterisk/res_sip.h"
#include "asterisk/res_sip_session.h"
@@ -117,6 +118,10 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session_media->rtp),
session_media->rtp, &session->endpoint->prefs);
+ if (session->endpoint->dtmf == AST_SIP_DTMF_INBAND) {
+ ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);
+ }
+
if (!session->endpoint->ice_support && (ice = ast_rtp_instance_get_ice(session_media->rtp))) {
ice->stop(session_media->rtp);
}
@@ -289,6 +294,18 @@ static pjmedia_sdp_attr* generate_fmtp_attr(pj_pool_t *pool, struct ast_format *
return attr;
}
+static int codec_pref_has_type(struct ast_codec_pref *prefs, enum ast_format_type media_type)
+{
+ int i;
+ struct ast_format fmt;
+ for (i = 0; ast_codec_pref_index(prefs, i, &fmt); ++i) {
+ if (AST_FORMAT_GET_TYPE(fmt.id) == media_type) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
/*! \brief Function which adds ICE attributes to a media stream */
static void add_ice_to_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media)
{
@@ -460,6 +477,101 @@ static void apply_packetization(struct ast_sip_session *session, struct ast_sip_
session_media->rtp, pref);
}
+/*! \brief figure out media transport encryption type from the media transport string */
+static enum ast_sip_session_media_encryption get_media_encryption_type(pj_str_t transport)
+{
+ RAII_VAR(char *, transport_str, ast_strndup(transport.ptr, transport.slen), ast_free);
+ if (strstr(transport_str, "UDP/TLS")) {
+ return AST_SIP_MEDIA_ENCRYPT_DTLS;
+ } else if (strstr(transport_str, "SAVP")) {
+ return AST_SIP_MEDIA_ENCRYPT_SDES;
+ } else {
+ return AST_SIP_MEDIA_ENCRYPT_NONE;
+ }
+}
+
+/*!
+ * \brief Checks whether the encryption offered in SDP is compatible with the endpoint's configuration
+ * \internal
+ *
+ * \param endpoint_encryption Media encryption configured for the endpoint
+ * \param stream pjmedia_sdp_media stream description
+ *
+ * \retval AST_SIP_MEDIA_TRANSPORT_INVALID on encryption mismatch
+ * \retval The encryption requested in the SDP
+ */
+static enum ast_sip_session_media_encryption check_endpoint_media_transport(
+ struct ast_sip_endpoint *endpoint,
+ const struct pjmedia_sdp_media *stream)
+{
+ enum ast_sip_session_media_encryption incoming_encryption;
+
+ if (endpoint->use_avpf) {
+ char transport_end = stream->desc.transport.ptr[stream->desc.transport.slen - 1];
+ if (transport_end != 'F') {
+ return AST_SIP_MEDIA_TRANSPORT_INVALID;
+ }
+ }
+
+ incoming_encryption = get_media_encryption_type(stream->desc.transport);
+ if (incoming_encryption == AST_SIP_MEDIA_ENCRYPT_DTLS) {
+ /* DTLS not yet supported */
+ return AST_SIP_MEDIA_TRANSPORT_INVALID;
+ }
+
+ if (incoming_encryption == endpoint->media_encryption) {
+ return incoming_encryption;
+ }
+
+ return AST_SIP_MEDIA_TRANSPORT_INVALID;
+}
+
+static int setup_sdes_srtp(struct ast_sip_session_media *session_media,
+ const struct pjmedia_sdp_media *stream)
+{
+ int i;
+
+ for (i = 0; i < stream->attr_count; i++) {
+ pjmedia_sdp_attr *attr;
+ RAII_VAR(char *, crypto_str, NULL, ast_free);
+
+ /* check the stream for the required crypto attribute */
+ attr = stream->attr[i];
+ if (pj_strcmp2(&attr->name, "crypto")) {
+ continue;
+ }
+
+ crypto_str = ast_strndup(attr->value.ptr, attr->value.slen);
+ if (!crypto_str) {
+ return -1;
+ }
+
+ if (!session_media->srtp) {
+ session_media->srtp = ast_sdp_srtp_alloc();
+ if (!session_media->srtp) {
+ return -1;
+ }
+ }
+
+ if (!session_media->srtp->crypto) {
+ session_media->srtp->crypto = ast_sdp_crypto_alloc();
+ if (!session_media->srtp->crypto) {
+ return -1;
+ }
+ }
+
+ if (!ast_sdp_crypto_process(session_media->rtp, session_media->srtp, crypto_str)) {
+ /* found a valid crypto attribute */
+ return 0;
+ }
+
+ ast_debug(1, "Ignoring crypto offer with unsupported parameters: %s\n", crypto_str);
+ }
+
+ /* no usable crypto attributes found */
+ return -1;
+}
+
/*! \brief Function which negotiates an incoming media stream */
static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
@@ -467,12 +579,19 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
char host[NI_MAXHOST];
RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
enum ast_format_type media_type = stream_to_media_type(session_media->stream_type);
+ enum ast_sip_session_media_encryption incoming_encryption;
/* If no type formats have been configured reject this stream */
if (!ast_format_cap_has_type(session->endpoint->codecs, media_type)) {
return 0;
}
+ /* Ensure incoming transport is compatible with the endpoint's configuration */
+ incoming_encryption = check_endpoint_media_transport(session->endpoint, stream);
+ if (incoming_encryption == AST_SIP_MEDIA_TRANSPORT_INVALID) {
+ return -1;
+ }
+
ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host));
/* Ensure that the address provided is valid */
@@ -486,9 +605,42 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
return -1;
}
+ if (incoming_encryption == AST_SIP_MEDIA_ENCRYPT_SDES
+ && setup_sdes_srtp(session_media, stream)) {
+ return -1;
+ }
+
return set_caps(session, session_media, stream);
}
+static int add_crypto_to_stream(struct ast_sip_session *session,
+ struct ast_sip_session_media *session_media,
+ pj_pool_t *pool, pjmedia_sdp_media *media)
+{
+ pj_str_t stmp;
+ pjmedia_sdp_attr *attr;
+ const char *crypto_attribute;
+
+ if (!session_media->srtp && session->endpoint->media_encryption != AST_SIP_MEDIA_ENCRYPT_NONE) {
+ session_media->srtp = ast_sdp_srtp_alloc();
+ if (!session_media->srtp) {
+ return -1;
+ }
+ }
+
+ crypto_attribute = ast_sdp_srtp_get_attrib(session_media->srtp,
+ 0 /* DTLS can not be enabled for res_sip */,
+ 0 /* don't prefer 32byte tag length */);
+ if (!crypto_attribute) {
+ /* No crypto attribute to add */
+ return -1;
+ }
+
+ attr = pjmedia_sdp_attr_create(pool, "crypto", pj_cstr(&stmp, crypto_attribute));
+ media->attr[media->attr_count++] = attr;
+ return 0;
+}
+
/*! \brief Function which creates an outgoing stream */
static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
struct pjmedia_sdp_session *sdp)
@@ -497,7 +649,6 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
static const pj_str_t STR_IN = { "IN", 2 };
static const pj_str_t STR_IP4 = { "IP4", 3};
static const pj_str_t STR_IP6 = { "IP6", 3};
- static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 };
static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
pjmedia_sdp_media *media;
char hostip[PJ_INET6_ADDRSTRLEN+2];
@@ -508,14 +659,19 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
int index = 0, min_packet_size = 0, noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733) ? AST_RTP_DTMF : 0;
int rtp_code;
struct ast_format format;
- struct ast_format compat_format;
RAII_VAR(struct ast_format_cap *, caps, NULL, ast_format_cap_destroy);
enum ast_format_type media_type = stream_to_media_type(session_media->stream_type);
+ int crypto_res;
int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
!ast_format_cap_is_empty(session->direct_media_cap);
- if (!ast_format_cap_has_type(session->endpoint->codecs, media_type)) {
+ int use_override_prefs = session->override_prefs.formats[0].id;
+ struct ast_codec_pref *prefs = use_override_prefs ?
+ &session->override_prefs : &session->endpoint->prefs;
+
+ if ((use_override_prefs && !codec_pref_has_type(&session->override_prefs, media_type)) ||
+ (!use_override_prefs && !ast_format_cap_has_type(session->endpoint->codecs, media_type))) {
/* If no type formats are configured don't add a stream */
return 0;
} else if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->rtp_ipv6)) {
@@ -527,9 +683,11 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
return -1;
}
- /* TODO: This should eventually support SRTP */
+ crypto_res = add_crypto_to_stream(session, session_media, pool, media);
+
media->desc.media = pj_str(session_media->stream_type);
- media->desc.transport = STR_RTP_AVP;
+ media->desc.transport = pj_str(ast_sdp_get_rtp_profile(
+ !crypto_res, session_media->rtp, session->endpoint->use_avpf));
/* Add connection level details */
if (direct_media_enabled) {
@@ -565,36 +723,36 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
} else if (ast_format_cap_is_empty(session->req_caps) || !ast_format_cap_has_joint(session->req_caps, session->endpoint->codecs)) {
ast_format_cap_copy(caps, session->endpoint->codecs);
} else {
- ast_format_cap_joint_copy(session->endpoint->codecs, session->req_caps, caps);
+ ast_format_cap_copy(caps, session->req_caps);
}
- for (index = 0; ast_codec_pref_index(&session->endpoint->prefs, index, &format); ++index) {
+ for (index = 0; ast_codec_pref_index(prefs, index, &format); ++index) {
struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session_media->rtp)->pref;
if (AST_FORMAT_GET_TYPE(format.id) != media_type) {
continue;
}
- if (!ast_format_cap_get_compatible_format(caps, &format, &compat_format)) {
+ if (!use_override_prefs && !ast_format_cap_get_compatible_format(caps, &format, &format)) {
continue;
}
- if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp), 1, &compat_format, 0)) == -1) {
+ if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp), 1, &format, 0)) == -1) {
return -1;
}
- if (!(attr = generate_rtpmap_attr(media, pool, rtp_code, 1, &compat_format, 0))) {
+ if (!(attr = generate_rtpmap_attr(media, pool, rtp_code, 1, &format, 0))) {
continue;
}
media->attr[media->attr_count++] = attr;
- if ((attr = generate_fmtp_attr(pool, &compat_format, rtp_code))) {
+ if ((attr = generate_fmtp_attr(pool, &format, rtp_code))) {
media->attr[media->attr_count++] = attr;
}
if (pref && media_type != AST_FORMAT_TYPE_VIDEO) {
- struct ast_format_list fmt = ast_codec_pref_getsize(pref, &compat_format);
+ struct ast_format_list fmt = ast_codec_pref_getsize(pref, &format);
if (fmt.cur_ms && ((fmt.cur_ms < min_packet_size) || !min_packet_size)) {
min_packet_size = fmt.cur_ms;
}
@@ -768,9 +926,9 @@ static int video_info_incoming_request(struct ast_sip_session *session, struct p
struct pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
pjsip_tx_data *tdata;
- if (pj_strcmp2(&rdata->msg_info.msg->body->content_type.type, "application") ||
- pj_strcmp2(&rdata->msg_info.msg->body->content_type.subtype, "media_control+xml")) {
-
+ if (!ast_sip_is_content_type(&rdata->msg_info.msg->body->content_type,
+ "application",
+ "media_control+xml")) {
return 0;
}