@@ 258,6 258,8 @@
/*! \brief Namespace for XMPP stanzas */
#define XMPP_STANZAS_NS "urn:ietf:params:xml:ns:xmpp-stanzas"
+#define DTLS_NS "urn:xmpp:jingle:apps:dtls:0"
+
/*! \brief The various transport methods supported, from highest priority to lowest priority when doing fallback */
enum jingle_transport {
JINGLE_TRANSPORT_ICE_UDP = 3, /*!< XEP-0176 */
@@ 686,7 688,7 @@ static void jingle_enable_video(struct jingle_session *session)
return;
}
- ast_rtp_instance_set_prop(session->vrtp, AST_RTP_PROPERTY_RTCP, 1);
+ ast_rtp_instance_set_prop(session->vrtp, AST_RTP_PROPERTY_RTCP, AST_RTP_INSTANCE_RTCP_MUX);
ast_rtp_instance_set_channel_id(session->vrtp, ast_channel_uniqueid(session->owner));
ast_channel_set_fd(session->owner, 2, ast_rtp_instance_fd(session->vrtp, 0));
ast_channel_set_fd(session->owner, 3, ast_rtp_instance_fd(session->vrtp, 1));
@@ 697,6 699,8 @@ static void jingle_enable_video(struct jingle_session *session)
}
}
+static struct ast_rtp_dtls_cfg dtls_cfg;
+
/*! \brief Internal helper function used to allocate Jingle session on an endpoint */
static struct jingle_session *jingle_alloc(struct jingle_endpoint *endpoint, const char *from, const char *sid)
{
@@ 755,9 759,35 @@ static struct jingle_session *jingle_alloc(struct jingle_endpoint *endpoint, con
ao2_ref(session, -1);
return NULL;
}
+
ast_rtp_instance_set_prop(session->rtp, AST_RTP_PROPERTY_RTCP, 1);
ast_rtp_instance_set_prop(session->rtp, AST_RTP_PROPERTY_DTMF, 1);
+ struct ast_rtp_engine_dtls *dtls;
+
+ if ((dtls = ast_rtp_instance_get_dtls(session->rtp))) {
+ dtls_cfg.enabled = 1;
+ dtls_cfg.default_setup = AST_RTP_DTLS_SETUP_ACTPASS;
+ dtls_cfg.hash = AST_RTP_DTLS_HASH_SHA256;
+ dtls_cfg.verify = AST_RTP_DTLS_VERIFY_FINGERPRINT;
+ dtls_cfg.suite = AST_AES_CM_128_HMAC_SHA1_80;
+ dtls_cfg.ephemeral_cert = 1;
+ dtls_cfg.certfile = ast_strdup("");
+ dtls_cfg.pvtfile = ast_strdup("");
+ dtls_cfg.cipher = ast_strdup("");
+ dtls_cfg.cafile = ast_strdup("");
+ dtls_cfg.capath = ast_strdup("");
+
+ if (dtls->set_configuration(session->rtp, &dtls_cfg)) {
+ ast_log(LOG_ERROR, "Attempted to set an invalid DTLS-SRTP configuration on RTP instance '%p'\n",
+ session->rtp);
+ }
+ }
+ else {
+ ast_log(LOG_ERROR, "No DTLS-SRTP support present on engine for RTP instance '%p', was it compiled with support for it?\n",
+ session->rtp);
+ }
+
session->maxicecandidates = endpoint->maxicecandidates;
session->maxpayloads = endpoint->maxpayloads;
@@ 914,6 944,49 @@ end:
iks_delete(response);
}
+static void jingle_add_fingerprint(struct ast_rtp_instance *rtp, iks *transport) {
+ struct ast_rtp_engine_dtls *dtls;
+ if ((dtls = ast_rtp_instance_get_dtls(rtp)) || dtls->active(rtp)) {
+ // Add the DTLS fingerprint in there as well
+ iks *fingerprint_node = NULL;
+ const char *fingerprint;
+ if (!(fingerprint_node = iks_new("fingerprint"))) {
+ ast_log(LOG_ERROR, "Failed to allocate stanzas for DTLS");
+ return;
+ }
+ iks_insert_attrib(fingerprint_node, "xmlns", DTLS_NS);
+ switch (dtls->get_setup(rtp)) {
+ case AST_RTP_DTLS_SETUP_ACTIVE:
+ iks_insert_attrib(fingerprint_node, "setup", "active");
+ break;
+ case AST_RTP_DTLS_SETUP_PASSIVE:
+ iks_insert_attrib(fingerprint_node, "setup", "passive");
+ break;
+ case AST_RTP_DTLS_SETUP_ACTPASS:
+ iks_insert_attrib(fingerprint_node, "setup", "actpass");
+ break;
+ case AST_RTP_DTLS_SETUP_HOLDCONN:
+ iks_insert_attrib(fingerprint_node, "setup", "holdconn");
+ break;
+ default:
+ break;
+ }
+ switch (dtls->get_fingerprint_hash(rtp)) {
+ case AST_RTP_DTLS_HASH_SHA1:
+ iks_insert_attrib(fingerprint_node, "hash", "sha-1");
+ break;
+ case AST_RTP_DTLS_HASH_SHA256:
+ iks_insert_attrib(fingerprint_node, "hash", "sha-256");
+ break;
+ default:
+ break;
+ }
+ fingerprint = dtls->get_fingerprint(rtp);
+ iks_insert_cdata(fingerprint_node, fingerprint, strlen(fingerprint));
+ iks_insert_node(transport, fingerprint_node);
+ }
+}
+
/*! \brief Internal helper function which adds ICE-UDP candidates to a transport node */
static int jingle_add_ice_udp_candidates_to_transport(struct ast_rtp_instance *rtp, iks *transport, iks **candidates, unsigned int maximum)
{
@@ 929,6 1002,7 @@ static int jingle_add_ice_udp_candidates_to_transport(struct ast_rtp_instance *r
}
iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
+ jingle_add_fingerprint(rtp, transport);
iks_insert_attrib(transport, "pwd", ice->get_password(rtp));
iks_insert_attrib(transport, "ufrag", ice->get_ufrag(rtp));
@@ 967,7 1041,9 @@ static int jingle_add_ice_udp_candidates_to_transport(struct ast_rtp_instance *r
}
iks_insert_node(transport, local_candidate);
- candidates[i++] = local_candidate;
+ if (candidates != NULL) {
+ candidates[i++] = local_candidate;
+ }
}
ao2_iterator_destroy(&it);
@@ 1208,7 1284,7 @@ static void jingle_queue_hangup_with_cause(struct jingle_session *session, int c
}
/*! \brief Internal function which sends a transport-info message */
-static void jingle_send_transport_info(struct jingle_session *session, const char *from)
+static void jingle_send_transport_info(struct jingle_session *session, const char *from, int actually_initiate)
{
iks *iq, *jingle = NULL, *audio = NULL, *audio_transport = NULL, *video = NULL, *video_transport = NULL;
iks *audio_candidates[session->maxicecandidates], *video_candidates[session->maxicecandidates];
@@ 1237,7 1313,7 @@ static void jingle_send_transport_info(struct jingle_session *session, const cha
iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : from);
} else {
- iks_insert_attrib(jingle, "action", "transport-info");
+ iks_insert_attrib(jingle, "action", actually_initiate == 1 ? "session-initiate" : "transport-info");
iks_insert_attrib(jingle, "sid", session->sid);
iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
}
@@ 1384,6 1460,10 @@ static int jingle_add_payloads_to_description(struct jingle_session *session, st
payloads[i++] = payload;
}
}
+ iks *rtcp_mux = iks_new("rtcp-mux");
+ if (rtcp_mux) {
+ iks_insert_node(description, rtcp_mux);
+ }
return res;
}
@@ 1416,6 1496,8 @@ static int jingle_add_content(struct jingle_session *session, iks *jingle, iks *
if (!(res = jingle_add_payloads_to_description(session, rtp, description, payloads, type))) {
if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
+ jingle_add_ice_udp_candidates_to_transport(rtp, transport, NULL, session->maxicecandidates);
+ jingle_add_fingerprint(rtp, transport);
iks_insert_node(content, transport);
} else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
iks_insert_attrib(transport, "xmlns", GOOGLE_TRANSPORT_NS);
@@ 1543,7 1625,7 @@ static int jingle_outgoing_hook(void *data, ikspak *pak)
}
ao2_unlock(session);
- jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"));
+ jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"), 0);
goto end;
}
@@ 2084,8 2166,19 @@ static int jingle_interpret_description(struct jingle_session *session, iks *des
return -1;
}
+ struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(session->rtp);
/* Iterate the codecs updating the relevant RTP instance as we go */
for (codec = iks_child(description); codec; codec = iks_next(codec)) {
+ if (strcasecmp(iks_name(codec), "rtcp-mux") == 0) {
+ if (ice) {
+ ast_log(LOG_NOTICE, "Enabling RTCP MUX for session '%s'\n", session->sid);
+ ice->change_components(session->rtp, 1);
+ }
+ else {
+ ast_log(LOG_ERROR, "Could not enable RTCP MUX for session '%s' as ICE not available\n", session->sid);
+ }
+ continue;
+ }
char *id = iks_find_attrib(codec, "id"), *name = iks_find_attrib(codec, "name");
char *clockrate = iks_find_attrib(codec, "clockrate");
int rtp_id, rtp_clockrate;
@@ 2131,10 2224,53 @@ static int jingle_interpret_ice_udp_transport(struct jingle_session *session, ik
if (!ast_strlen_zero(ufrag) && !ast_strlen_zero(pwd)) {
ice->set_authentication(rtp, ufrag, pwd);
}
+ iks *fingerprint_node;
+ if ((fingerprint_node = iks_find_with_attrib(transport, "transport", "xmlns", DTLS_NS))) {
+ char *dtls_hash, *dtls_fingerprint, *dtls_setup;
+ dtls_hash = iks_find_attrib(fingerprint_node, "hash");
+ dtls_fingerprint = iks_cdata(fingerprint_node);
+ dtls_setup = iks_find_attrib(fingerprint_node, "setup");
+ if (!ast_strlen_zero(dtls_hash) && !ast_strlen_zero(dtls_fingerprint) && !ast_strlen_zero(dtls_setup)) {
+ struct ast_rtp_engine_dtls *dtls;
+ if ((dtls = ast_rtp_instance_get_dtls(rtp))) {
+ ast_log(LOG_NOTICE, "Received DTLS information on session '%s' (hash %s): %s\n", session->sid, dtls_hash, dtls_fingerprint);
+ if (!strcasecmp(dtls_hash, "sha-1")) {
+ dtls->set_fingerprint(rtp, AST_RTP_DTLS_HASH_SHA1, dtls_fingerprint);
+ } else if (!strcasecmp(dtls_hash, "sha-256")) {
+ dtls->set_fingerprint(rtp, AST_RTP_DTLS_HASH_SHA256, dtls_fingerprint);
+ } else {
+ ast_log(LOG_WARNING, "Unsupported fingerprint hash type '%s' received for session '%s'\n",
+ dtls_hash, session->sid);
+ }
+ if (!strcasecmp(dtls_setup, "active")) {
+ dtls->set_setup(rtp, AST_RTP_DTLS_SETUP_ACTIVE);
+ } else if (!strcasecmp(dtls_setup, "passive")) {
+ dtls->set_setup(rtp, AST_RTP_DTLS_SETUP_PASSIVE);
+ } else if (!strcasecmp(dtls_setup, "actpass")) {
+ dtls->set_setup(rtp, AST_RTP_DTLS_SETUP_ACTPASS);
+ } else if (!strcasecmp(dtls_setup, "holdconn")) {
+ dtls->set_setup(rtp, AST_RTP_DTLS_SETUP_HOLDCONN);
+ } else {
+ ast_log(LOG_WARNING, "Unsupported setup attribute dtls_setup '%s' received for session '%s'\n",
+ dtls_setup, session->sid);
+ }
+ }
+ else {
+ ast_log(LOG_WARNING, "Received DTLS information on session '%s', but it wasn't enabled\n", session->sid);
+ }
+ }
+ else {
+ ast_log(LOG_WARNING, "Invalid DTLS information on session '%s'\n", session->sid);
+ }
+ }
for (candidate = iks_child(transport); candidate; candidate = iks_next(candidate)) {
+ char *name = iks_name(candidate);
+ if (strcasecmp(name, "candidate") != 0) {
+ continue;
+ }
char *component = iks_find_attrib(candidate, "component"), *foundation = iks_find_attrib(candidate, "foundation");
- char *generation = iks_find_attrib(candidate, "generation"), *id = iks_find_attrib(candidate, "id");
+ char *generation = iks_find_attrib(candidate, "generation");
char *ip = iks_find_attrib(candidate, "ip"), *port = iks_find_attrib(candidate, "port");
char *priority = iks_find_attrib(candidate, "priority"), *protocol = iks_find_attrib(candidate, "protocol");
char *type = iks_find_attrib(candidate, "type");
@@ 2143,7 2279,7 @@ static int jingle_interpret_ice_udp_transport(struct jingle_session *session, ik
struct ast_sockaddr remote_address = { { 0, } };
/* If this candidate is incomplete skip it */
- if (ast_strlen_zero(component) || ast_strlen_zero(foundation) || ast_strlen_zero(generation) || ast_strlen_zero(id) ||
+ if (ast_strlen_zero(component) || ast_strlen_zero(foundation) || ast_strlen_zero(generation) ||
ast_strlen_zero(ip) || ast_strlen_zero(port) || ast_strlen_zero(priority) ||
ast_strlen_zero(protocol) || ast_strlen_zero(type)) {
jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
@@ 2450,7 2586,7 @@ static void jingle_action_session_initiate(struct jingle_endpoint *endpoint, str
/* Only send a transport-info message if we successfully interpreted the available content */
if (!jingle_interpret_content(session, pak)) {
- jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"));
+ jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"), 0);
}
break;
}
@@ 2427,11 2427,11 @@ done:
static int xmpp_client_service_discovery_get_hook(void *data, ikspak *pak)
{
struct ast_xmpp_client *client = data;
- iks *iq, *disco = NULL, *ident = NULL, *google = NULL, *jingle = NULL, *ice = NULL, *rtp = NULL, *audio = NULL, *video = NULL, *query = NULL;
+ iks *iq, *disco = NULL, *ident = NULL, *google = NULL, *jingle = NULL, *ice = NULL, *rtp = NULL, *audio = NULL, *video = NULL, *query = NULL, *dtls = NULL;
if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(ident = iks_new("identity")) || !(disco = iks_new("feature")) ||
!(google = iks_new("feature")) || !(jingle = iks_new("feature")) || !(ice = iks_new("feature")) || !(rtp = iks_new("feature")) ||
- !(audio = iks_new("feature")) || !(video = iks_new("feature"))) {
+ !(audio = iks_new("feature")) || !(video = iks_new("feature")) || !(dtls = iks_new("feature"))) {
ast_log(LOG_ERROR, "Could not allocate memory for responding to service discovery request from '%s' on client '%s'\n",
pak->from->full, client->name);
goto end;
@@ 2455,6 2455,7 @@ static int xmpp_client_service_discovery_get_hook(void *data, ikspak *pak)
iks_insert_attrib(jingle, "var", "urn:xmpp:jingle:1");
iks_insert_attrib(ice, "var", "urn:xmpp:jingle:transports:ice-udp:1");
iks_insert_attrib(rtp, "var", "urn:xmpp:jingle:apps:rtp:1");
+ iks_insert_attrib(dtls, "var", "urn:xmpp:jingle:apps:dtls:0");
iks_insert_attrib(audio, "var", "urn:xmpp:jingle:apps:rtp:audio");
iks_insert_attrib(video, "var", "urn:xmpp:jingle:apps:rtp:video");
iks_insert_node(iq, query);
@@ 2464,6 2465,7 @@ static int xmpp_client_service_discovery_get_hook(void *data, ikspak *pak)
iks_insert_node(query, jingle);
iks_insert_node(query, ice);
iks_insert_node(query, rtp);
+ iks_insert_node(query, dtls);
iks_insert_node(query, audio);
iks_insert_node(query, video);
ast_xmpp_client_send(client, iq);