[asterisk-commits] file: branch file/chan_jingle2 r367117 - in /team/file/chan_jingle2: channels...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri May 18 18:45:16 CDT 2012
Author: file
Date: Fri May 18 18:45:04 2012
New Revision: 367117
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=367117
Log:
Incorporate changes based on review feedback.
Modified:
team/file/chan_jingle2/channels/chan_jingle2.c
team/file/chan_jingle2/configs/jingle2.conf.sample
Modified: team/file/chan_jingle2/channels/chan_jingle2.c
URL: http://svnview.digium.com/svn/asterisk/team/file/chan_jingle2/channels/chan_jingle2.c?view=diff&rev=367117&r1=367116&r2=367117
==============================================================================
--- team/file/chan_jingle2/channels/chan_jingle2.c (original)
+++ team/file/chan_jingle2/channels/chan_jingle2.c Fri May 18 18:45:04 2012
@@ -31,8 +31,8 @@
<depend>iksemel</depend>
<depend>res_jabber</depend>
<use type="external">openssl</use>
- <defaultenabled>no</defaultenabled>
- <support_level>extended</support_level>
+ <defaultenabled>yes</defaultenabled>
+ <support_level>core</support_level>
***/
#include "asterisk.h"
@@ -70,33 +70,14 @@
#include "asterisk/abstract_jb.h"
#include "asterisk/jabber.h"
-/*! \brief Default jitterbuffer configuration */
-static struct ast_jb_conf default_jbconf =
-{
- .flags = 0,
- .max_size = -1,
- .resync_threshold = -1,
- .impl = "",
- .target_extra = -1,
-};
-
-/*! \brief Global jitterbuffer configuration */
-static struct ast_jb_conf global_jbconf;
-
/*! \brief Default filename for the configuration file */
#define JINGLE_CONFIG "jingle2.conf"
-/*! \brief Maximum size for a username */
-#define MAX_SIZE_USERNAME 100
-
-/*! \brief Maximum size for a session identifier */
-#define MAX_SIZE_SID 100
-
-/*! \brief Maximum number of ICE candidates we will offer */
-#define MAX_ICE_CANDIDATES 10
-
-/*! \brief Maximum number of payloads we will offer */
-#define MAX_PAYLOADS 30
+/*! \brief Default maximum number of ICE candidates we will offer */
+#define DEFAULT_MAX_ICE_CANDIDATES 10
+
+/*! \brief Default maximum number of payloads we will offer */
+#define DEFAULT_MAX_PAYLOADS 30
/*! \brief Number of buckets for endpoints */
#define ENDPOINT_BUCKETS 37
@@ -144,9 +125,13 @@
/*! \brief Endpoint which contains configuration information and active sessions */
struct jingle_endpoint {
- char name[MAX_SIZE_USERNAME]; /*!< Name of the endpoint */
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(name); /*!< Name of the endpoint */
+ );
struct aji_client *connection; /*!< Connection to use for traffic */
iksrule *rule; /*!< Action matching rule */
+ unsigned int maxicecandidates; /*!< Maximum number of ICE candidates we will offer */
+ unsigned int maxpayloads; /*!< Maximum number of payloads we will offer */
struct ast_codec_pref prefs; /*!< Codec preferences */
char context[AST_MAX_CONTEXT]; /*!< Context to place incoming calls into */
char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */
@@ -159,22 +144,27 @@
enum jingle_transport transport; /*!< Default transport to use on outgoing sessions */
struct ao2_container *sessions; /*!< Active sessions to or from the endpoint */
unsigned int destroy:1; /*!< Bit to indicate that the endpoint should be destroyed */
+ unsigned int destroyed:1; /*!< Bit to indicate this endpoint is effectively destroyed */
};
/*! \brief Session which contains information about an active session */
struct jingle_session {
- char sid[MAX_SIZE_SID]; /*!< Session identifier */
- struct jingle_endpoint *endpoint; /*!< Endpoint we belong to */
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(sid); /*!< Session identifier */
+ AST_STRING_FIELD(audio_name); /*!< Name of the audio content */
+ AST_STRING_FIELD(video_name); /*!< Name of the video content */
+ );
+ struct jingle_endpoint *endpoint; /*!< Endpoint we are associated with */
struct aji_client *connection; /*!< Connection to use for traffic */
enum jingle_transport transport; /*!< Transport type to use for this session */
+ unsigned int maxicecandidates; /*!< Maximum number of ICE candidates we will offer */
+ unsigned int maxpayloads; /*!< Maximum number of payloads we will offer */
char remote_original[AJI_MAX_JIDLEN]; /*!< Identifier of the original remote party (remote may have changed due to redirect) */
char remote[AJI_MAX_JIDLEN]; /*!< Identifier of the remote party */
iksrule *rule; /*!< Session matching rule */
struct ast_codec_pref prefs; /*!< Codec preferences */
struct ast_channel *owner; /*!< Master Channel */
- char audio_content_name[100]; /*!< Name attribute for audio content */
struct ast_rtp_instance *rtp; /*!< RTP audio session */
- char video_content_name[100]; /*!< Name attribute for video content */
struct ast_rtp_instance *vrtp; /*!< RTP video session */
struct ast_format_cap *cap; /*!< Local codec capabilities */
struct ast_format_cap *jointcap; /*!< Joint codec capabilities */
@@ -289,22 +279,26 @@
ast_format_cap_destroy(endpoint->cap);
ao2_ref(endpoint->sessions, -1);
+
+ ast_string_field_free_memory(endpoint);
}
/*! \brief Hashing function for Jingle endpoints */
static int jingle_endpoint_hash(const void *obj, const int flags)
{
const struct jingle_endpoint *endpoint = obj;
-
- return ast_str_hash(endpoint->name);
+ const char *name = obj;
+
+ return ast_str_hash(flags & OBJ_KEY ? name : endpoint->name);
}
/*! \brief Comparator function for Jingle endpoints */
static int jingle_endpoint_cmp(void *obj, void *arg, int flags)
{
struct jingle_endpoint *endpoint1 = obj, *endpoint2 = arg;
-
- return !strcmp(endpoint1->name, endpoint2->name) ? CMP_MATCH | CMP_STOP : 0;
+ const char *name = arg;
+
+ return !strcmp(endpoint1->name, flags & OBJ_KEY ? name : endpoint2->name) ? CMP_MATCH | CMP_STOP : 0;
}
/*! \brief Destructor for Jingle sessions */
@@ -331,22 +325,26 @@
ast_format_cap_destroy(session->cap);
ast_format_cap_destroy(session->jointcap);
ast_format_cap_destroy(session->peercap);
+
+ ast_string_field_free_memory(session);
}
/*! \brief Hashing function for Jingle sessions */
static int jingle_session_hash(const void *obj, const int flags)
{
const struct jingle_session *session = obj;
-
- return ast_str_hash(session->sid);
+ const char *sid = obj;
+
+ return ast_str_hash(flags & OBJ_KEY ? sid : session->sid);
}
/*! \brief Comparator function for Jingle sessions */
static int jingle_session_cmp(void *obj, void *arg, int flags)
{
struct jingle_session *session1 = obj, *session2 = arg;
-
- return !strcmp(session1->sid, session2->sid) ? CMP_MATCH | CMP_STOP : 0;
+ const char *sid = arg;
+
+ return !strcmp(session1->sid, flags & OBJ_KEY ? sid : session2->sid) ? CMP_MATCH | CMP_STOP : 0;
}
/*! \brief Function called by RTP engine to get local RTP peer */
@@ -361,10 +359,6 @@
ao2_ref(session->rtp, +1);
*instance = session->rtp;
-
- if (ast_test_flag(&global_jbconf, AST_JB_FORCED)) {
- res = AST_RTP_GLUE_RESULT_FORBID;
- }
return res;
}
@@ -431,21 +425,33 @@
return NULL;
}
+ if (ast_string_field_init(session, 512)) {
+ ao2_ref(session, -1);
+ return NULL;
+ }
+
if (!ast_strlen_zero(from)) {
ast_copy_string(session->remote_original, from, sizeof(session->remote_original));
ast_copy_string(session->remote, from, sizeof(session->remote));
}
if (ast_strlen_zero(sid)) {
- snprintf(session->sid, sizeof(session->sid), "%08lx%08lx", ast_random(), ast_random());
+ ast_string_field_build(session, sid, "%08lx%08lx", ast_random(), ast_random());
session->outgoing = 1;
- ast_copy_string(session->audio_content_name, "audio", sizeof(session->audio_content_name));
- ast_copy_string(session->video_content_name, "video", sizeof(session->video_content_name));
+ ast_string_field_set(session, audio_name, "audio");
+ ast_string_field_set(session, video_name, "video");
} else {
- ast_copy_string(session->sid, sid, sizeof(session->sid));
+ ast_string_field_set(session, sid, sid);
}
ao2_lock(endpoint);
+
+ /* If the endpoint is effectively destroyed it is not acceptable to create a session on it */
+ if (endpoint->destroyed) {
+ ao2_unlock(endpoint);
+ ao2_ref(session, -1);
+ return NULL;
+ }
ao2_ref(endpoint, +1);
session->endpoint = endpoint;
@@ -479,6 +485,9 @@
memcpy(&session->prefs, &endpoint->prefs, sizeof(session->prefs));
+ session->maxicecandidates = endpoint->maxicecandidates;
+ session->maxpayloads = endpoint->maxpayloads;
+
ao2_unlock(endpoint);
return session;
@@ -564,6 +573,7 @@
iks *response;
if (!(response = iks_new("iq"))) {
+ ast_log(LOG_ERROR, "Unable to allocate an IKS response stanza\n");
return;
}
@@ -585,6 +595,7 @@
if (!(response = iks_new("iq")) ||
!(error = iks_new("error")) ||
!(reason = iks_new(reasonstr))) {
+ ast_log(LOG_ERROR, "Unable to allocate IKS error response stanzas\n");
goto end;
}
@@ -611,7 +622,7 @@
}
/*! \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)
+static int jingle_add_ice_udp_candidates_to_transport(struct ast_rtp_instance *rtp, iks *transport, iks **candidates, unsigned int maximum)
{
struct ast_rtp_engine_ice *ice;
struct ao2_container *local_candidates;
@@ -620,6 +631,7 @@
int i = 0, res = 0;
if (!(ice = ast_rtp_instance_get_ice(rtp)) || !(local_candidates = ice->get_local_candidates(rtp))) {
+ ast_log(LOG_ERROR, "Unable to add ICE-UDP candidates as ICE support not available or no candidates available\n");
return -1;
}
@@ -629,12 +641,13 @@
it = ao2_iterator_init(local_candidates, 0);
- while ((candidate = ao2_iterator_next(&it)) && (i < MAX_ICE_CANDIDATES)) {
+ while ((candidate = ao2_iterator_next(&it)) && (i < maximum)) {
iks *local_candidate;
char tmp[30];
if (!(local_candidate = iks_new("candidate"))) {
res = -1;
+ ast_log(LOG_ERROR, "Unable to allocate IKS candidate stanza for ICE-UDP transport\n");
goto end;
}
@@ -671,7 +684,7 @@
}
/*! \brief Internal helper function which adds Google candidates to a transport node */
-static int jingle_add_google_candidates_to_transport(struct ast_rtp_instance *rtp, iks *transport, iks **candidates, unsigned int video, enum jingle_transport transport_type)
+static int jingle_add_google_candidates_to_transport(struct ast_rtp_instance *rtp, iks *transport, iks **candidates, unsigned int video, enum jingle_transport transport_type, unsigned int maximum)
{
struct ast_rtp_engine_ice *ice;
struct ao2_container *local_candidates;
@@ -680,6 +693,7 @@
int i = 0, res = 0;
if (!(ice = ast_rtp_instance_get_ice(rtp)) || !(local_candidates = ice->get_local_candidates(rtp))) {
+ ast_log(LOG_ERROR, "Unable to add Google ICE candidates as ICE support not available or no candidates available\n");
return -1;
}
@@ -689,13 +703,14 @@
it = ao2_iterator_init(local_candidates, 0);
- while ((candidate = ao2_iterator_next(&it)) && (i < MAX_ICE_CANDIDATES)) {
+ while ((candidate = ao2_iterator_next(&it)) && (i < maximum)) {
iks *local_candidate;
/* In Google land a username is 16 bytes, explicitly */
char ufrag[17] = "";
if (!(local_candidate = iks_new("candidate"))) {
res = -1;
+ ast_log(LOG_ERROR, "Unable to allocate IKS candidate stanza for Google ICE transport\n");
goto end;
}
@@ -751,6 +766,7 @@
if (!(iq = iks_new("iq")) || !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle")) ||
!(reason = iks_new("reason")) || !(text = iks_new(reasontext))) {
+ ast_log(LOG_ERROR, "Failed to allocate stanzas for session-terminate message on session '%s'\n", session->sid);
goto end;
}
@@ -794,6 +810,7 @@
}
if (!(iq = iks_new("iq")) || !(jingle = iks_new("jingle")) || !(text = iks_new(info))) {
+ ast_log(LOG_ERROR, "Failed to allocate stanzas for session-info message on session '%s'\n", session->sid);
goto end;
}
@@ -816,28 +833,82 @@
iks_delete(iq);
}
-/*! \brief Helper function which gets the session lock and ultimately channel lock if present */
-static void jingle_get_owner_lock(struct jingle_session *session)
-{
- while (session->owner && ast_channel_trylock(session->owner)) {
- ao2_unlock(session);
- usleep(1);
- ao2_lock(session);
- }
+/*! \internal
+ *
+ * \brief Locks both pvt and pvt owner if owner is present.
+ *
+ * \note This function gives a ref to pvt->owner if it is present and locked.
+ * This reference must be decremented after pvt->owner is unlocked.
+ *
+ * \note This function will never give you up,
+ * \note This function will never let you down.
+ * \note This function will run around and desert you.
+ *
+ * \pre pvt is not locked
+ * \post pvt is locked
+ * \post pvt->owner is locked and its reference count is increased (if pvt->owner is not NULL)
+ *
+ * \returns a pointer to the locked and reffed pvt->owner channel if it exists.
+ */
+static struct ast_channel *jingle_session_lock_full(struct jingle_session *pvt)
+{
+ struct ast_channel *chan;
+
+ /* Locking is simple when it is done right. If you see a deadlock resulting
+ * in this function, it is not this function's fault, Your problem exists elsewhere.
+ * This function is perfect... seriously. */
+ for (;;) {
+ /* First, get the channel and grab a reference to it */
+ ao2_lock(pvt);
+ chan = pvt->owner;
+ if (chan) {
+ /* The channel can not go away while we hold the pvt lock.
+ * Give the channel a ref so it will not go away after we let
+ * the pvt lock go. */
+ ast_channel_ref(chan);
+ } else {
+ /* no channel, return pvt locked */
+ return NULL;
+ }
+
+ /* We had to hold the pvt lock while getting a ref to the owner channel
+ * but now we have to let this lock go in order to preserve proper
+ * locking order when grabbing the channel lock */
+ ao2_unlock(pvt);
+
+ /* Look, no deadlock avoidance, hooray! */
+ ast_channel_lock(chan);
+ ao2_lock(pvt);
+ if (pvt->owner == chan) {
+ /* done */
+ break;
+ }
+
+ /* If the owner changed while everything was unlocked, no problem,
+ * just start over and everthing will work. This is rare, do not be
+ * confused by this loop and think this it is an expensive operation.
+ * The majority of the calls to this function will never involve multiple
+ * executions of this loop. */
+ ast_channel_unlock(chan);
+ ast_channel_unref(chan);
+ ao2_unlock(pvt);
+ }
+
+ /* If owner exists, it is locked and reffed */
+ return pvt->owner;
}
/*! \brief Helper function which queues a hangup frame with cause code */
static void jingle_queue_hangup_with_cause(struct jingle_session *session, int cause)
{
- ao2_lock(session);
-
- jingle_get_owner_lock(session);
-
- if (session->owner) {
- ast_queue_hangup_with_cause(session->owner, cause);
- ast_channel_unlock(session->owner);
- }
-
+ struct ast_channel *chan;
+
+ if ((chan = jingle_session_lock_full(session))) {
+ ast_debug(3, "Hanging up channel '%s' with cause '%d'\n", ast_channel_name(chan), cause);
+ ast_queue_hangup_with_cause(chan, cause);
+ ast_channel_unlock(chan);
+ ast_channel_unref(chan);
+ }
ao2_unlock(session);
}
@@ -845,15 +916,19 @@
static void jingle_send_transport_info(struct jingle_session *session, const char *from)
{
iks *iq, *jingle = NULL, *audio = NULL, *audio_transport = NULL, *video = NULL, *video_transport = NULL;
- iks *audio_candidates[MAX_ICE_CANDIDATES] = { 0, }, *video_candidates[MAX_ICE_CANDIDATES] = { 0, };
+ iks *audio_candidates[session->maxicecandidates], *video_candidates[session->maxicecandidates];
int i, res = 0;
if (!(iq = iks_new("iq")) ||
!(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle"))) {
iks_delete(iq);
jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
+ ast_log(LOG_ERROR, "Failed to allocate stanzas for transport-info message, hanging up session '%s'\n", session->sid);
return;
}
+
+ memset(audio_candidates, 0, sizeof(audio_candidates));
+ memset(video_candidates, 0, sizeof(video_candidates));
iks_insert_attrib(iq, "from", session->connection->jid->full);
iks_insert_attrib(iq, "to", from);
@@ -876,17 +951,18 @@
if (session->rtp) {
if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
/* V1 protocol has the candidates directly in the session */
- res = jingle_add_google_candidates_to_transport(session->rtp, jingle, audio_candidates, 0, session->transport);
+ res = jingle_add_google_candidates_to_transport(session->rtp, jingle, audio_candidates, 0, session->transport, session->maxicecandidates);
} else if ((audio = iks_new("content")) && (audio_transport = iks_new("transport"))) {
iks_insert_attrib(audio, "creator", session->outgoing ? "initiator" : "responder");
- iks_insert_attrib(audio, "name", session->audio_content_name);
+ iks_insert_attrib(audio, "name", session->audio_name);
iks_insert_node(jingle, audio);
iks_insert_node(audio, audio_transport);
if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
- res = jingle_add_ice_udp_candidates_to_transport(session->rtp, audio_transport, audio_candidates);
+ res = jingle_add_ice_udp_candidates_to_transport(session->rtp, audio_transport, audio_candidates, session->maxicecandidates);
} else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
- res = jingle_add_google_candidates_to_transport(session->rtp, audio_transport, audio_candidates, 0, session->transport);
+ res = jingle_add_google_candidates_to_transport(session->rtp, audio_transport, audio_candidates, 0, session->transport,
+ session->maxicecandidates);
}
} else {
res = -1;
@@ -896,14 +972,15 @@
if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V1) && !res && session->vrtp) {
if ((video = iks_new("content")) && (video_transport = iks_new("transport"))) {
iks_insert_attrib(video, "creator", session->outgoing ? "initiator" : "responder");
- iks_insert_attrib(video, "name", session->video_content_name);
+ iks_insert_attrib(video, "name", session->video_name);
iks_insert_node(jingle, video);
iks_insert_node(video, video_transport);
if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
- res = jingle_add_ice_udp_candidates_to_transport(session->vrtp, video_transport, video_candidates);
+ res = jingle_add_ice_udp_candidates_to_transport(session->vrtp, video_transport, video_candidates, session->maxicecandidates);
} else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
- res = jingle_add_google_candidates_to_transport(session->vrtp, video_transport, video_candidates, 1, session->transport);
+ res = jingle_add_google_candidates_to_transport(session->vrtp, video_transport, video_candidates, 1, session->transport,
+ session->maxicecandidates);
}
} else {
res = -1;
@@ -917,7 +994,7 @@
}
/* Clean up after ourselves */
- for (i = 0; i < MAX_ICE_CANDIDATES; i++) {
+ for (i = 0; i < session->maxicecandidates; i++) {
iks_delete(video_candidates[i]);
iks_delete(audio_candidates[i]);
}
@@ -937,7 +1014,7 @@
int i = 0, res = 0;
ast_format_cap_iter_start(session->jointcap);
- while (!(ast_format_cap_iter_next(session->jointcap, &format)) && (i < MAX_PAYLOADS)) {
+ while (!(ast_format_cap_iter_next(session->jointcap, &format)) && (i < (session->maxpayloads - 2))) {
int rtp_code;
iks *payload;
char tmp[32];
@@ -967,7 +1044,7 @@
payloads[i++] = payload;
}
/* If this is for audio and there is room for RFC2833 add it in */
- if ((type == AST_FORMAT_TYPE_AUDIO) && (i < MAX_PAYLOADS)) {
+ if ((type == AST_FORMAT_TYPE_AUDIO) && (i < session->maxpayloads)) {
iks *payload;
if ((payload = iks_new("payload-type"))) {
@@ -990,11 +1067,49 @@
return res;
}
+/*! \brief Helper function which adds content to a description */
+static int jingle_add_content(struct jingle_session *session, iks *jingle, iks *content, iks *description, iks *transport,
+ const char *name, enum ast_format_type type, struct ast_rtp_instance *rtp, iks **payloads)
+{
+ int res = 0;
+
+ if (session->transport != JINGLE_TRANSPORT_GOOGLE_V1) {
+ iks_insert_attrib(content, "creator", session->outgoing ? "initiator" : "responder");
+ iks_insert_attrib(content, "name", name);
+ iks_insert_node(jingle, content);
+
+ iks_insert_attrib(description, "xmlns", JINGLE_RTP_NS);
+ if (type == AST_FORMAT_TYPE_AUDIO) {
+ iks_insert_attrib(description, "media", "audio");
+ } else if (type == AST_FORMAT_TYPE_VIDEO) {
+ iks_insert_attrib(description, "media", "video");
+ } else {
+ return -1;
+ }
+ iks_insert_node(content, description);
+ } else {
+ iks_insert_attrib(description, "xmlns", GOOGLE_PHONE_NS);
+ iks_insert_node(jingle, description);
+ }
+
+ 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);
+ iks_insert_node(content, transport);
+ } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
+ iks_insert_attrib(transport, "xmlns", GOOGLE_TRANSPORT_NS);
+ iks_insert_node(content, transport);
+ }
+ }
+
+ return res;
+}
+
/*! \brief Internal function which sends a complete session message */
static void jingle_send_session_action(struct jingle_session *session, const char *action)
{
iks *iq, *jingle, *audio = NULL, *audio_description = NULL, *video = NULL, *video_description = NULL;
- iks *audio_payloads[MAX_PAYLOADS] = { 0, }, *video_payloads[MAX_PAYLOADS] = { 0, };
+ iks *audio_payloads[session->maxpayloads], *video_payloads[session->maxpayloads];
iks *audio_transport = NULL, *video_transport = NULL;
int i, res = 0;
@@ -1004,6 +1119,9 @@
iks_delete(iq);
return;
}
+
+ memset(audio_payloads, 0, sizeof(audio_payloads));
+ memset(video_payloads, 0, sizeof(video_payloads));
iks_insert_attrib(iq, "from", session->connection->jid->full);
iks_insert_attrib(iq, "to", session->remote);
@@ -1029,52 +1147,20 @@
if (session->rtp && (audio = iks_new("content")) && (audio_description = iks_new("description")) &&
(audio_transport = iks_new("transport"))) {
- if (session->transport != JINGLE_TRANSPORT_GOOGLE_V1) {
- iks_insert_attrib(audio, "creator", session->outgoing ? "initiator" : "responder");
- iks_insert_attrib(audio, "name", session->audio_content_name);
- iks_insert_node(jingle, audio);
-
- iks_insert_attrib(audio_description, "xmlns", JINGLE_RTP_NS);
- iks_insert_attrib(audio_description, "media", "audio");
- iks_insert_node(audio, audio_description);
- } else {
- iks_insert_attrib(audio_description, "xmlns", GOOGLE_PHONE_NS);
- iks_insert_node(jingle, audio_description);
- }
-
- if (!(res = jingle_add_payloads_to_description(session, session->rtp, audio_description, audio_payloads, AST_FORMAT_TYPE_AUDIO))) {
- if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
- iks_insert_attrib(audio_transport, "xmlns", JINGLE_ICE_UDP_NS);
- iks_insert_node(audio, audio_transport);
- } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
- iks_insert_attrib(audio_transport, "xmlns", GOOGLE_TRANSPORT_NS);
- iks_insert_node(audio, audio_transport);
- }
- }
+ res = jingle_add_content(session, jingle, audio, audio_description, audio_transport, session->audio_name,
+ AST_FORMAT_TYPE_AUDIO, session->rtp, audio_payloads);
} else {
+ ast_log(LOG_ERROR, "Failed to allocate audio content stanzas for session '%s', hanging up\n", session->sid);
res = -1;
}
if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V1) && !res && session->vrtp) {
if ((video = iks_new("content")) && (video_description = iks_new("description")) &&
(video_transport = iks_new("transport"))) {
- iks_insert_attrib(video, "creator", session->outgoing ? "initiator" : "responder");
- iks_insert_attrib(video, "name", session->video_content_name);
- iks_insert_node(jingle, video);
-
- iks_insert_attrib(video_description, "xmlns", JINGLE_RTP_NS);
- iks_insert_attrib(video_description, "media", "video");
- iks_insert_node(video, video_description);
-
- if (!(res = jingle_add_payloads_to_description(session, session->vrtp, video_description, video_payloads, AST_FORMAT_TYPE_VIDEO))) {
- if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
- iks_insert_attrib(video_transport, "xmlns", JINGLE_ICE_UDP_NS);
- } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
- iks_insert_attrib(video_transport, "xmlns", GOOGLE_TRANSPORT_NS);
- }
- iks_insert_node(video, video_transport);
- }
+ res = jingle_add_content(session, jingle, video, video_description, video_transport, session->video_name,
+ AST_FORMAT_TYPE_VIDEO, session->vrtp, video_payloads);
} else {
+ ast_log(LOG_ERROR, "Failed to allocate video content stanzas for session '%s', hanging up\n", session->sid);
res = -1;
}
}
@@ -1088,7 +1174,7 @@
iks_delete(video_transport);
iks_delete(audio_transport);
- for (i = 0; i < MAX_PAYLOADS; i++) {
+ for (i = 0; i < session->maxpayloads; i++) {
iks_delete(video_payloads[i]);
iks_delete(audio_payloads[i]);
}
@@ -1125,15 +1211,13 @@
/* If no error occurred they accepted our session-initiate message happily */
if (!error) {
- ao2_lock(session);
-
- jingle_get_owner_lock(session);
-
- if (session->owner) {
- ast_queue_control(session->owner, AST_CONTROL_PROCEEDING);
- ast_channel_unlock(session->owner);
- }
-
+ struct ast_channel *chan;
+
+ if ((chan = jingle_session_lock_full(session))) {
+ ast_queue_control(chan, AST_CONTROL_PROCEEDING);
+ ast_channel_unlock(chan);
+ ast_channel_unref(chan);
+ }
ao2_unlock(session);
jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"));
@@ -1231,7 +1315,6 @@
struct jingle_session *session = ast_channel_tech_pvt(ast);
struct ast_frame *frame = &ast_null_frame;
-
switch (ast_channel_fdno(ast)) {
case 0:
if (session->rtp) {
@@ -1257,30 +1340,21 @@
break;
}
- ao2_lock(session);
-
- jingle_get_owner_lock(session);
-
- if (session->owner) {
- if (frame && frame->frametype == AST_FRAME_VOICE &&
- !ast_format_cap_iscompatible(ast_channel_nativeformats(session->owner), &frame->subclass.format)) {
- if (!ast_format_cap_iscompatible(session->jointcap, &frame->subclass.format)) {
- ast_debug(1, "Bogus frame of format '%s' received from '%s'!\n",
- ast_getformatname(&frame->subclass.format), ast_channel_name(session->owner));
- frame = &ast_null_frame;
- } else {
- ast_debug(1, "Oooh, format changed to %s\n",
- ast_getformatname(&frame->subclass.format));
- ast_format_cap_remove_bytype(ast_channel_nativeformats(session->owner), AST_FORMAT_TYPE_AUDIO);
- ast_format_cap_add(ast_channel_nativeformats(session->owner), &frame->subclass.format);
- ast_set_read_format(session->owner, ast_channel_readformat(session->owner));
- ast_set_write_format(session->owner, ast_channel_writeformat(session->owner));
- }
- }
- ast_channel_unlock(session->owner);
- }
-
- ao2_unlock(session);
+ if (frame && frame->frametype == AST_FRAME_VOICE &&
+ !ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &frame->subclass.format)) {
+ if (!ast_format_cap_iscompatible(session->jointcap, &frame->subclass.format)) {
+ ast_debug(1, "Bogus frame of format '%s' received from '%s'!\n",
+ ast_getformatname(&frame->subclass.format), ast_channel_name(ast));
+ frame = &ast_null_frame;
+ } else {
+ ast_debug(1, "Oooh, format changed to %s\n",
+ ast_getformatname(&frame->subclass.format));
+ ast_format_cap_remove_bytype(ast_channel_nativeformats(ast), AST_FORMAT_TYPE_AUDIO);
+ ast_format_cap_add(ast_channel_nativeformats(ast), &frame->subclass.format);
+ ast_set_read_format(ast, ast_channel_readformat(ast));
+ ast_set_write_format(ast, ast_channel_writeformat(ast));
+ }
+ }
return frame;
}
@@ -1458,6 +1532,8 @@
{
struct jingle_session *session = ast_channel_tech_pvt(ast);
+ ao2_lock(session);
+
if ((ast_channel_state(ast) != AST_STATE_DOWN) && !session->gone) {
int cause = (session->owner ? ast_channel_hangupcause(session->owner) : AST_CAUSE_CONGESTION);
const char *reason = "success";
@@ -1475,8 +1551,12 @@
}
ast_channel_tech_pvt_set(ast, NULL);
+ session->owner = NULL;
+
ao2_unlink(session->endpoint->sessions, session);
ao2_ref(session->endpoint, -1);
+
+ ao2_unlock(session);
ao2_ref(session, -1);
return 0;
@@ -1486,7 +1566,7 @@
static struct ast_channel *jingle_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
{
char *dialed, target[200] = "";
- struct jingle_endpoint tmp, *endpoint;
+ struct jingle_endpoint *endpoint;
struct aji_buddy *buddy;
struct jingle_session *session;
struct ast_channel *chan;
@@ -1518,9 +1598,7 @@
return NULL;
}
- ast_copy_string(tmp.name, args.name, sizeof(tmp.name));
-
- if (!(endpoint = ao2_find(endpoints, &tmp, OBJ_POINTER))) {
+ if (!(endpoint = ao2_find(endpoints, args.name, OBJ_KEY))) {
ast_log(LOG_ERROR, "Endpoint '%s' does not exist.\n", args.name);
*cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
return NULL;
@@ -1603,8 +1681,248 @@
return chan;
}
-/*! \brief Helper function which locates content stanzas and interprets them */
-static void jingle_interpret_content(struct jingle_session *session, ikspak *pak)
+/*! \brief Helper function which handles content descriptions */
+static int jingle_interpret_description(struct jingle_session *session, iks *description, const char *name, struct ast_rtp_instance **rtp)
+{
+ char *media = iks_find_attrib(description, "media");
+ struct ast_rtp_codecs codecs;
+ iks *codec;
+ int othercapability = 0;
+
+ /* Google-V1 is always carrying audio, but just doesn't tell us so */
+ if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
+ media = "audio";
+ } else if (ast_strlen_zero(media)) {
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ ast_log(LOG_ERROR, "Received a content description on session '%s' without a name\n", session->sid);
+ return -1;
+ }
+
+ /* Determine the type of media that is being carried and update the RTP instance, as well as the name */
+ if (!strcasecmp(media, "audio")) {
+ if (!ast_strlen_zero(name)) {
+ ast_string_field_set(session, audio_name, name);
+ }
+ *rtp = session->rtp;
+ ast_format_cap_remove_bytype(session->peercap, AST_FORMAT_TYPE_AUDIO);
+ ast_format_cap_remove_bytype(session->jointcap, AST_FORMAT_TYPE_AUDIO);
+ } else if (!strcasecmp(media, "video")) {
+ if (!ast_strlen_zero(name)) {
+ ast_string_field_set(session, video_name, name);
+ }
+
+ jingle_enable_video(session);
+ *rtp = session->vrtp;
+
+ /* If video is not present cancel this session */
+ if (!session->vrtp) {
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ ast_log(LOG_ERROR, "Received a video content description on session '%s' but could not enable video\n", session->sid);
+ return -1;
+ }
+
+ ast_format_cap_remove_bytype(session->peercap, AST_FORMAT_TYPE_VIDEO);
+ ast_format_cap_remove_bytype(session->jointcap, AST_FORMAT_TYPE_VIDEO);
+ } else {
+ /* Unknown media type */
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ ast_log(LOG_ERROR, "Unsupported media type '%s' received in content description on session '%s'\n", media, session->sid);
+ return -1;
+ }
+
+ ast_rtp_codecs_payloads_clear(&codecs, NULL);
+
+ /* Iterate the codecs updating the relevant RTP instance as we go */
+ for (codec = iks_child(description); codec; codec = iks_next(codec)) {
+ 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;
+
+ if (!ast_strlen_zero(id) && !ast_strlen_zero(name) && (sscanf(id, "%30d", &rtp_id) == 1)) {
+ ast_rtp_codecs_payloads_set_m_type(&codecs, NULL, rtp_id);
+
+ if (!ast_strlen_zero(clockrate) && (sscanf(clockrate, "%30d", &rtp_clockrate) == 1)) {
+ ast_rtp_codecs_payloads_set_rtpmap_type_rate(&codecs, NULL, rtp_id, media, name, 0, rtp_clockrate);
+ } else {
+ ast_rtp_codecs_payloads_set_rtpmap_type(&codecs, NULL, rtp_id, media, name, 0);
+ }
+ }
+ }
+
+ ast_rtp_codecs_payload_formats(&codecs, session->peercap, &othercapability);
+ ast_format_cap_joint_append(session->cap, session->peercap, session->jointcap);
+
+ if (ast_format_cap_is_empty(session->jointcap)) {
+ /* We have no compatible codecs, so terminate the session appropriately */
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+ return -1;
+ }
+
+ ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(*rtp), *rtp);
+
+ return 0;
+}
+
+/*! \brief Helper function which handles ICE-UDP transport information */
+static int jingle_interpret_ice_udp_transport(struct jingle_session *session, iks *transport, struct ast_rtp_instance *rtp)
+{
+ struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(rtp);
+ char *ufrag = iks_find_attrib(transport, "ufrag"), *pwd = iks_find_attrib(transport, "pwd");
+ iks *candidate;
+
+ if (!ice) {
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
+ ast_log(LOG_ERROR, "Received ICE-UDP transport information on session '%s' but ICE support not available\n", session->sid);
+ return -1;
+ }
+
+ if (ast_strlen_zero(ufrag) || ast_strlen_zero(pwd)) {
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
+ ast_log(LOG_ERROR, "Invalid ICE-UDP transport information received on session '%s', ufrag or pwd not present\n", session->sid);
+ return -1;
+ }
+
+ ice->set_authentication(rtp, ufrag, pwd);
+
+ for (candidate = iks_child(transport); candidate; candidate = iks_next(candidate)) {
+ 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 *ip = iks_find_attrib(candidate, "ip"), *network = iks_find_attrib(candidate, "network");
+ char *port = iks_find_attrib(candidate, "port"), *priority = iks_find_attrib(candidate, "priority");
+ char *protocol = iks_find_attrib(candidate, "protocol"), *type = iks_find_attrib(candidate, "type");
+ struct ast_rtp_engine_ice_candidate local_candidate = { 0, };
+ int real_port;
+ 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) ||
+ ast_strlen_zero(ip) || ast_strlen_zero(network) || 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);
+ ast_log(LOG_ERROR, "Incomplete ICE-UDP candidate received on session '%s'\n", session->sid);
+ return -1;
+ }
+
+ if ((sscanf(component, "%30u", &local_candidate.id) != 1) ||
+ (sscanf(priority, "%30u", &local_candidate.priority) != 1) ||
+ (sscanf(port, "%30d", &real_port) != 1)) {
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
+ ast_log(LOG_ERROR, "Invalid ICE-UDP candidate information received on session '%s'\n", session->sid);
+ return -1;
+ }
+
+ local_candidate.foundation = foundation;
+ local_candidate.transport = protocol;
+
+ ast_sockaddr_parse(&local_candidate.address, ip, PARSE_PORT_FORBID);
+
+ /* We only support IPv4 right now */
+ if (!ast_sockaddr_is_ipv4(&local_candidate.address)) {
+ continue;
+ }
+
+ ast_sockaddr_set_port(&local_candidate.address, real_port);
+
+ if (!strcasecmp(type, "host")) {
+ local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST;
+ } else if (!strcasecmp(type, "srflx")) {
+ local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX;
+ } else if (!strcasecmp(type, "relay")) {
+ local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
+ } else {
+ continue;
+ }
+
+ /* Worst case use the first viable address */
+ ast_rtp_instance_get_remote_address(rtp, &remote_address);
+
+ if (ast_sockaddr_is_ipv4(&local_candidate.address) && ast_sockaddr_isnull(&remote_address)) {
+ ast_rtp_instance_set_remote_address(rtp, &local_candidate.address);
+ }
+
+ ice->add_remote_candidate(rtp, &local_candidate);
+ }
+
+ ice->start(rtp);
+
+ return 0;
+}
+
+/*! \brief Helper function which handles Google transport information */
+static int jingle_interpret_google_transport(struct jingle_session *session, iks *transport, struct ast_rtp_instance *rtp)
+{
+ struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(rtp);
+ iks *candidate;
+
+ if (!ice) {
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
+ ast_log(LOG_ERROR, "Received Google transport information on session '%s' but ICE support not available\n", session->sid);
+ return -1;
+ }
+
+ /* If this session has not transitioned to the Google transport do so now */
+ if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V2) &&
+ (session->transport != JINGLE_TRANSPORT_GOOGLE_V1)) {
+ /* Stop built-in ICE support... we need to fall back to the old old old STUN */
+ ice->stop(rtp);
+
+ session->transport = JINGLE_TRANSPORT_GOOGLE_V2;
+ }
+
+ for (candidate = iks_child(transport); candidate; candidate = iks_next(candidate)) {
+ char *address = iks_find_attrib(candidate, "address"), *port = iks_find_attrib(candidate, "port");
+ char *username = iks_find_attrib(candidate, "username"), *name = iks_find_attrib(candidate, "name");
+ char *protocol = iks_find_attrib(candidate, "protocol");
+ int real_port;
+ struct ast_sockaddr target = { { 0, } };
+ /* In Google land the combined value is 32 bytes */
+ char combined[33] = "";
+
+ /* If this is NOT actually a candidate just skip it */
+ if (strcasecmp(iks_name(candidate), "candidate") &&
+ strcasecmp(iks_name(candidate), "p:candidate") &&
+ strcasecmp(iks_name(candidate), "ses:candidate")) {
+ continue;
+ }
+
+ /* If this candidate is incomplete skip it */
+ if (ast_strlen_zero(address) || ast_strlen_zero(port) || ast_strlen_zero(username) ||
+ ast_strlen_zero(name)) {
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
+ ast_log(LOG_ERROR, "Incomplete Google candidate received on session '%s'\n", session->sid);
+ return -1;
+ }
+
+ /* We only support UDP so skip any other protocols */
+ if (!ast_strlen_zero(protocol) && strcasecmp(protocol, "udp")) {
+ continue;
+ }
+
+ /* Parse the target information so we can send a STUN request to the candidate */
+ if (sscanf(port, "%30d", &real_port) != 1) {
+ jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
+ ast_log(LOG_ERROR, "Invalid Google candidate port '%s' received on session '%s'\n", port, session->sid);
+ return -1;
+ }
+ ast_sockaddr_parse(&target, address, PARSE_PORT_FORBID);
+ ast_sockaddr_set_port(&target, real_port);
+
+ /* Per the STUN support Google talk uses combine the two usernames */
+ snprintf(combined, sizeof(combined), "%s%s", username, ice->get_ufrag(rtp));
+
+ /* This should appease the masses... we will actually change the remote address when we get their STUN packet */
+ ast_rtp_instance_stun_request(rtp, &target, combined);
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Helper function which locates content stanzas and interprets them
+ *
+ * \note The session *must not* be locked before calling this
+ */
+static int jingle_interpret_content(struct jingle_session *session, ikspak *pak)
{
iks *content;
[... 689 lines stripped ...]
More information about the asterisk-commits
mailing list