summaryrefslogtreecommitdiff
path: root/res/res_sip_sdp_rtp.c
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2013-06-22 14:03:22 +0000
committerJoshua Colp <jcolp@digium.com>2013-06-22 14:03:22 +0000
commit77002bc377f19ea11e60732c486b6ef371688773 (patch)
treec19fd245c519c6d7905403849a7af9c7e4a4be3e /res/res_sip_sdp_rtp.c
parentea03516cb5426915d183526335d3a7d662ea29dc (diff)
Merge in current pimp_my_sip work, including:
1. Security events 2. Websocket support 3. Diversion header + redirecting support 4. An anonymous endpoint identifier 5. Inbound extension state subscription support 6. PIDF notify generation 7. One touch recording support (special thanks Sean Bright!) 8. Blind and attended transfer support 9. Automatic inbound registration expiration 10. SRTP support 11. Media offer control dialplan function 12. Connected line support 13. SendText() support 14. Qualify support 15. Inband DTMF detection 16. Call and pickup groups 17. Messaging support Thanks everyone! Side note: I'm reminded of the song "How Far We've Come" by Matchbox Twenty. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@392565 65c4cc65-6c06-0410-ace0-fbb531ad65f3
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;
}