summaryrefslogtreecommitdiff
path: root/src/rexmpp_jingle.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rexmpp_jingle.c')
-rw-r--r--src/rexmpp_jingle.c1956
1 files changed, 1180 insertions, 776 deletions
diff --git a/src/rexmpp_jingle.c b/src/rexmpp_jingle.c
index 3a72a95..2b84445 100644
--- a/src/rexmpp_jingle.c
+++ b/src/rexmpp_jingle.c
@@ -27,24 +27,251 @@ A/V calls over ICE-UDP + DTLS-SRTP:
#include <syslog.h>
#include <errno.h>
#include <libgen.h>
-#include <gcrypt.h>
#include "config.h"
#ifdef ENABLE_CALLS
+#include <inttypes.h>
#include <glib.h>
#include <gio/gnetworking.h>
#include <nice.h>
#include <agent.h>
-#include <gnutls/dtls.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
#include <srtp2/srtp.h>
+#include <math.h>
+#include "portaudio.h"
+#ifdef HAVE_OPUS
+#include <opus.h>
+#endif
#endif
#include "rexmpp.h"
+#include "rexmpp_xml.h"
#include "rexmpp_jingle.h"
#include "rexmpp_base64.h"
+#include "rexmpp_random.h"
+#include "rexmpp_tls.h"
+#include "rexmpp_digest.h"
+
+
+/* https://en.wikipedia.org/wiki/G.711 */
+uint8_t rexmpp_pcma_encode (int16_t x) {
+ uint8_t sign = 0x80;
+ uint8_t pos;
+ uint8_t val = 0;
+ if (x < 0) {
+ x = -x;
+ sign = 0;
+ }
+ x >>= 3;
+ for (pos = 11; pos > 5 && ! (x & (1 << pos)); pos--);
+ val = (x >> (pos - 4)) & 0xf;
+ return (sign | ((pos - 4) << 4) | val) ^ 0x55;
+}
+
+int16_t rexmpp_pcma_decode (uint8_t x) {
+ x ^= 0x55;
+ int16_t sign = -1;
+ uint8_t shift = (x >> 4) & 7;
+ int16_t val = x & 0xf;
+ if (x & 0x80) {
+ x ^= 0x80;
+ sign = 1;
+ }
+ val = (val << 1) | 1;
+ if (shift > 0) {
+ val = (val | 0x20) << (shift - 1);
+ }
+ val <<= 3;
+ return sign * val;
+}
+
+uint8_t rexmpp_pcmu_encode (int16_t x) {
+ uint8_t sign = 0;
+ uint8_t pos;
+ uint8_t val = 0;
+ if (x < 0) {
+ x = -x;
+ sign = 0x80;
+ }
+ x >>= 2;
+ for (pos = 12; pos > 5 && ! (x & (1 << pos)); pos--);
+ val = (x >> (pos - 4)) & 0xf;
+ return (sign | ((pos - 4) << 4) | val) ^ 0xff;
+}
+
+int16_t rexmpp_pcmu_decode (uint8_t x) {
+ x ^= 0xff;
+ int16_t sign = 1;
+ uint8_t shift = (x >> 4) & 7;
+ int16_t val = x & 0xf;
+ if (x & 0x80) {
+ x ^= 0x80;
+ sign = -1;
+ }
+ val = (val << 1) | 1;
+ if (shift > 0) {
+ val = (val | 0x20) << (shift - 1);
+ }
+ val <<= 2;
+ return sign * val;
+}
+
+rexmpp_xml_t *
+rexmpp_jingle_session_payload_by_id (rexmpp_jingle_session_t *sess,
+ int payload_type_id)
+{
+ if (sess->accept == NULL) {
+ return NULL;
+ }
+ rexmpp_xml_t *descr_child =
+ rexmpp_xml_first_elem_child
+ (rexmpp_xml_find_child
+ (rexmpp_xml_find_child
+ (sess->accept, "urn:xmpp:jingle:1", "content"),
+ "urn:xmpp:jingle:apps:rtp:1", "description"));
+ while (descr_child != NULL) {
+ if (rexmpp_xml_match(descr_child, "urn:xmpp:jingle:apps:rtp:1",
+ "payload-type"))
+ {
+ const char *pl_id = rexmpp_xml_find_attr_val(descr_child, "id");
+ if (pl_id != NULL && atoi(pl_id) == payload_type_id) {
+ return descr_child;
+ }
+ }
+ descr_child = rexmpp_xml_next_elem_sibling(descr_child);
+ }
+ return NULL;
+}
+
+#ifdef ENABLE_CALLS
+int rexmpp_jingle_session_configure_audio (rexmpp_jingle_session_t *sess) {
+ if (sess->accept == NULL) {
+ return 0;
+ }
+ rexmpp_xml_t *descr_child =
+ rexmpp_xml_first_elem_child
+ (rexmpp_xml_find_child
+ (rexmpp_xml_find_child(sess->accept, "urn:xmpp:jingle:1", "content"),
+ "urn:xmpp:jingle:apps:rtp:1", "description"));
+ while (descr_child != NULL) {
+ if (rexmpp_xml_match(descr_child, "urn:xmpp:jingle:apps:rtp:1",
+ "payload-type"))
+ {
+ const char *pl_id = rexmpp_xml_find_attr_val(descr_child, "id");
+ if (pl_id != NULL) {
+ if (atoi(pl_id) == 0) {
+ rexmpp_log(sess->s, LOG_INFO,
+ "Setting the codec to PCMU (0) for Jingle session %s",
+ sess->sid);
+ sess->codec = REXMPP_CODEC_PCMU;
+ sess->payload_type = 0;
+ return sess->payload_type;
+ }
+ if (atoi(pl_id) == 8) {
+ rexmpp_log(sess->s, LOG_INFO,
+ "Setting the codec to PCMA (8) for Jingle session %s",
+ sess->sid);
+ sess->codec = REXMPP_CODEC_PCMA;
+ sess->payload_type = 8;
+ return sess->payload_type;
+ }
+#ifdef HAVE_OPUS
+ const char *pl_name = rexmpp_xml_find_attr_val(descr_child, "name");
+ if ((pl_name != NULL) && (strcmp(pl_name, "opus") == 0)) {
+ sess->codec = REXMPP_CODEC_OPUS;
+ sess->payload_type = atoi(pl_id);
+ rexmpp_log(sess->s, LOG_INFO,
+ "Setting the codec to Opus (%u) for Jingle session %s",
+ sess->payload_type, sess->sid);
+ return sess->payload_type;
+ }
+#endif /* HAVE_OPUS */
+ }
+ }
+ descr_child = rexmpp_xml_next_elem_sibling(descr_child);
+ }
+ return 0;
+}
+
+
+static int rexmpp_pa_callback (const void *input,
+ void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData)
+{
+ (void)timeInfo;
+ (void)statusFlags;
+ struct pa_buffers *data = (struct pa_buffers*)userData;
+ int16_t *out = (int16_t*)output;
+ int16_t *in = (int16_t*)input;
+ unsigned int i;
+ for (i = 0; i < frameCount; i++) {
+ if (in != NULL) {
+ data->capture.buf[data->capture.write_pos] = in[i];
+ data->capture.write_pos++;
+ data->capture.write_pos %= PA_BUF_SIZE;
+ }
+
+ if (data->playback.read_pos != data->playback.write_pos) {
+ out[i] = data->playback.buf[data->playback.read_pos];
+ data->playback.read_pos++;
+ data->playback.read_pos %= PA_BUF_SIZE;
+ } else {
+ out[i] = 0;
+ }
+ }
+ return 0;
+}
+
+void
+rexmpp_jingle_run_audio (rexmpp_jingle_session_t *sess) {
+ rexmpp_random_buf(&(sess->rtp_seq_num), sizeof(uint16_t));
+ sess->rtp_last_seq_num = 0xffff;
+ rexmpp_random_buf(&(sess->rtp_timestamp), sizeof(uint32_t));
+ rexmpp_random_buf(&(sess->rtp_ssrc), sizeof(uint32_t));
+
+ int rate = 8000;
+ int channels = 1;
+#ifdef HAVE_OPUS
+ if (sess->codec == REXMPP_CODEC_OPUS) {
+ rate = 48000;
+ channels = 2;
+ int opus_error;
+ sess->opus_enc =
+ opus_encoder_create(rate, channels, OPUS_APPLICATION_VOIP, &opus_error);
+ sess->opus_dec =
+ opus_decoder_create(rate, channels, &opus_error);
+ }
+#endif /* HAVE_OPUS */
+
+ rexmpp_log(sess->s, LOG_DEBUG,
+ "Setting up an audio stream: SSRC %" PRIx32
+ ", %d Hz, %d channels",
+ sess->rtp_ssrc, rate, channels);
+
+ PaError err = Pa_OpenDefaultStream (&(sess->pa_stream),
+ channels,
+ channels,
+ paInt16,
+ /* paFloat32, */
+ rate,
+ /* 480, */
+ paFramesPerBufferUnspecified,
+ rexmpp_pa_callback,
+ &(sess->ring_buffers));
+ if (err != paNoError) {
+ rexmpp_log(sess->s, LOG_ERR, "Failed to open a PA stream: %s",
+ Pa_GetErrorText(err));
+ }
+ err = Pa_StartStream(sess->pa_stream);
+ if (err != paNoError) {
+ rexmpp_log(sess->s, LOG_ERR, "Failed to start a PA stream: %s",
+ Pa_GetErrorText(err));
+ }
+}
+#endif /* ENABLE_CALLS */
rexmpp_jingle_session_t *
@@ -52,7 +279,7 @@ rexmpp_jingle_session_by_id (rexmpp_t *s, const char *sid) {
if (sid == NULL) {
return NULL;
}
- rexmpp_jingle_session_t *cur = s->jingle.sessions;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions;
while (cur != NULL) {
if (strcmp(cur->sid, sid) == 0) {
return cur;
@@ -71,17 +298,36 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) {
free(session->sid);
}
if (session->initiate != NULL) {
- xmlFreeNodeList(session->initiate);
+ rexmpp_xml_free_list(session->initiate);
}
if (session->accept != NULL) {
- xmlFreeNodeList(session->accept);
+ rexmpp_xml_free_list(session->accept);
}
if (session->ibb_fh != NULL) {
fclose(session->ibb_fh);
}
- #ifdef ENABLE_CALLS
+#ifdef ENABLE_CALLS
if (session->type == REXMPP_JINGLE_SESSION_MEDIA) {
int i;
+ if (session->pa_stream != NULL) {
+ PaError err = Pa_StopStream(session->pa_stream);
+ if (err != paNoError) {
+ rexmpp_log(session->s, LOG_ERR,
+ "Failed to close a PortAudio stream: %s",
+ Pa_GetErrorText(err));
+ }
+ session->pa_stream = NULL;
+ }
+#ifdef HAVE_OPUS
+ if (session->opus_enc != NULL) {
+ opus_encoder_destroy(session->opus_enc);
+ session->opus_enc = NULL;
+ }
+ if (session->opus_dec != NULL) {
+ opus_decoder_destroy(session->opus_dec);
+ session->opus_dec = NULL;
+ }
+#endif /* HAVE_OPUS */
for (i = 0; i < 2; i++) {
rexmpp_jingle_component_t *comp = &session->component[i];
if (comp->dtls_state == REXMPP_TLS_ACTIVE ||
@@ -90,22 +336,29 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) {
/* SRTP structures are allocated upon a TLS connection, so
using the TLS state to find when they should be
deallocated. */
- srtp_dealloc(comp->srtp_in);
- srtp_dealloc(comp->srtp_out);
+ if (comp->srtp_in != NULL) {
+ srtp_dealloc(comp->srtp_in);
+ }
+ if (comp->srtp_out != NULL) {
+ srtp_dealloc(comp->srtp_out);
+ }
}
if (comp->dtls_state == REXMPP_TLS_HANDSHAKE ||
comp->dtls_state == REXMPP_TLS_ACTIVE ||
comp->dtls_state == REXMPP_TLS_CLOSING ||
comp->dtls_state == REXMPP_TLS_CLOSED) {
- gnutls_deinit(comp->dtls_session);
+ if (comp->dtls_state == REXMPP_TLS_HANDSHAKE ||
+ comp->dtls_state == REXMPP_TLS_ACTIVE) {
+ rexmpp_tls_disconnect(comp->s, comp->dtls);
+ }
+ rexmpp_tls_session_free(comp->dtls);
+ rexmpp_tls_ctx_free(comp->dtls);
+ comp->dtls = NULL;
comp->dtls_state = REXMPP_TLS_INACTIVE;
}
- if (comp->udp_socket != -1) {
- close(comp->udp_socket);
- comp->udp_socket = -1;
- }
}
if (session->ice_agent != NULL) {
+ nice_agent_close_async(session->ice_agent, NULL, NULL);
g_object_unref(session->ice_agent);
session->ice_agent = NULL;
}
@@ -126,7 +379,7 @@ void rexmpp_jingle_session_destroy (rexmpp_jingle_session_t *session) {
session->turn_password = NULL;
}
}
- #endif
+#endif /* ENABLE_CALLS */
free(session);
}
@@ -135,11 +388,11 @@ void rexmpp_jingle_session_delete (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
return;
}
rexmpp_log(s, LOG_DEBUG, "Removing Jingle session %s", sess->sid);
- rexmpp_jingle_session_t *cur = s->jingle.sessions, *prev = NULL;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions, *prev = NULL;
while (cur != NULL) {
if (sess == cur) {
if (prev == NULL) {
- s->jingle.sessions = cur->next;
+ s->jingle->sessions = cur->next;
} else {
prev->next = cur->next;
}
@@ -157,7 +410,7 @@ void rexmpp_jingle_session_delete_by_id (rexmpp_t *s, const char *sid) {
int rexmpp_jingle_session_add (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
uint32_t sessions_num = 0;
- rexmpp_jingle_session_t *cur = s->jingle.sessions;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions;
while (cur != NULL) {
sessions_num++;
cur = cur->next;
@@ -168,8 +421,8 @@ int rexmpp_jingle_session_add (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
return 0;
}
rexmpp_log(s, LOG_DEBUG, "Adding Jingle session %s", sess->sid);
- sess->next = s->jingle.sessions;
- s->jingle.sessions = sess;
+ sess->next = s->jingle->sessions;
+ s->jingle->sessions = sess;
return 1;
}
@@ -184,45 +437,61 @@ rexmpp_jingle_session_create (rexmpp_t *s,
{
rexmpp_jingle_session_t *sess = malloc(sizeof(rexmpp_jingle_session_t));
if (sess != NULL) {
- sess->s = s;
sess->jid = jid;
sess->sid = sid;
- sess->type = type;
- sess->initiator = initiator;
sess->initiate = NULL;
sess->accept = NULL;
+ sess->s = s;
+ sess->initiator = initiator;
+ sess->type = type;
sess->ibb_fh = NULL;
sess->ibb_sid = NULL;
sess->ibb_seq = 0;
#ifdef ENABLE_CALLS
+ sess->stun_host = NULL;
+ sess->stun_port = 0;
+ sess->turn_host = NULL;
+ sess->turn_port = 0;
+ sess->turn_username = NULL;
+ sess->turn_password = NULL;
+
int i;
for (i = 0; i < 2; i++) {
sess->component[i].component_id = i + 1;
sess->component[i].session = sess;
sess->component[i].s = s;
sess->component[i].dtls_state = REXMPP_TLS_INACTIVE;
- sess->component[i].dtls_buf_len = 0;
- sess->component[i].udp_socket = -1;
+ sess->component[i].dtls = rexmpp_tls_ctx_new(s, 1);
+ sess->component[i].srtp_out = NULL;
+ sess->component[i].srtp_in = NULL;
}
- sess->ice_agent = NULL;
+
sess->rtcp_mux = s->jingle_prefer_rtcp_mux;
+ sess->ice_agent = NULL;
- sess->stun_host = NULL;
- sess->stun_port = 0;
- sess->turn_host = NULL;
- sess->turn_port = 0;
- sess->turn_username = NULL;
- sess->turn_password = NULL;
- /* rexmpp_jingle_ice_agent_init(sess); */
-#endif
+ sess->pa_stream = NULL;
+
+ sess->ring_buffers.capture.read_pos = 0;
+ sess->ring_buffers.capture.write_pos = 0;
+ sess->ring_buffers.playback.read_pos = 0;
+ sess->ring_buffers.playback.write_pos = 0;
+ sess->codec = REXMPP_CODEC_UNDEFINED;
+ sess->payload_type = 0;
+
+#ifdef HAVE_OPUS
+ sess->opus_enc = NULL;
+ sess->opus_dec = NULL;
+#endif /* HAVE_POUS */
+#endif /* ENABLE_CALLS */
if (! rexmpp_jingle_session_add(s, sess)) {
- rexmpp_jingle_session_destroy(sess);
- sess = NULL;
+ /* The session is destroyed by rexmpp_jingle_session_add */
+ return NULL;
}
+ return sess;
} else {
rexmpp_log(s, LOG_ERR, "Failed to allocate memory for a Jingle session");
+ return NULL;
}
- return sess;
}
rexmpp_jingle_session_t *
@@ -230,7 +499,7 @@ rexmpp_jingle_session_by_ibb_sid (rexmpp_t *s, const char *ibb_sid) {
if (ibb_sid == NULL) {
return NULL;
}
- rexmpp_jingle_session_t *cur = s->jingle.sessions;
+ rexmpp_jingle_session_t *cur = s->jingle->sessions;
while (cur != NULL) {
if (cur->type == REXMPP_JINGLE_SESSION_FILE &&
strcmp(cur->ibb_sid, ibb_sid) == 0) {
@@ -244,31 +513,37 @@ rexmpp_jingle_session_by_ibb_sid (rexmpp_t *s, const char *ibb_sid) {
}
int rexmpp_jingle_init (rexmpp_t *s) {
- s->jingle.sessions = NULL;
+ s->jingle = malloc(sizeof(struct rexmpp_jingle_ctx));
+ s->jingle->sessions = NULL;
#ifdef ENABLE_CALLS
g_networking_init();
srtp_init();
- s->jingle.gloop = g_main_loop_new(NULL, FALSE);
-#endif
+ s->jingle->gloop = g_main_loop_new(NULL, FALSE);
+ Pa_Initialize();
+ nice_debug_enable(1);
+#endif /* ENABLE_CALLS */
return 0;
}
void rexmpp_jingle_stop (rexmpp_t *s) {
- while (s->jingle.sessions != NULL) {
- rexmpp_jingle_session_delete(s, s->jingle.sessions);
+ while (s->jingle->sessions != NULL) {
+ rexmpp_jingle_session_delete(s, s->jingle->sessions);
}
#ifdef ENABLE_CALLS
- g_main_loop_quit(s->jingle.gloop);
- s->jingle.gloop = NULL;
+ g_main_loop_quit(s->jingle->gloop);
+ s->jingle->gloop = NULL;
srtp_shutdown();
-#endif
+ Pa_Terminate();
+#endif /* ENABLE_CALLS */
+ free(s->jingle);
+ s->jingle = NULL;
}
void rexmpp_jingle_accept_file_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -292,15 +567,16 @@ rexmpp_jingle_accept_file (rexmpp_t *s,
path, strerror(errno));
return REXMPP_E_OTHER;
}
- xmlNodePtr jingle = session->initiate;
- xmlNodePtr content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
+ rexmpp_xml_t *jingle = session->initiate;
+ rexmpp_xml_t *content = rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
- xmlNodePtr new_jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(new_jingle, "action", "session-accept");
- xmlNewProp(new_jingle, "responder", s->assigned_jid.full);
- xmlNewProp(new_jingle, "sid", session->sid);
- xmlAddChild(new_jingle, xmlCopyNode(content, 1));
- session->accept = xmlCopyNode(new_jingle, 1);
+ rexmpp_xml_t *new_jingle =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(new_jingle, "action", "session-accept");
+ rexmpp_xml_add_attr(new_jingle, "responder", s->assigned_jid.full);
+ rexmpp_xml_add_attr(new_jingle, "sid", session->sid);
+ rexmpp_xml_add_child(new_jingle, rexmpp_xml_clone(content));
+ session->accept = rexmpp_xml_clone(new_jingle);
return rexmpp_iq_new(s, "set", session->jid, new_jingle,
rexmpp_jingle_accept_file_cb, strdup(session->sid));
}
@@ -319,8 +595,8 @@ rexmpp_jingle_accept_file_by_id (rexmpp_t *s,
void rexmpp_jingle_session_terminate_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -336,24 +612,27 @@ void rexmpp_jingle_session_terminate_cb (rexmpp_t *s,
rexmpp_err_t
rexmpp_jingle_session_terminate (rexmpp_t *s,
const char *sid,
- xmlNodePtr reason_node,
+ rexmpp_xml_t *reason_node,
const char *reason_text)
{
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid);
if (session == NULL) {
return REXMPP_E_OTHER;
}
- xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle, "action", "session-terminate");
- xmlNewProp(jingle, "sid", sid);
- xmlNodePtr reason = rexmpp_xml_new_node("reason", "urn:xmpp:jingle:1");
+ rexmpp_xml_t *jingle =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle, "action", "session-terminate");
+ rexmpp_xml_add_attr(jingle, "sid", sid);
+ rexmpp_xml_t *reason =
+ rexmpp_xml_new_elem("reason", "urn:xmpp:jingle:1");
if (reason_text != NULL) {
- xmlNodePtr text = rexmpp_xml_new_node("text", "urn:xmpp:jingle:1");
- xmlNodeAddContent(text, reason_text);
- xmlAddChild(reason, text);
+ rexmpp_xml_t *text =
+ rexmpp_xml_new_elem("text", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_text(text, reason_text);
+ rexmpp_xml_add_child(reason, text);
}
- xmlAddChild(reason, reason_node);
- xmlAddChild(jingle, reason);
+ rexmpp_xml_add_child(reason, reason_node);
+ rexmpp_xml_add_child(jingle, reason);
rexmpp_err_t ret = rexmpp_iq_new(s, "set", session->jid, jingle,
rexmpp_jingle_session_terminate_cb,
strdup(sid));
@@ -363,8 +642,8 @@ rexmpp_jingle_session_terminate (rexmpp_t *s,
void rexmpp_jingle_send_file_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -391,95 +670,99 @@ rexmpp_jingle_send_file (rexmpp_t *s,
}
char buf[4096];
- gcry_md_hd_t hd;
- gcry_error_t err = gcry_md_open(&hd, GCRY_MD_SHA256, 0);
- if (err != GPG_ERR_NO_ERROR) {
- rexmpp_log(s, LOG_ERR, "Failed to create a MD object: %s",
- gcry_strerror(err));
+ rexmpp_digest_t sha256, sha3_256;
+ if (rexmpp_digest_init(&sha256, REXMPP_DIGEST_SHA256)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a SHA-256 digest object");
fclose(fh);
return REXMPP_E_OTHER;
}
- err = gcry_md_enable(hd, GCRY_MD_SHA3_256);
- if (err != GPG_ERR_NO_ERROR) {
- rexmpp_log(s, LOG_ERR, "Failed to add sha3-256 to the MD object: %s",
- gcry_strerror(err));
+ if (rexmpp_digest_init(&sha3_256, REXMPP_DIGEST_SHA3_256)) {
+ rexmpp_log(s, LOG_ERR, "Failed to initialize a SHA-3-256 digest object");
+ rexmpp_digest_finish(&sha256, NULL, 0);
fclose(fh);
return REXMPP_E_OTHER;
}
size_t len = fread(buf, 1, 4096, fh);
while (len > 0) {
- gcry_md_write(hd, buf, len);
+ rexmpp_digest_update(&sha256, buf, len);
+ rexmpp_digest_update(&sha3_256, buf, len);
len = fread(buf, 1, 4096, fh);
}
- gcry_md_final(hd);
-
- char *sid = rexmpp_gen_id(s);
- char *ibb_sid = rexmpp_gen_id(s);
-
- xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle, "action", "session-initiate");
- xmlNewProp(jingle, "sid", sid);
- xmlNewProp(jingle, "initiator", s->assigned_jid.full);
-
- xmlNodePtr content = rexmpp_xml_new_node("content", "urn:xmpp:jingle:1");
- xmlNewProp(content, "creator", "initiator");
- xmlNewProp(content, "name", "IBB file");
- xmlAddChild(jingle, content);
-
- xmlNodePtr transport =
- rexmpp_xml_new_node("transport", "urn:xmpp:jingle:transports:ibb:1");
- xmlNewProp(transport, "block-size", "4096");
- xmlNewProp(transport, "sid", ibb_sid);
- xmlAddChild(content, transport);
- xmlNodePtr description =
- rexmpp_xml_new_node("description", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlAddChild(content, description);
- xmlNodePtr file =
- rexmpp_xml_new_node("file", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlAddChild(description, file);
- xmlNodePtr file_name =
- rexmpp_xml_new_node("name", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlNodeAddContent(file_name, basename(path));
- xmlAddChild(file, file_name);
+ char *sid = rexmpp_random_id();
+ char *ibb_sid = rexmpp_random_id();
+
+ rexmpp_xml_t *jingle =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle, "action", "session-initiate");
+ rexmpp_xml_add_attr(jingle, "sid", sid);
+ rexmpp_xml_add_attr(jingle, "initiator", s->assigned_jid.full);
+
+ rexmpp_xml_t *content =
+ rexmpp_xml_new_elem("content", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(content, "creator", "initiator");
+ rexmpp_xml_add_attr(content, "name", "IBB file");
+ rexmpp_xml_add_child(jingle, content);
+
+ rexmpp_xml_t *transport =
+ rexmpp_xml_new_elem("transport", "urn:xmpp:jingle:transports:ibb:1");
+ rexmpp_xml_add_attr(transport, "block-size", "4096");
+ rexmpp_xml_add_attr(transport, "sid", ibb_sid);
+ rexmpp_xml_add_child(content, transport);
+ rexmpp_xml_t *description =
+ rexmpp_xml_new_elem("description", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_child(content, description);
+ rexmpp_xml_t *file =
+ rexmpp_xml_new_elem("file", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_child(description, file);
+ rexmpp_xml_t *file_name =
+ rexmpp_xml_new_elem("name", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_text(file_name, basename(path));
+ rexmpp_xml_add_child(file, file_name);
+
+ char hash[32];
char *hash_base64 = NULL;
size_t hash_base64_len = 0;
- rexmpp_base64_to(gcry_md_read(hd, GCRY_MD_SHA256),
- gcry_md_get_algo_dlen(GCRY_MD_SHA256),
+
+ rexmpp_digest_finish(&sha256, hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA256));
+ rexmpp_base64_to(hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA256),
&hash_base64,
&hash_base64_len);
- xmlNodePtr file_hash = rexmpp_xml_new_node("hash", "urn:xmpp:hashes:2");
- xmlNewProp(file_hash, "algo", "sha-256");
- xmlNodeAddContent(file_hash, hash_base64);
+ rexmpp_xml_t *file_hash =
+ rexmpp_xml_new_elem("hash", "urn:xmpp:hashes:2");
+ rexmpp_xml_add_attr(file_hash, "algo", "sha-256");
+ rexmpp_xml_add_text(file_hash, hash_base64);
free(hash_base64);
- xmlAddChild(file, file_hash);
+ rexmpp_xml_add_child(file, file_hash);
hash_base64 = NULL;
hash_base64_len = 0;
- rexmpp_base64_to(gcry_md_read(hd, GCRY_MD_SHA3_256),
- gcry_md_get_algo_dlen(GCRY_MD_SHA3_256),
+ rexmpp_digest_finish(&sha3_256, hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA3_256));
+ rexmpp_base64_to(hash,
+ rexmpp_digest_len(REXMPP_DIGEST_SHA3_256),
&hash_base64,
&hash_base64_len);
- file_hash = rexmpp_xml_new_node("hash", "urn:xmpp:hashes:2");
- xmlNewProp(file_hash, "algo", "sha3-256");
- xmlNodeAddContent(file_hash, hash_base64);
+ file_hash = rexmpp_xml_new_elem("hash", "urn:xmpp:hashes:2");
+ rexmpp_xml_add_attr(file_hash, "algo", "sha3-256");
+ rexmpp_xml_add_text(file_hash, hash_base64);
free(hash_base64);
- xmlAddChild(file, file_hash);
-
- gcry_md_close(hd);
+ rexmpp_xml_add_child(file, file_hash);
long fsize = ftell(fh);
fseek(fh, 0, SEEK_SET);
snprintf(buf, 11, "%ld", fsize);
- xmlNodePtr file_size =
- rexmpp_xml_new_node("size", "urn:xmpp:jingle:apps:file-transfer:5");
- xmlNodeAddContent(file_size, buf);
- xmlAddChild(file, file_size);
+ rexmpp_xml_t *file_size =
+ rexmpp_xml_new_elem("size", "urn:xmpp:jingle:apps:file-transfer:5");
+ rexmpp_xml_add_text(file_size, buf);
+ rexmpp_xml_add_child(file, file_size);
rexmpp_jingle_session_t *sess =
rexmpp_jingle_session_create(s, strdup(jid), sid, REXMPP_JINGLE_SESSION_FILE, 1);
if (sess != NULL) {
- sess->initiate = xmlCopyNode(jingle, 1);
+ sess->initiate = rexmpp_xml_clone(jingle);
sess->ibb_sid = ibb_sid;
sess->ibb_fh = fh;
return rexmpp_iq_new(s, "set", sess->jid, jingle,
@@ -492,8 +775,8 @@ rexmpp_jingle_send_file (rexmpp_t *s,
void rexmpp_jingle_ibb_close_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -509,8 +792,8 @@ void rexmpp_jingle_ibb_close_cb (rexmpp_t *s,
void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -529,8 +812,9 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
return;
}
if (feof(session->ibb_fh)) {
- xmlNodePtr close = rexmpp_xml_new_node("close", "http://jabber.org/protocol/ibb");
- xmlNewProp(close, "sid", session->ibb_sid);
+ rexmpp_xml_t *close =
+ rexmpp_xml_new_elem("close", "http://jabber.org/protocol/ibb");
+ rexmpp_xml_add_attr(close, "sid", session->ibb_sid);
rexmpp_iq_new(s, "set", session->jid, close,
rexmpp_jingle_ibb_close_cb, sid);
return;
@@ -538,15 +822,16 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
char buf[4096];
size_t len = fread(buf, 1, 4096, session->ibb_fh);
if (len > 0) {
- xmlNodePtr data = rexmpp_xml_new_node("data", "http://jabber.org/protocol/ibb");
- xmlNewProp(data, "sid", session->ibb_sid);
+ rexmpp_xml_t *data =
+ rexmpp_xml_new_elem("data", "http://jabber.org/protocol/ibb");
+ rexmpp_xml_add_attr(data, "sid", session->ibb_sid);
char *out = NULL;
size_t out_len = 0;
rexmpp_base64_to(buf, len, &out, &out_len);
- xmlNodeAddContent(data, out);
+ rexmpp_xml_add_text(data, out);
free(out);
snprintf(buf, 11, "%u", session->ibb_seq);
- xmlNewProp(data, "seq", buf);
+ rexmpp_xml_add_attr(data, "seq", buf);
session->ibb_seq++;
rexmpp_iq_new(s, "set", session->jid, data,
rexmpp_jingle_ibb_send_cb, sid);
@@ -554,7 +839,7 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
} else {
rexmpp_log(s, LOG_ERR, "Failed to read from a file: %s ", strerror(errno));
rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("media-error",
+ rexmpp_xml_new_elem("media-error",
"urn:xmpp:jingle:1"),
"File reading error");
}
@@ -565,8 +850,8 @@ void rexmpp_jingle_ibb_send_cb (rexmpp_t *s,
#ifdef ENABLE_CALLS
void rexmpp_jingle_call_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -581,31 +866,29 @@ void rexmpp_jingle_call_cb (rexmpp_t *s,
void
rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess,
- xmlNodePtr transport)
+ rexmpp_xml_t *transport)
{
if (sess->ice_agent == NULL) {
/* Must be an incoming call; just add candidates to
session-initiate's transport. */
- xmlNodePtr old_transport =
+ rexmpp_xml_t *old_transport =
rexmpp_xml_find_child(rexmpp_xml_find_child(sess->initiate,
"urn:xmpp:jingle:1",
"content"),
"urn:xmpp:jingle:transports:ice-udp:1",
"transport");
- xmlNodePtr candidate = xmlFirstElementChild(transport);
+ rexmpp_xml_t *candidate = rexmpp_xml_first_elem_child(transport);
while (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1",
"candidate")) {
- xmlAddChild(old_transport, xmlCopyNode(candidate, 1));
- candidate = candidate->next;
+ rexmpp_xml_add_child(old_transport, rexmpp_xml_clone(candidate));
+ candidate = rexmpp_xml_next_elem_sibling(candidate);
}
return;
}
- char *ufrag = xmlGetProp(transport, "ufrag");
- char *password = xmlGetProp(transport, "pwd");
+ const char *ufrag = rexmpp_xml_find_attr_val(transport, "ufrag");
+ const char *password = rexmpp_xml_find_attr_val(transport, "pwd");
nice_agent_set_remote_credentials(sess->ice_agent, sess->ice_stream_id,
ufrag, password);
- free(ufrag);
- free(password);
int component_id;
@@ -614,59 +897,63 @@ rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess,
nice_agent_get_remote_candidates(sess->ice_agent,
sess->ice_stream_id,
component_id);
- xmlNodePtr candidate = xmlFirstElementChild(transport);
- while (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1",
- "candidate")) {
- char *component = xmlGetProp(candidate, "component");
- if (component[0] == component_id + '0') {
- char *type_str = xmlGetProp(candidate, "type");
- int type_n = NICE_CANDIDATE_TYPE_HOST;
- if (strcmp(type_str, "host") == 0) {
- type_n = NICE_CANDIDATE_TYPE_HOST;
- } else if (strcmp(type_str, "srflx") == 0) {
- type_n = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
- } else if (strcmp(type_str, "prflx") == 0) {
- type_n = NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
- } else if (strcmp(type_str, "relay") == 0) {
- type_n = NICE_CANDIDATE_TYPE_RELAYED;
- }
- free(type_str);
- NiceCandidate *c = nice_candidate_new(type_n);
- c->component_id = component_id;
- c->stream_id = sess->ice_stream_id;
+ rexmpp_xml_t *candidate = rexmpp_xml_first_elem_child(transport);
+ while (candidate != NULL) {
+ if (rexmpp_xml_match(candidate, "urn:xmpp:jingle:transports:ice-udp:1",
+ "candidate")) {
+ const char *component = rexmpp_xml_find_attr_val(candidate, "component");
+ if (component[0] == component_id + '0') {
+ const char *type_str = rexmpp_xml_find_attr_val(candidate, "type");
+ int type_n = NICE_CANDIDATE_TYPE_HOST;
+ if (strcmp(type_str, "host") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_HOST;
+ } else if (strcmp(type_str, "srflx") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
+ } else if (strcmp(type_str, "prflx") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_PEER_REFLEXIVE;
+ } else if (strcmp(type_str, "relay") == 0) {
+ type_n = NICE_CANDIDATE_TYPE_RELAYED;
+ }
+ NiceCandidate *c = nice_candidate_new(type_n);
+ c->component_id = component_id;
+ c->stream_id = sess->ice_stream_id;
- char *foundation = xmlGetProp(candidate, "foundation");
- strncpy(c->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION - 1);
- c->foundation[NICE_CANDIDATE_MAX_FOUNDATION - 1] = 0;
- free(foundation);
+ const char *foundation = rexmpp_xml_find_attr_val(candidate, "foundation");
+ strncpy(c->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION - 1);
+ c->foundation[NICE_CANDIDATE_MAX_FOUNDATION - 1] = 0;
- c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
+ c->transport = NICE_CANDIDATE_TRANSPORT_UDP;
- char *priority = xmlGetProp(candidate, "priority");
- c->priority = atoi(priority);
- free(priority);
+ const char *priority = rexmpp_xml_find_attr_val(candidate, "priority");
+ c->priority = atoi(priority);
- char *ip = xmlGetProp(candidate, "ip");
- if (! nice_address_set_from_string(&c->addr, ip)) {
- rexmpp_log(sess->s, LOG_ERR,
- "Failed to parse an ICE-UDP candidate's address: %s",
- ip);
- }
- free(ip);
+ const char *ip = rexmpp_xml_find_attr_val(candidate, "ip");
+ if (! nice_address_set_from_string(&c->addr, ip)) {
+ rexmpp_log(sess->s, LOG_ERR,
+ "Failed to parse an ICE-UDP candidate's address: %s",
+ ip);
+ }
- char *port = xmlGetProp(candidate, "port");
- nice_address_set_port(&c->addr, atoi(port));
- free(port);
+ const char *port = rexmpp_xml_find_attr_val(candidate, "port");
+ nice_address_set_port(&c->addr, atoi(port));
- remote_candidates = g_slist_prepend(remote_candidates, c);
+ remote_candidates = g_slist_prepend(remote_candidates, c);
+ }
}
- free(component);
- candidate = candidate->next;
+ candidate = rexmpp_xml_next_elem_sibling(candidate);
}
if (remote_candidates != NULL) {
+ rexmpp_log(sess->s, LOG_DEBUG,
+ "Setting %d remote candidates for component %d",
+ g_slist_length(remote_candidates),
+ component_id);
nice_agent_set_remote_candidates(sess->ice_agent, sess->ice_stream_id,
component_id, remote_candidates);
g_slist_free_full(remote_candidates, (GDestroyNotify)&nice_candidate_free);
+ } else {
+ rexmpp_log(sess->s, LOG_WARNING,
+ "No remote candidates for component %d",
+ component_id);
}
}
}
@@ -674,7 +961,7 @@ rexmpp_jingle_ice_udp_add_remote (rexmpp_jingle_session_t *sess,
/* Checks whether we are in the active (client) role for DTLS, based
on either "session-initiate" or "session-accept" message. */
int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate) {
- xmlNodePtr fingerprint =
+ rexmpp_xml_t *fingerprint =
rexmpp_xml_find_child
(rexmpp_xml_find_child
(rexmpp_xml_find_child
@@ -687,7 +974,7 @@ int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate
in_initiate ? "initiate" : "accept");
return 0;
}
- char *fingerprint_setup = xmlGetProp(fingerprint, "setup");
+ const char *fingerprint_setup = rexmpp_xml_find_attr_val(fingerprint, "setup");
if (fingerprint_setup == NULL) {
rexmpp_log(sess->s, LOG_ERR, "No 'setup' attribute for a fingerprint element");
return 0;
@@ -706,15 +993,14 @@ int rexmpp_jingle_dtls_is_active (rexmpp_jingle_session_t *sess, int in_initiate
active = (strcmp(fingerprint_setup, "active") == 0);
}
}
- free(fingerprint_setup);
return active;
}
void rexmpp_transport_info_call_cb (rexmpp_t *s,
void *ptr,
- xmlNodePtr request,
- xmlNodePtr response,
+ rexmpp_xml_t *request,
+ rexmpp_xml_t *response,
int success)
{
(void)request;
@@ -735,126 +1021,110 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
{
rexmpp_jingle_session_t *sess = data;
- gnutls_x509_crt_t *cert_list;
- int cert_list_size = 0;
- /* We'll need a certificate a bit later, but checking it before
- allocating other things. */
- int err = gnutls_certificate_get_x509_crt(sess->s->jingle.dtls_cred, 0,
- &cert_list, &cert_list_size);
- if (err) {
- rexmpp_log(sess->s, LOG_ERR,
- "Failed to read own certificate list: %s",
- gnutls_strerror(err));
- return;
- }
+ rexmpp_log(sess->s, LOG_DEBUG, "ICE agent candidate gathering is done");
- char fp[32], fp_str[97];
+ /* We'll need a fingerprint a bit later, but checking it before
+ allocating other things. */
+ char fp[32], fp_str[32 * 3 + 1];
size_t fp_size = 32;
- gnutls_x509_crt_get_fingerprint(cert_list[0], GNUTLS_DIG_SHA256, fp, &fp_size);
- int i;
- for (i = 0; i < 32; i++) {
- snprintf(fp_str + i * 3, 4, "%02X:", fp[i] & 0xFF);
- }
- fp_str[95] = 0;
-
- for (i = 0; i < cert_list_size; i++) {
- gnutls_x509_crt_deinit(cert_list[i]);
+ if (rexmpp_tls_my_fp(sess->s, fp, fp_str, &fp_size)) {
+ return;
}
- gnutls_free(cert_list);
- xmlNodePtr jingle = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle, "sid", sess->sid);
+ rexmpp_xml_t *jingle = rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle, "sid", sess->sid);
- xmlNodePtr content = rexmpp_xml_new_node("content", "urn:xmpp:jingle:1");
- xmlNewProp(content, "creator", "initiator");
- xmlNewProp(content, "senders", "both");
- xmlNodePtr description;
+ rexmpp_xml_t *content = rexmpp_xml_new_elem("content", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(content, "creator", "initiator");
+ rexmpp_xml_add_attr(content, "senders", "both");
+ rexmpp_xml_t *description;
if (sess->initiator) {
- xmlNewProp(jingle, "action", "session-initiate");
- xmlNewProp(jingle, "initiator", sess->s->assigned_jid.full);
- xmlNewProp(content, "name", "call");
+ /* We are the intiator: send the predefined options. */
+ rexmpp_xml_add_attr(jingle, "action", "session-initiate");
+ rexmpp_xml_add_attr(jingle, "initiator", sess->s->assigned_jid.full);
+ rexmpp_xml_add_attr(content, "name", "call");
/* https://datatracker.ietf.org/doc/html/rfc4568 */
- xmlNodePtr encryption =
- rexmpp_xml_new_node("encryption", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(encryption, "required", "true");
- xmlAddChild(content, encryption);
- xmlNodePtr crypto = rexmpp_xml_new_node("crypto", "urn:xmpp:jingle:apps:rtp:1");
- xmlNewProp(crypto, "crypto-suite", "AES_CM_128_HMAC_SHA1_80");
- xmlNewProp(crypto, "tag", "1");
- xmlAddChild(encryption, crypto);
-
- description = xmlCopyNode(sess->s->jingle_rtp_description, 1);
+ rexmpp_xml_t *encryption =
+ rexmpp_xml_new_elem("encryption", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(encryption, "required", "true");
+ rexmpp_xml_add_child(content, encryption);
+ rexmpp_xml_t *crypto =
+ rexmpp_xml_new_elem("crypto", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_attr(crypto, "crypto-suite", "AES_CM_128_HMAC_SHA1_80");
+ rexmpp_xml_add_attr(crypto, "tag", "1");
+ rexmpp_xml_add_child(encryption, crypto);
+
+ description = rexmpp_xml_clone(sess->s->jingle_rtp_description);
} else {
- xmlNodePtr init_jingle = sess->initiate;
- xmlNodePtr init_content =
+ /* Accepting the call, will have to compare payload type options
+ to supported and preferred ones, and pick some from those. */
+ rexmpp_xml_t *init_jingle = sess->initiate;
+ rexmpp_xml_t *init_content =
rexmpp_xml_find_child(init_jingle, "urn:xmpp:jingle:1", "content");
- char *init_content_name = xmlGetProp(init_content, "name");
+ const char *init_content_name = rexmpp_xml_find_attr_val(init_content, "name");
if (init_content_name != NULL) {
- xmlNewProp(content, "name", init_content_name);
- free(init_content_name);
+ rexmpp_xml_add_attr(content, "name", init_content_name);
} else {
rexmpp_log(sess->s, LOG_ERR,
"Empty content name for Jingle session %s with %s",
sess->sid, sess->jid);
}
- xmlNewProp(jingle, "action", "session-accept");
- xmlNewProp(jingle, "initiator", sess->jid);
- xmlNewProp(jingle, "responder", sess->s->assigned_jid.full);
+ rexmpp_xml_add_attr(jingle, "action", "session-accept");
+ rexmpp_xml_add_attr(jingle, "initiator", sess->jid);
+ rexmpp_xml_add_attr(jingle, "responder", sess->s->assigned_jid.full);
- description = xmlCopyNode(sess->s->jingle_rtp_description, 2);
+ description = rexmpp_xml_clone(sess->s->jingle_rtp_description);
/* Find the first matching payload-type and add that */
- xmlNodePtr pl_type =
- xmlFirstElementChild(sess->s->jingle_rtp_description);
- xmlNodePtr selected_pl = NULL;
- while (pl_type != NULL && selected_pl == NULL) {
+ rexmpp_xml_t *pl_type =
+ rexmpp_xml_first_elem_child(sess->s->jingle_rtp_description);
+ rexmpp_xml_t *selected_pl = NULL;
+ while (pl_type != NULL) {
if (rexmpp_xml_match(pl_type, "urn:xmpp:jingle:apps:rtp:1", "payload-type")) {
- char *pl_id = xmlGetProp(pl_type, "id");
+ const char *pl_id = rexmpp_xml_find_attr_val(pl_type, "id");
if (pl_id != NULL) {
int pl_id_num = atoi(pl_id);
- xmlNodePtr proposed_pl_type =
- xmlFirstElementChild
+ rexmpp_xml_t *proposed_pl_type =
+ rexmpp_xml_first_elem_child
(rexmpp_xml_find_child
(rexmpp_xml_find_child(sess->initiate,
"urn:xmpp:jingle:1", "content"),
"urn:xmpp:jingle:apps:rtp:1", "description"));
while (proposed_pl_type != NULL && selected_pl == NULL) {
if (rexmpp_xml_match(proposed_pl_type, "urn:xmpp:jingle:apps:rtp:1", "payload-type")) {
- char *proposed_pl_id = xmlGetProp(proposed_pl_type, "id");
+ const char *proposed_pl_id = rexmpp_xml_find_attr_val(proposed_pl_type, "id");
if (proposed_pl_id != NULL) {
int proposed_pl_id_num = atoi(proposed_pl_id);
if (pl_id_num < 96 && pl_id_num == proposed_pl_id_num) {
selected_pl = pl_type;
} else {
- char *pl_name = xmlGetProp(pl_type, "name");
+ const char *pl_name =
+ rexmpp_xml_find_attr_val(pl_type, "name");
if (pl_name != NULL) {
- char *proposed_pl_name = xmlGetProp(proposed_pl_type, "name");
+ const char *proposed_pl_name =
+ rexmpp_xml_find_attr_val(proposed_pl_type, "name");
if (proposed_pl_name != NULL) {
if (strcmp(pl_name, proposed_pl_name) == 0) {
/* todo: compare clock rates, numbers of
channels, parameters */
selected_pl = pl_type;
}
- free(proposed_pl_name);
}
- free(pl_name);
}
}
- free(proposed_pl_id);
}
}
- proposed_pl_type = proposed_pl_type->next;
+ proposed_pl_type = rexmpp_xml_next_elem_sibling(proposed_pl_type);
}
- free(pl_id);
} else {
rexmpp_log(sess->s, LOG_ERR,
- "No 'id' specified for a pyaload-type element.");
+ "No 'id' specified for a payload-type element.");
}
}
pl_type = pl_type->next;
}
if (selected_pl != NULL) {
- xmlAddChild(description, xmlCopyNode(selected_pl, 1));
+ rexmpp_xml_add_child(description, rexmpp_xml_clone(selected_pl));
} else {
rexmpp_log(sess->s, LOG_ERR, "No suitable payload type found");
/* todo: fail if it's NULL, though it shouldn't happen, since
@@ -862,69 +1132,70 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
}
}
- xmlAddChild(jingle, content);
- xmlAddChild(content, description);
+ rexmpp_xml_add_child(jingle, content);
+ rexmpp_xml_add_child(content, description);
if (sess->rtcp_mux) {
- xmlNodePtr rtcp_mux =
- rexmpp_xml_new_node("rtcp-mux", "urn:xmpp:jingle:apps:rtp:1");
- xmlAddChild(description, rtcp_mux);
+ rexmpp_xml_t *rtcp_mux =
+ rexmpp_xml_new_elem("rtcp-mux", "urn:xmpp:jingle:apps:rtp:1");
+ rexmpp_xml_add_child(description, rtcp_mux);
}
- xmlNodePtr transport =
- rexmpp_xml_new_node("transport", "urn:xmpp:jingle:transports:ice-udp:1");
+ rexmpp_xml_t *transport =
+ rexmpp_xml_new_elem("transport", "urn:xmpp:jingle:transports:ice-udp:1");
gchar *ufrag = NULL;
gchar *password = NULL;
nice_agent_get_local_credentials(agent, stream_id, &ufrag, &password);
- xmlNewProp(transport, "ufrag", ufrag);
- xmlNewProp(transport, "pwd", password);
+ rexmpp_xml_add_attr(transport, "ufrag", ufrag);
+ rexmpp_xml_add_attr(transport, "pwd", password);
g_free(ufrag);
g_free(password);
- xmlAddChild(content, transport);
+ rexmpp_xml_add_child(content, transport);
int component_id;
- xmlNodePtr postponed_candidates = NULL;
+ rexmpp_xml_t *postponed_candidates = NULL;
for (component_id = 1; component_id <= (sess->rtcp_mux ? 1 : 2); component_id++) {
GSList *candidates = nice_agent_get_local_candidates(agent, stream_id, component_id);
GSList *cand_cur = candidates;
int cand_num = 0;
while (cand_cur != NULL) {
- xmlNodePtr candidate =
- rexmpp_xml_new_node("candidate", "urn:xmpp:jingle:transports:ice-udp:1");
+ rexmpp_xml_t *candidate =
+ rexmpp_xml_new_elem("candidate", "urn:xmpp:jingle:transports:ice-udp:1");
char buf[INET6_ADDRSTRLEN];
NiceCandidate *c = (NiceCandidate *)cand_cur->data;
snprintf(buf, 11, "%u", component_id);
- xmlNewProp(candidate, "component", buf);
- xmlNewProp(candidate, "foundation", c->foundation);
- xmlNewProp(candidate, "generation", "0");
- char *cid = rexmpp_gen_id(sess->s);
- xmlNewProp(candidate, "id", cid);
+ rexmpp_xml_add_attr(candidate, "component", buf);
+ rexmpp_xml_add_attr(candidate, "foundation", c->foundation);
+ rexmpp_xml_add_attr(candidate, "generation", "0");
+ char *cid = rexmpp_random_id();
+ rexmpp_xml_add_attr(candidate, "id", cid);
free(cid);
nice_address_to_string(&c->addr, buf);
- xmlNewProp(candidate, "ip", buf);
+ rexmpp_xml_add_attr(candidate, "ip", buf);
snprintf(buf, 11, "%u", nice_address_get_port(&c->addr));
- xmlNewProp(candidate, "port", buf);
- xmlNewProp(candidate, "network", "0");
- xmlNewProp(candidate, "protocol", "udp");
+ rexmpp_xml_add_attr(candidate, "port", buf);
+ rexmpp_xml_add_attr(candidate, "network", "0");
+ rexmpp_xml_add_attr(candidate, "protocol", "udp");
snprintf(buf, 11, "%u", c->priority);
- xmlNewProp(candidate, "priority", buf);
+ rexmpp_xml_add_attr(candidate, "priority", buf);
char *nice_type[] = {"host", "srflx", "prflx", "relay"};
if (c->type < 4) {
- xmlNewProp(candidate, "type", nice_type[c->type]);
+ rexmpp_xml_add_attr(candidate, "type", nice_type[c->type]);
}
/* Can't send too many candidates, since stanza sizes are usually
- limited, and then it breaks the stream. Limiting to 10 per
- component, sending the rest later, via transport-info. */
+ limited, and then it breaks the stream. Limiting to 10 per
+ component, sending the rest later, via transport-info. */
if (cand_num < 10) {
- xmlAddChild(transport, candidate);
+ rexmpp_xml_add_child(transport, candidate);
} else {
- xmlNodePtr jingle_ti = rexmpp_xml_new_node("jingle", "urn:xmpp:jingle:1");
- xmlNewProp(jingle_ti, "sid", sess->sid);
- xmlNewProp(jingle_ti, "action", "transport-info");
- xmlNodePtr content_copy = xmlCopyNode(content, 2);
- xmlNodePtr transport_copy = xmlCopyNode(transport, 2);
- xmlAddChild(jingle_ti, content_copy);
- xmlAddChild(content_copy, transport_copy);
- xmlAddChild(transport_copy, candidate);
+ rexmpp_xml_t *jingle_ti =
+ rexmpp_xml_new_elem("jingle", "urn:xmpp:jingle:1");
+ rexmpp_xml_add_attr(jingle_ti, "sid", sess->sid);
+ rexmpp_xml_add_attr(jingle_ti, "action", "transport-info");
+ rexmpp_xml_t *content_copy = rexmpp_xml_clone(content);
+ rexmpp_xml_t *transport_copy = rexmpp_xml_clone(transport);
+ rexmpp_xml_add_child(jingle_ti, content_copy);
+ rexmpp_xml_add_child(content_copy, transport_copy);
+ rexmpp_xml_add_child(transport_copy, candidate);
jingle_ti->next = postponed_candidates;
postponed_candidates = jingle_ti;
}
@@ -936,24 +1207,26 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
}
}
- xmlNodePtr fingerprint =
- rexmpp_xml_new_node("fingerprint", "urn:xmpp:jingle:apps:dtls:0");
- xmlNewProp(fingerprint, "hash", "sha-256");
+ rexmpp_xml_t *fingerprint =
+ rexmpp_xml_new_elem("fingerprint", "urn:xmpp:jingle:apps:dtls:0");
+ rexmpp_xml_add_attr(fingerprint, "hash", "sha-256");
if (sess->initiator) {
- xmlNewProp(fingerprint, "setup", "actpass");
+ rexmpp_xml_add_attr(fingerprint, "setup", "actpass");
} else if (rexmpp_jingle_dtls_is_active(sess, 1)) {
- xmlNewProp(fingerprint, "setup", "active");
+ rexmpp_xml_add_attr(fingerprint, "setup", "active");
} else {
- xmlNewProp(fingerprint, "setup", "passive");
+ rexmpp_xml_add_attr(fingerprint, "setup", "passive");
}
- xmlNodeAddContent(fingerprint, fp_str);
- xmlAddChild(transport, fingerprint);
+ rexmpp_xml_add_text(fingerprint, fp_str);
+ rexmpp_xml_add_child(transport, fingerprint);
if (sess->initiator) {
- sess->initiate = xmlCopyNode(jingle, 1);
+ sess->initiate = rexmpp_xml_clone(jingle);
} else {
- sess->accept = xmlCopyNode(jingle, 1);
+ sess->accept = rexmpp_xml_clone(jingle);
+ rexmpp_jingle_session_configure_audio(sess);
+ rexmpp_jingle_run_audio(sess);
}
rexmpp_iq_new(sess->s, "set", sess->jid, jingle,
@@ -962,7 +1235,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
/* Now send transport-info messages with candidates that didn't fit
initially. */
while (postponed_candidates != NULL) {
- xmlNodePtr pc_next = postponed_candidates->next;
+ rexmpp_xml_t *pc_next = postponed_candidates->next;
postponed_candidates->next = NULL;
rexmpp_iq_new(sess->s, "set", sess->jid, postponed_candidates,
rexmpp_transport_info_call_cb, strdup(sess->sid));
@@ -971,7 +1244,7 @@ rexmpp_jingle_candidate_gathering_done_cb (NiceAgent *agent,
}
ssize_t
-rexmpp_jingle_dtls_push_func (gnutls_transport_ptr_t p, const void *data, size_t size)
+rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size)
{
rexmpp_jingle_component_t *comp = p;
rexmpp_jingle_session_t *sess = comp->session;
@@ -979,58 +1252,21 @@ rexmpp_jingle_dtls_push_func (gnutls_transport_ptr_t p, const void *data, size_t
comp->component_id, size, data);
}
-ssize_t rexmpp_jingle_dtls_generic_pull_func (rexmpp_jingle_session_t *sess,
- char *tls_buf,
- size_t *tls_buf_len,
- gnutls_session_t tls_session,
- void *data,
- size_t size)
-{
- (void)sess;
- size_t ret = -1;
- if (*tls_buf_len > 0) {
- if (size >= *tls_buf_len) {
- memcpy(data, tls_buf, *tls_buf_len);
- ret = *tls_buf_len;
- *tls_buf_len = 0;
- } else {
- if (size > DTLS_SRTP_BUF_SIZE) {
- size = DTLS_SRTP_BUF_SIZE;
- }
- memcpy(data, tls_buf, size);
- memmove(tls_buf, tls_buf + size, DTLS_SRTP_BUF_SIZE - size);
- ret = size;
- *tls_buf_len = *tls_buf_len - size;
- }
- } else {
- gnutls_transport_set_errno(tls_session, EAGAIN);
- ret = -1;
- }
-
- return ret;
+rexmpp_tls_t *rexmpp_jingle_component_dtls(void *p) {
+ rexmpp_jingle_component_t *comp = p;
+ return comp->dtls;
}
-ssize_t
-rexmpp_jingle_dtls_pull_func (gnutls_transport_ptr_t p,
- void *data,
- size_t size)
+/* The timeout is always zero for DTLS. */
+int rexmpp_jingle_dtls_pull_timeout_func (void *p,
+ unsigned int ms)
{
rexmpp_jingle_component_t *comp = p;
rexmpp_jingle_session_t *sess = comp->session;
- return
- rexmpp_jingle_dtls_generic_pull_func(sess,
- comp->dtls_buf,
- &comp->dtls_buf_len,
- comp->dtls_session,
- data,
- size);
-}
+ /* if (comp->dtls->dtls_buf_len > 0) { */
+ /* return comp->dtls->dtls_buf_len; */
+ /* } */
-int
-rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess,
- unsigned int ms,
- guint component_id)
-{
fd_set rfds;
struct timeval tv;
@@ -1040,38 +1276,72 @@ rexmpp_jingle_dtls_generic_pull_timeout_func (rexmpp_jingle_session_t *sess,
char c;
FD_ZERO(&rfds);
-
- GSocket *sock =
- nice_agent_get_selected_socket(sess->ice_agent,
- sess->ice_stream_id, component_id);
- int fd = g_socket_get_fd(sock);
- FD_SET(fd, &rfds);
+ int fd, nfds = -1;
+
+ GPtrArray *sockets =
+ nice_agent_get_sockets(sess->ice_agent,
+ sess->ice_stream_id, comp->component_id);
+ guint i;
+ for (i = 0; i < sockets->len; i++) {
+ fd = g_socket_get_fd(sockets->pdata[i]);
+ FD_SET(fd, &rfds);
+ if (fd > nfds) {
+ nfds = fd;
+ }
+ }
+ g_ptr_array_unref(sockets);
tv.tv_sec = ms / 1000;
tv.tv_usec = (ms % 1000) * 1000;
- ret = select(fd + 1, &rfds, NULL, NULL, &tv);
- if (ret <= 0) {
+ ret = select(nfds + 1, &rfds, NULL, NULL, &tv);
+ if (ret < 0) {
+ rexmpp_log(sess->s, LOG_WARNING,
+ "DTLS pull function: select failed: %s",
+ strerror(errno));
return ret;
}
cli_addr_size = sizeof(cli_addr);
- ret =
- recvfrom(fd, &c, 1, MSG_PEEK,
- (struct sockaddr *) &cli_addr, &cli_addr_size);
- if (ret > 0) {
- return 1;
+ ret = 0;
+ sockets =
+ nice_agent_get_sockets(sess->ice_agent,
+ sess->ice_stream_id, comp->component_id);
+ for (i = 0; i < sockets->len; i++) {
+ int err =
+ recvfrom(g_socket_get_fd(sockets->pdata[i]), &c, 1, MSG_PEEK,
+ (struct sockaddr *) &cli_addr, &cli_addr_size);
+ if (err == -1) {
+ /* ENOTCONN and EAGAIN are common here, but report other
+ errors. */
+ if (errno != ENOTCONN && errno != EAGAIN) {
+ rexmpp_log(sess->s, LOG_WARNING,
+ "DTLS pull function: failed to peek a socket: %s",
+ strerror(errno));
+ }
+ } else {
+ ret += err;
+ }
}
+ g_ptr_array_unref(sockets);
+ if (ret > 0) {
+ return ret;
+ }
return 0;
}
-int rexmpp_jingle_dtls_pull_timeout_func (gnutls_transport_ptr_t p,
- unsigned int ms)
-{
- rexmpp_jingle_component_t *comp = p;
- return rexmpp_jingle_dtls_generic_pull_timeout_func(comp->session, ms,
- comp->component_id);
+const char *rexmpp_ice_component_state_text(int state) {
+ switch (state) {
+ case NICE_COMPONENT_STATE_DISCONNECTED: return "disconnected";
+ case NICE_COMPONENT_STATE_GATHERING: return "gathering";
+ case NICE_COMPONENT_STATE_CONNECTING: return "connecting";
+ case NICE_COMPONENT_STATE_CONNECTED: return "connected";
+ case NICE_COMPONENT_STATE_READY: return "ready";
+ case NICE_COMPONENT_STATE_FAILED: return "failed";
+ case NICE_COMPONENT_STATE_LAST: return "last";
+ default: return "unknown";
+ }
}
void
@@ -1083,6 +1353,9 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent,
{
rexmpp_jingle_session_t *sess = data;
(void)agent;
+ rexmpp_log(sess->s, LOG_DEBUG,
+ "ICE agent component %d state for stream %d changed to %s",
+ component_id, stream_id, rexmpp_ice_component_state_text(state));
if (component_id < 1 || component_id > 2) {
rexmpp_log(sess->s, LOG_CRIT, "Unexpected ICE component_id: %d",
component_id);
@@ -1101,30 +1374,13 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent,
return;
}
- int active_role = rexmpp_jingle_dtls_is_active(sess, 0);
-
- gnutls_session_t *tls_session = &sess->component[component_id - 1].dtls_session;
- gnutls_init(tls_session,
- (active_role ? GNUTLS_CLIENT : GNUTLS_SERVER) |
- GNUTLS_DATAGRAM |
- GNUTLS_NONBLOCK);
- if (! active_role) {
- gnutls_certificate_server_set_request(*tls_session, GNUTLS_CERT_REQUEST);
- }
- gnutls_set_default_priority(*tls_session);
- gnutls_credentials_set(*tls_session, GNUTLS_CRD_CERTIFICATE,
- sess->s->jingle.dtls_cred);
-
- gnutls_transport_set_ptr(*tls_session, &sess->component[component_id - 1]);
- gnutls_transport_set_push_function(*tls_session, rexmpp_jingle_dtls_push_func);
- gnutls_transport_set_pull_function(*tls_session, rexmpp_jingle_dtls_pull_func);
- gnutls_transport_set_pull_timeout_function(*tls_session,
- rexmpp_jingle_dtls_pull_timeout_func);
+ rexmpp_jingle_component_t *comp = &(sess->component[component_id - 1]);
+ rexmpp_dtls_connect(sess->s,
+ comp->dtls,
+ comp,
+ rexmpp_jingle_dtls_is_active(sess, 0));
sess->component[component_id - 1].dtls_state = REXMPP_TLS_HANDSHAKE;
- /* todo: use the profile/crypto-suite from <crypto/> element */
- gnutls_srtp_set_profile(*tls_session, GNUTLS_SRTP_AES128_CM_HMAC_SHA1_80);
- gnutls_handshake(*tls_session);
-
+ rexmpp_tls_handshake(sess->s, comp->dtls);
} else if (state == NICE_COMPONENT_STATE_FAILED) {
rexmpp_log(sess->s, LOG_ERR,
"ICE connection failed for Jingle session %s, ICE stream %d, component %d",
@@ -1136,19 +1392,20 @@ rexmpp_jingle_component_state_changed_cb (NiceAgent *agent,
void
rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id,
- guint len, gchar *buf, gpointer data)
+ guint len, gchar *gbuf, gpointer data)
{
/* Demultiplexing here for DTLS and SRTP:
https://datatracker.ietf.org/doc/html/rfc5764#section-5.1.2 */
(void)agent;
(void)stream_id;
(void)component_id;
+ uint8_t *buf = (uint8_t *)gbuf;
rexmpp_jingle_component_t *comp = data;
if (len == 0) {
rexmpp_log(comp->s, LOG_WARNING, "Received an empty ICE message");
return;
}
- if (127 < (uint8_t)buf[0] && (uint8_t)buf[0] < 192) {
+ if (127 < buf[0] && buf[0] < 192) {
int err;
srtp_ctx_t *srtp_in;
if (comp->dtls_state == REXMPP_TLS_ACTIVE) {
@@ -1162,43 +1419,178 @@ rexmpp_jingle_ice_recv_cb (NiceAgent *agent, guint stream_id, guint component_id
"Received an SRTP packet while DTLS is inactive");
return;
}
- uint16_t port_out = comp->udp_port_out;
+ if (srtp_in == NULL) {
+ rexmpp_log(comp->s, LOG_WARNING,
+ "Received an SRTP packet while SRTP is not set up");
+ return;
+ }
if (component_id == 1) {
- err = srtp_unprotect(srtp_in, buf, &len);
+ err = srtp_unprotect(srtp_in, buf, (int*)&len);
if (err == srtp_err_status_auth_fail && comp->session->rtcp_mux) {
/* Try to demultiplex. Maybe there's a better way to do it,
but this will do for now. */
- err = srtp_unprotect_rtcp(srtp_in, buf, &len);
- port_out = comp->session->component[0].udp_port_out;
+ err = srtp_unprotect_rtcp(srtp_in, buf, (int*)&len);
}
} else {
- err = srtp_unprotect_rtcp(srtp_in, buf, &len);
+ err = srtp_unprotect_rtcp(srtp_in, buf, (int*)&len);
}
if (err) {
rexmpp_log(comp->s, LOG_ERR, "SRT(C)P unprotect error %d on component %d",
err, component_id);
} else {
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- addr.sin_port = htons(port_out);
- sendto(comp->udp_socket, buf, len, 0,
- (struct sockaddr*)&addr, sizeof(struct sockaddr_in));
+ /* TODO: apparently sometimes there is more than one RTCP packet
+ in the decrypted data; parse them all. */
+ uint8_t version = (buf[0] & 0xc0) >> 6;
+ if (version == 2 && len >= 12) {
+ size_t i;
+ uint8_t payload_type = buf[1] & 0x7f;
+ uint8_t csrc_count = buf[0] & 0xF;
+ uint16_t seq_num = ((uint16_t)buf[2] << 8) | buf[3];
+ int data_start_pos = 12 + csrc_count * 4;
+
+ /* Exclude possible RTCP packets here, RFC 5761 */
+ if (component_id == 1
+ && (payload_type < 64 || payload_type > 80)) {
+ if ((seq_num > comp->session->rtp_last_seq_num)
+ || (comp->session->rtp_last_seq_num > 65500)) {
+ if ((comp->session->rtp_last_seq_num <= 65500)
+ && (seq_num - comp->session->rtp_last_seq_num > 1)) {
+ rexmpp_log(comp->s, LOG_NOTICE,
+ "%d RTP packets lost",
+ seq_num - comp->session->rtp_last_seq_num - 1);
+ }
+ struct ring_buf *playback = &(comp->session->ring_buffers.playback);
+
+ /* Skip the RTP header. */
+ if (payload_type == 0) {
+ /* PCMU */
+ for (i = data_start_pos; i < len; i++) {
+ playback->buf[playback->write_pos] = rexmpp_pcmu_decode(buf[i]);
+ playback->write_pos++;
+ playback->write_pos %= PA_BUF_SIZE;
+ }
+ } else if (payload_type == 8) {
+ /* PCMA */
+ for (i = data_start_pos; i < len; i++) {
+ playback->buf[playback->write_pos] = rexmpp_pcma_decode(buf[i]);
+ playback->write_pos++;
+ playback->write_pos %= PA_BUF_SIZE;
+ }
+ }
+#ifdef HAVE_OPUS
+ else if (payload_type == comp->session->payload_type
+ && comp->session->codec == REXMPP_CODEC_OPUS) {
+ /* The same payload type as for output, which is Opus */
+ opus_int16 decoded[5760 * 2];
+ int decoded_len;
+ decoded_len =
+ opus_decode(comp->session->opus_dec,
+ (const unsigned char *)buf + data_start_pos,
+ len - data_start_pos,
+ decoded,
+ 5760,
+ 0);
+ int j;
+ for (j = 0; j < decoded_len; j++) {
+ playback->buf[playback->write_pos] = decoded[j];
+ playback->write_pos++;
+ playback->write_pos %= PA_BUF_SIZE;
+ }
+ }
+#endif /* HAVE_OPUS */
+ else {
+ /* Some other payload type, possibly with a dynamic ID */
+ rexmpp_xml_t *payload =
+ rexmpp_jingle_session_payload_by_id(comp->session,
+ payload_type);
+ const char *pl_name = rexmpp_xml_find_attr_val(payload, "name");
+ rexmpp_log(comp->s, LOG_WARNING,
+ "Unhandled payload type %d, '%s'",
+ payload_type, pl_name);
+ }
+ comp->session->rtp_last_seq_num = seq_num;
+ } else {
+ rexmpp_log(comp->s, LOG_NOTICE,
+ "Out of order RTP packets received: %d after %d",
+ seq_num, comp->session->rtp_last_seq_num);
+ }
+ } else {
+ uint8_t packet_type = buf[1];
+ unsigned int pos = 0;
+ unsigned int rtcp_len = (buf[2] << 8) | buf[3];
+ if (packet_type == 200 && len >= 28) {
+ uint32_t rtcp_ssrc =
+ (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ uint64_t rtcp_ntp_timestamp = 0;
+ for (i = 8; i < 16; i++) {
+ rtcp_ntp_timestamp <<= 8;
+ rtcp_ntp_timestamp |= buf[i];
+ }
+ uint32_t rtcp_rtp_timestamp =
+ (buf[16] << 24) | (buf[17] << 16) | (buf[18] << 8) | buf[19];
+ uint32_t rtcp_packet_count =
+ (buf[20] << 24) | (buf[21] << 16) | (buf[22] << 8) | buf[23];
+ uint32_t rtcp_octet_count =
+ (buf[24] << 24) | (buf[25] << 16) | (buf[26] << 8) | buf[27];
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "RTCP SR received: SSRC % " PRIx32 ", NTP TS %" PRIu64
+ ", RTP TS %" PRIu32 ", PC %" PRIu32 ", OC %" PRIu32,
+ rtcp_ssrc, rtcp_ntp_timestamp, rtcp_rtp_timestamp,
+ rtcp_packet_count, rtcp_octet_count);
+ pos = 28;
+ } else if (packet_type == 201 && len >= 8) {
+ uint32_t rtcp_ssrc =
+ (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "RTCP RR received: SSRC %x",
+ rtcp_ssrc);
+ pos = 8;
+ } else {
+ rexmpp_log(comp->s, LOG_DEBUG, "RTCP packet received, PT %d",
+ packet_type);
+ }
+
+ while ((pos > 0)
+ && ((pos + 24) <= len)
+ && ((pos + 24) <= (rtcp_len + 1) * 4)) {
+ uint32_t rtcp_ssrc = (buf[pos] << 24) | (buf[pos + 1] << 16)
+ | (buf[pos + 2] << 8) | buf[pos + 3];
+ uint8_t fraction_lost = buf[pos + 4];
+ uint32_t packets_lost =
+ (buf[pos + 5] << 24) | (buf[pos + 6] << 16) | buf[pos + 7];
+ uint32_t rtcp_ehsn = (buf[pos + 8] << 24) | (buf[pos + 9] << 16)
+ | (buf[pos + 10] << 8) | buf[pos + 11];
+ uint32_t rtcp_jitter = (buf[pos + 12] << 24) | (buf[pos + 13] << 16)
+ | (buf[pos + 14] << 8) | buf[pos + 15];
+ uint32_t rtcp_lsr = (buf[pos + 16] << 24) | (buf[pos + 17] << 16)
+ | (buf[pos + 18] << 8) | buf[pos + 19];
+ uint32_t rtcp_dlsr = (buf[pos + 20] << 24) | (buf[pos + 21] << 16)
+ | (buf[pos + 22] << 8) | buf[pos + 23];
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "RTCP report block: SSRC % " PRIx32 ", lost %" PRIu32
+ "%, %" PRIu8 " packets, "
+ "highest seq num %" PRIu32
+ ", jitter %" PRIu32 ", LSR %" PRIu32 ", DLSR %" PRIu32,
+ rtcp_ssrc, fraction_lost, packets_lost,
+ rtcp_ehsn, rtcp_jitter, rtcp_lsr, rtcp_dlsr);
+ pos += 24;
+ }
+ }
+ } else {
+ rexmpp_log(comp->s, LOG_WARNING,
+ "Unhandled RT(C)P packet: version %d, length %d",
+ version, len);
+ }
}
} else {
- if (comp->dtls_buf_len + len < DTLS_SRTP_BUF_SIZE) {
- memcpy(comp->dtls_buf + comp->dtls_buf_len, buf, len);
- comp->dtls_buf_len += len;
- } else {
- rexmpp_log(comp->s, LOG_WARNING, "Dropping a DTLS packet");
- }
+ rexmpp_dtls_feed(comp->s, comp->dtls, buf, len);
}
}
int
rexmpp_jingle_ice_agent_init (rexmpp_jingle_session_t *sess)
{
- sess->ice_agent = nice_agent_new(g_main_loop_get_context (sess->s->jingle.gloop),
+ sess->ice_agent = nice_agent_new(g_main_loop_get_context (sess->s->jingle->gloop),
NICE_COMPATIBILITY_RFC5245);
if (sess->s->local_address != NULL) {
NiceAddress *address = nice_address_new();
@@ -1223,7 +1615,7 @@ rexmpp_jingle_ice_agent_init (rexmpp_jingle_session_t *sess)
int i;
for (i = 0; i < (sess->rtcp_mux ? 1 : 2); i++) {
nice_agent_attach_recv(sess->ice_agent, sess->ice_stream_id, i + 1,
- g_main_loop_get_context (sess->s->jingle.gloop),
+ g_main_loop_get_context (sess->s->jingle->gloop),
rexmpp_jingle_ice_recv_cb,
&sess->component[i]);
}
@@ -1231,29 +1623,6 @@ rexmpp_jingle_ice_agent_init (rexmpp_jingle_session_t *sess)
return 1;
}
-void
-rexmpp_jingle_bind_sockets (rexmpp_jingle_session_t *sess,
- uint16_t rtp_port_in, uint16_t rtp_port_out)
-{
- sess->component[0].udp_port_in = rtp_port_in;
- sess->component[0].udp_port_out = rtp_port_out;
- sess->component[1].udp_port_in = rtp_port_in + 1;
- sess->component[1].udp_port_out = rtp_port_out + 1;
- int i;
- for (i = 0; i < 2; i++) {
- sess->component[i].udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- addr.sin_port = htons(sess->component[i].udp_port_in);
- if (bind (sess->component[i].udp_socket,
- (struct sockaddr*)&addr, sizeof(struct sockaddr_in))) {
- rexmpp_log(sess->s, LOG_ERR, "Failed to bind a UDP socket on port %u",
- sess->component[i].udp_port_in);
- }
- }
-}
-
void rexmpp_jingle_turn_dns_cb (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *result) {
rexmpp_jingle_session_t *sess = ptr;
if (result != NULL && result->data != NULL) {
@@ -1306,25 +1675,25 @@ void rexmpp_jingle_stun_dns_cb (rexmpp_t *s, void *ptr, rexmpp_dns_result_t *res
void rexmpp_jingle_turn_cb (rexmpp_t *s,
void *sess_ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)req;
rexmpp_jingle_session_t *sess = sess_ptr;
if (success) {
/* use credentials */
- xmlNodePtr services = xmlFirstElementChild(response);
+ rexmpp_xml_t *services = rexmpp_xml_first_elem_child(response);
if (rexmpp_xml_match(services, "urn:xmpp:extdisco:2", "services")) {
- xmlNodePtr service = xmlFirstElementChild(services);
+ rexmpp_xml_t *service = rexmpp_xml_first_elem_child(services);
while (service != NULL) {
if (rexmpp_xml_match(service, "urn:xmpp:extdisco:2", "service")) {
- char *type = xmlGetProp(service, "type");
- char *transport = xmlGetProp(service, "transport");
- char *host = xmlGetProp(service, "host");
- char *port = xmlGetProp(service, "port");
- char *username = xmlGetProp(service, "username");
- char *password = xmlGetProp(service, "password");
+ const char *type = rexmpp_xml_find_attr_val(service, "type");
+ const char *transport = rexmpp_xml_find_attr_val(service, "transport");
+ const char *host = rexmpp_xml_find_attr_val(service, "host");
+ const char *port = rexmpp_xml_find_attr_val(service, "port");
+ const char *username = rexmpp_xml_find_attr_val(service, "username");
+ const char *password = rexmpp_xml_find_attr_val(service, "password");
if (sess->stun_host == NULL &&
type != NULL && transport != NULL && host != NULL && port != NULL &&
@@ -1344,27 +1713,8 @@ void rexmpp_jingle_turn_cb (rexmpp_t *s,
sess->turn_password = strdup(password);
rexmpp_log(s, LOG_DEBUG, "Setting TURN server to %s:%s", host, port);
}
-
- if (type != NULL) {
- free(type);
- }
- if (transport != NULL) {
- free(transport);
- }
- if (host != NULL) {
- free(host);
- }
- if (port != NULL) {
- free(port);
- }
- if (username != NULL) {
- free(username);
- }
- if (password != NULL) {
- free(password);
- }
}
- service = service->next;
+ service = rexmpp_xml_next_elem_sibling(service);
}
if (sess->stun_host != NULL) {
/* Resolve, then resolve STUN host, then connect. */
@@ -1392,25 +1742,24 @@ void rexmpp_jingle_turn_cb (rexmpp_t *s,
void rexmpp_jingle_discover_turn_cb (rexmpp_t *s,
void *sess_ptr,
- xmlNodePtr req,
- xmlNodePtr response,
+ rexmpp_xml_t *req,
+ rexmpp_xml_t *response,
int success)
{
(void)req;
- char *response_from = xmlGetProp(response, "from");
+ const char *response_from = rexmpp_xml_find_attr_val(response, "from");
rexmpp_jingle_session_t *sess = sess_ptr;
if (success) {
- xmlNodePtr services = rexmpp_xml_new_node("services", "urn:xmpp:extdisco:2");
- xmlNewProp(services, "type", "turn");
- rexmpp_iq_new(s, "get", response_from, services, rexmpp_jingle_turn_cb, sess_ptr);
+ rexmpp_xml_t *services =
+ rexmpp_xml_new_elem("services", "urn:xmpp:extdisco:2");
+ rexmpp_xml_add_attr(services, "type", "turn");
+ rexmpp_iq_new(s, "get", response_from, services,
+ rexmpp_jingle_turn_cb, sess_ptr);
} else {
rexmpp_log(s, LOG_DEBUG,
"No external service discovery, trying to connect without STUN/TURN");
nice_agent_gather_candidates(sess->ice_agent, sess->ice_stream_id);
}
- if (response_from != NULL) {
- free(response_from);
- }
}
void rexmpp_jingle_discover_turn (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
@@ -1420,37 +1769,36 @@ void rexmpp_jingle_discover_turn (rexmpp_t *s, rexmpp_jingle_session_t *sess) {
rexmpp_err_t
rexmpp_jingle_call (rexmpp_t *s,
- const char *jid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *jid)
{
rexmpp_jingle_session_t *sess =
- rexmpp_jingle_session_create(s, strdup(jid), rexmpp_gen_id(s),
+ rexmpp_jingle_session_create(s, strdup(jid), rexmpp_random_id(),
REXMPP_JINGLE_SESSION_MEDIA, 1);
- rexmpp_jingle_ice_agent_init(sess);
- rexmpp_jingle_bind_sockets(sess, rtp_port_in, rtp_port_out);
- rexmpp_jingle_discover_turn(s, sess);
- return REXMPP_SUCCESS;
+ if (sess != NULL) {
+ rexmpp_jingle_ice_agent_init(sess);
+ rexmpp_jingle_discover_turn(s, sess);
+ return REXMPP_SUCCESS;
+ } else {
+ rexmpp_log(s, LOG_ERR, "Failed to create a Jingle session for a call");
+ return REXMPP_E_OTHER;
+ }
}
rexmpp_err_t
rexmpp_jingle_call_accept (rexmpp_t *s,
- const char *sid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *sid)
{
rexmpp_jingle_session_t *sess = rexmpp_jingle_session_by_id(s, sid);
if (sess == NULL) {
return REXMPP_E_OTHER;
}
rexmpp_jingle_ice_agent_init(sess);
- rexmpp_jingle_bind_sockets(sess, rtp_port_in, rtp_port_out);
- xmlNodePtr content =
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(sess->initiate,
"urn:xmpp:jingle:1",
"content");
- xmlNodePtr ice_udp_transport =
+ rexmpp_xml_t * ice_udp_transport =
rexmpp_xml_find_child(content,
"urn:xmpp:jingle:transports:ice-udp:1",
"transport");
@@ -1458,7 +1806,7 @@ rexmpp_jingle_call_accept (rexmpp_t *s,
rexmpp_log(s, LOG_ERR, "No ICE-UDP transport defined for session %s", sid);
rexmpp_jingle_session_terminate
(s, sid,
- rexmpp_xml_new_node("unsupported-transports", "urn:xmpp:jingle:1"),
+ rexmpp_xml_new_elem("unsupported-transports", "urn:xmpp:jingle:1"),
"No ICE-UDP transport defined");
return REXMPP_E_OTHER;
}
@@ -1466,71 +1814,74 @@ rexmpp_jingle_call_accept (rexmpp_t *s,
rexmpp_jingle_discover_turn(s, sess);
return REXMPP_SUCCESS;
}
-#else
+#else /* ENABLE_CALLS */
+
+ssize_t
+rexmpp_jingle_dtls_push_func (void *p, const void *data, size_t size)
+{
+ (void)p;
+ (void)data;
+ (void)size;
+ return -1;
+}
rexmpp_err_t
rexmpp_jingle_call (rexmpp_t *s,
- const char *jid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *jid)
{
(void)jid;
- (void)rtp_port_in;
- (void)rtp_port_out;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without support for media calls");
return REXMPP_E_OTHER;
}
rexmpp_err_t
rexmpp_jingle_call_accept (rexmpp_t *s,
- const char *sid,
- uint16_t rtp_port_in,
- uint16_t rtp_port_out)
+ const char *sid)
{
(void)sid;
- (void)rtp_port_in;
- (void)rtp_port_out;
rexmpp_log(s, LOG_ERR, "rexmpp is compiled without support for media calls");
return REXMPP_E_OTHER;
}
-#endif
+#endif /* ENABLE_CALLS */
-int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
+int rexmpp_jingle_iq (rexmpp_t *s, rexmpp_xml_t *elem) {
int handled = 0;
if (! s->enable_jingle) {
return handled;
}
- xmlNodePtr jingle = rexmpp_xml_find_child(elem, "urn:xmpp:jingle:1", "jingle");
+ rexmpp_xml_t *jingle =
+ rexmpp_xml_find_child(elem, "urn:xmpp:jingle:1", "jingle");
if (jingle != NULL) {
handled = 1;
- char *action = xmlGetProp(jingle, "action");
- char *sid = xmlGetProp(jingle, "sid");
- char *from_jid = xmlGetProp(elem, "from");
+ const char *action = rexmpp_xml_find_attr_val(jingle, "action");
+ const char *sid = rexmpp_xml_find_attr_val(jingle, "sid");
+ const char *from_jid = rexmpp_xml_find_attr_val(elem, "from");
if (action != NULL && sid != NULL && from_jid != NULL) {
if (strcmp(action, "session-initiate") == 0) {
/* todo: could be more than one content element, handle that */
- xmlNodePtr content =
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
if (content == NULL) {
- rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request"));
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "bad-request"));
} else {
rexmpp_iq_reply(s, elem, "result", NULL);
- xmlNodePtr file_description =
+ rexmpp_xml_t *file_description =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:file-transfer:5",
"description");
- xmlNodePtr ibb_transport =
+ rexmpp_xml_t *ibb_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ibb:1",
"transport");
- xmlNodePtr ice_udp_transport =
+ rexmpp_xml_t *ice_udp_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1",
"transport");
- xmlNodePtr rtp_description =
+ rexmpp_xml_t *rtp_description =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:rtp:1",
"description");
if (file_description != NULL && ibb_transport != NULL) {
- char *ibb_sid = xmlGetProp(ibb_transport, "sid");
+ const char *ibb_sid = rexmpp_xml_find_attr_val(ibb_transport, "sid");
if (ibb_sid != NULL) {
rexmpp_log(s, LOG_DEBUG,
"Jingle session-initiate from %s, sid %s, ibb sid %s",
@@ -1539,19 +1890,21 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_jingle_session_create(s, strdup(from_jid), strdup(sid),
REXMPP_JINGLE_SESSION_FILE, 0);
if (sess != NULL) {
- sess->initiate = xmlCopyNode(jingle, 1);
- sess->ibb_sid = ibb_sid;
+ sess->initiate = rexmpp_xml_clone(jingle);
+ sess->ibb_sid = strdup(ibb_sid);
} else {
- rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("failed-transport",
- "urn:xmpp:jingle:1"),
- NULL);
+ rexmpp_jingle_session_terminate
+ (s, sid,
+ rexmpp_xml_new_elem("failed-transport",
+ "urn:xmpp:jingle:1"),
+ NULL);
}
} else {
- rexmpp_log(s, LOG_ERR, "Jingle IBB transport doesn't have a sid attribute");
+ rexmpp_log(s, LOG_ERR,
+ "Jingle IBB transport doesn't have a sid attribute");
rexmpp_jingle_session_terminate
(s, sid,
- rexmpp_xml_new_node("unsupported-transports",
+ rexmpp_xml_new_elem("unsupported-transports",
"urn:xmpp:jingle:1"),
NULL);
}
@@ -1562,25 +1915,28 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_jingle_session_t *sess =
rexmpp_jingle_session_create(s, strdup(from_jid), strdup(sid),
REXMPP_JINGLE_SESSION_MEDIA, 0);
- sess->rtcp_mux =
- (rexmpp_xml_find_child(rtp_description,
- "urn:xmpp:jingle:apps:rtp:1",
- "rtcp-mux") != NULL);
- sess->initiate = xmlCopyNode(jingle, 1);
-#endif
+ if (sess != NULL) {
+ sess->rtcp_mux =
+ (rexmpp_xml_find_child(rtp_description,
+ "urn:xmpp:jingle:apps:rtp:1",
+ "rtcp-mux") != NULL);
+ sess->initiate = rexmpp_xml_clone(jingle);
+ }
+#endif /* ENABLE_CALLS */
} else if (file_description == NULL &&
rtp_description == NULL) {
rexmpp_jingle_session_terminate
(s, sid,
- rexmpp_xml_new_node("unsupported-applications",
+ rexmpp_xml_new_elem("unsupported-applications",
"urn:xmpp:jingle:1"),
NULL);
} else if (ibb_transport == NULL &&
ice_udp_transport == NULL) {
- rexmpp_jingle_session_terminate(s, sid,
- rexmpp_xml_new_node("unsupported-transports",
- "urn:xmpp:jingle:1"),
- NULL);
+ rexmpp_jingle_session_terminate
+ (s, sid,
+ rexmpp_xml_new_elem("unsupported-transports",
+ "urn:xmpp:jingle:1"),
+ NULL);
} else {
/* todo: some other error */
}
@@ -1593,107 +1949,106 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
rexmpp_iq_reply(s, elem, "result", NULL);
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid);
if (session != NULL) {
- session->accept = xmlCopyNode(jingle, 1);
- xmlNodePtr content =
+ session->accept = rexmpp_xml_clone(jingle);
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
- xmlNodePtr file_description =
+ rexmpp_xml_t *file_description =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:apps:file-transfer:5",
"description");
- xmlNodePtr ibb_transport =
+ rexmpp_xml_t *ibb_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ibb:1",
"transport");
if (ibb_transport != NULL && file_description != NULL) {
- xmlNodePtr open =
- rexmpp_xml_new_node("open", "http://jabber.org/protocol/ibb");
- xmlNewProp(open, "sid", session->ibb_sid);
- xmlNewProp(open, "block-size", "4096");
- xmlNewProp(open, "stanza", "iq");
+ rexmpp_xml_t *open =
+ rexmpp_xml_new_elem("open", "http://jabber.org/protocol/ibb");
+ rexmpp_xml_add_attr(open, "sid", session->ibb_sid);
+ rexmpp_xml_add_attr(open, "block-size", "4096");
+ rexmpp_xml_add_attr(open, "stanza", "iq");
rexmpp_iq_new(s, "set", session->jid, open,
rexmpp_jingle_ibb_send_cb, strdup(sid));
} else {
#ifdef ENABLE_CALLS
- xmlNodePtr ice_udp_transport =
+ rexmpp_jingle_session_configure_audio(session);
+ rexmpp_jingle_run_audio(session);
+ rexmpp_xml_t *ice_udp_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1",
"transport");
if (ice_udp_transport != NULL) {
rexmpp_jingle_ice_udp_add_remote(session, ice_udp_transport);
+ } else {
+ rexmpp_log(s, LOG_WARNING,
+ "ICE-UDP transport is unset in session-accept");
}
-#endif
+#endif /* ENABLE_CALLS */
}
+ } else {
+ rexmpp_log(s, LOG_WARNING, "Jingle session %s is not found", sid);
}
} else if (strcmp(action, "transport-info") == 0) {
rexmpp_iq_reply(s, elem, "result", NULL);
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_id(s, sid);
if (session != NULL) {
#ifdef ENABLE_CALLS
- xmlNodePtr content =
+ rexmpp_xml_t *content =
rexmpp_xml_find_child(jingle, "urn:xmpp:jingle:1", "content");
- xmlNodePtr ice_udp_transport =
+ rexmpp_xml_t *ice_udp_transport =
rexmpp_xml_find_child(content, "urn:xmpp:jingle:transports:ice-udp:1",
"transport");
if (ice_udp_transport != NULL) {
rexmpp_jingle_ice_udp_add_remote(session, ice_udp_transport);
}
-#endif
+#endif /* ENABLE_CALLS */
}
} else {
rexmpp_log(s, LOG_WARNING, "Unknown Jingle action: %s", action);
- rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request"));
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "bad-request"));
}
} else {
rexmpp_log(s, LOG_WARNING, "Received a malformed Jingle element");
- rexmpp_iq_reply(s, elem, "error", rexmpp_xml_error("cancel", "bad-request"));
- }
- if (action != NULL) {
- free(action);
- }
- if (sid != NULL) {
- free(sid);
- }
- if (from_jid != NULL) {
- free(from_jid);
+ rexmpp_iq_reply(s, elem, "error",
+ rexmpp_xml_error("cancel", "bad-request"));
}
}
/* XEP-0261: Jingle In-Band Bytestreams Transport Method */
- xmlNodePtr ibb_open =
+ rexmpp_xml_t *ibb_open =
rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "open");
if (ibb_open != NULL) {
handled = 1;
/* no-op, though could check sid here. */
rexmpp_iq_reply(s, elem, "result", NULL);
}
- xmlNodePtr ibb_close =
+ rexmpp_xml_t *ibb_close =
rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "close");
if (ibb_close != NULL) {
handled = 1;
rexmpp_iq_reply(s, elem, "result", NULL);
- char *sid = xmlGetProp(ibb_close, "sid");
+ const char *sid = rexmpp_xml_find_attr_val(ibb_close, "sid");
if (sid != NULL) {
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_ibb_sid(s, sid);
if (session != NULL) {
rexmpp_jingle_session_terminate
(s, session->sid,
- rexmpp_xml_new_node("success", "urn:xmpp:jingle:1"), NULL);
+ rexmpp_xml_new_elem("success", "urn:xmpp:jingle:1"), NULL);
}
- free(sid);
}
}
- xmlNodePtr ibb_data =
+ rexmpp_xml_t *ibb_data =
rexmpp_xml_find_child(elem, "http://jabber.org/protocol/ibb", "data");
if (ibb_data != NULL) {
handled = 1;
- char *sid = xmlGetProp(ibb_data, "sid");
+ const char *sid = rexmpp_xml_find_attr_val(ibb_data, "sid");
if (sid != NULL) {
rexmpp_jingle_session_t *session = rexmpp_jingle_session_by_ibb_sid(s, sid);
if (session != NULL && session->ibb_fh != NULL) {
- char *data = NULL, *data_base64 = xmlNodeGetContent(ibb_data);
+ char *data = NULL;
+ const char *data_base64 = rexmpp_xml_text_child(ibb_data);
if (data_base64 != NULL) {
size_t data_len = 0;
int base64_err = rexmpp_base64_from(data_base64, strlen(data_base64),
- &data, &data_len);
- free(data_base64);
+ &data, &data_len);
if (base64_err != 0) {
rexmpp_log(s, LOG_ERR, "Base-64 decoding failure");
} else {
@@ -1706,7 +2061,6 @@ int rexmpp_jingle_iq (rexmpp_t *s, xmlNodePtr elem) {
}
}
}
- free(sid);
}
/* todo: report errors */
rexmpp_iq_reply(s, elem, "result", NULL);
@@ -1719,7 +2073,7 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
#ifdef ENABLE_CALLS
gint poll_timeout;
GPollFD poll_fds[10];
- GMainContext* gctx = g_main_loop_get_context(s->jingle.gloop);
+ GMainContext* gctx = g_main_loop_get_context(s->jingle->gloop);
if (g_main_context_acquire(gctx)) {
gint poll_fds_n = g_main_context_query(gctx,
G_PRIORITY_HIGH,
@@ -1741,29 +2095,23 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
}
rexmpp_jingle_session_t *sess;
- for (sess = s->jingle.sessions; sess != NULL; sess = sess->next) {
+ for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
for (i = 0; i < 2; i++) {
if (sess->component[i].dtls_state != REXMPP_TLS_INACTIVE &&
sess->component[i].dtls_state != REXMPP_TLS_CLOSED &&
sess->component[i].dtls_state != REXMPP_TLS_ERROR) {
- GSocket *sock =
- nice_agent_get_selected_socket(sess->ice_agent,
- sess->ice_stream_id,
- i + 1);
- if (sock != NULL) {
- int fd = g_socket_get_fd(sock);
- g_object_unref(sock);
+ GPtrArray *sockets =
+ nice_agent_get_sockets(sess->ice_agent,
+ sess->ice_stream_id, i + 1);
+ guint i;
+ for (i = 0; i < sockets->len; i++) {
+ int fd = g_socket_get_fd(sockets->pdata[i]);
FD_SET(fd, read_fds);
if (fd > nfds) {
nfds = fd;
}
}
- if (sess->component[i].udp_socket != -1) {
- FD_SET(sess->component[i].udp_socket, read_fds);
- if (sess->component[i].udp_socket > nfds) {
- nfds = sess->component[i].udp_socket;
- }
- }
+ g_ptr_array_unref(sockets);
}
}
}
@@ -1771,21 +2119,21 @@ int rexmpp_jingle_fds(rexmpp_t *s, fd_set *read_fds, fd_set *write_fds) {
rexmpp_log(s, LOG_ERR,
"Failed to acquire GMainContext in rexmpp_jingle_fds");
}
-#else
+#else /* ENABLE_CALLS */
(void)s;
(void)read_fds;
(void)write_fds;
-#endif
+#endif /* ENABLE_CALLS */
return (nfds + 1);
}
-struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
- struct timeval *max_tv,
- struct timeval *tv) {
+struct timespec * rexmpp_jingle_timeout (rexmpp_t *s,
+ struct timespec *max_tv,
+ struct timespec *tv) {
#ifdef ENABLE_CALLS
gint poll_timeout;
GPollFD poll_fds[10];
- GMainContext* gctx = g_main_loop_get_context(s->jingle.gloop);
+ GMainContext* gctx = g_main_loop_get_context(s->jingle->gloop);
if (g_main_context_acquire(gctx)) {
g_main_context_query(gctx,
G_PRIORITY_HIGH,
@@ -1795,28 +2143,34 @@ struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
g_main_context_release(gctx);
rexmpp_jingle_session_t *sess;
- for (sess = s->jingle.sessions; sess != NULL; sess = sess->next) {
+ for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
int i;
for (i = 0; i < 2; i++) {
if (sess->component[i].dtls_state != REXMPP_TLS_INACTIVE &&
sess->component[i].dtls_state != REXMPP_TLS_CLOSED &&
sess->component[i].dtls_state != REXMPP_TLS_ERROR) {
- int tms = gnutls_dtls_get_timeout(sess->component[i].dtls_session);
+ int tms = rexmpp_dtls_timeout(sess->s, sess->component[i].dtls);
if (tms > 0 && (poll_timeout < 0 || tms < poll_timeout)) {
poll_timeout = tms;
}
+ /* Set poll timeout to at most 5 ms if there are connected
+ components, for timely transmission of Jingle data. */
+ if (sess->component[i].dtls_state == REXMPP_TLS_ACTIVE &&
+ (poll_timeout < 0 || poll_timeout > 5)) {
+ poll_timeout = 5;
+ }
}
}
}
if (poll_timeout >= 0) {
int sec = poll_timeout / 1000;
- int usec = (poll_timeout % 1000) * 1000;
+ int nsec = (poll_timeout % 1000) * 1000000;
if (max_tv == NULL ||
(max_tv->tv_sec > sec ||
- (max_tv->tv_sec == sec && max_tv->tv_usec > usec))) {
+ (max_tv->tv_sec == sec && max_tv->tv_nsec > nsec))) {
tv->tv_sec = sec;
- tv->tv_usec = usec;
+ tv->tv_nsec = nsec;
max_tv = tv;
}
}
@@ -1824,10 +2178,10 @@ struct timeval * rexmpp_jingle_timeout (rexmpp_t *s,
rexmpp_log(s, LOG_ERR,
"Failed to acquire GMainContext in rexmpp_jingle_timeout");
}
-#else
+#else /* ENABLE_CALLS */
(void)s;
(void)tv;
-#endif
+#endif /* ENABLE_CALLS */
return max_tv;
}
@@ -1837,232 +2191,282 @@ rexmpp_jingle_run (rexmpp_t *s,
fd_set *write_fds)
{
(void)write_fds;
+ (void)read_fds;
#ifdef ENABLE_CALLS
rexmpp_jingle_session_t *sess;
- int key_mat_size;
- char key_mat[4096];
int err;
- gnutls_datum_t client_key, client_salt, server_key, server_salt;
- char client_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2],
- server_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT * 2];
- for (sess = s->jingle.sessions; sess != NULL; sess = sess->next) {
+ unsigned char key_mat[2 * (SRTP_AES_ICM_128_KEY_LEN_WSALT)],
+ client_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT],
+ server_sess_key[SRTP_AES_ICM_128_KEY_LEN_WSALT];
+ for (sess = s->jingle->sessions; sess != NULL; sess = sess->next) {
char input[4096 + SRTP_MAX_TRAILER_LEN];
int input_len;
int comp_id;
+
for (comp_id = 0; comp_id < 2; comp_id++) {
rexmpp_jingle_component_t *comp = &sess->component[comp_id];
if (comp->dtls_state == REXMPP_TLS_HANDSHAKE) {
- int ret = gnutls_handshake(comp->dtls_session);
- if (ret == 0) {
+ int ret = rexmpp_tls_handshake(s, comp->dtls);
+ if (ret == REXMPP_TLS_SUCCESS) {
rexmpp_log(s, LOG_DEBUG,
"DTLS connected for Jingle session %s, component %d",
sess->sid, comp->component_id);
comp->dtls_state = REXMPP_TLS_ACTIVE;
- /* Verify the peer's fingerprint */
-
- unsigned int cert_list_size = 0;
- const gnutls_datum_t *cert_list;
- cert_list =
- gnutls_certificate_get_peers(comp->dtls_session, &cert_list_size);
- if (cert_list_size != 1) {
+ rexmpp_xml_t *jingle = comp->session->initiator
+ ? comp->session->accept
+ : comp->session->initiate;
+ rexmpp_xml_t *fingerprint =
+ rexmpp_xml_find_child
+ (rexmpp_xml_find_child
+ (rexmpp_xml_find_child
+ (jingle, "urn:xmpp:jingle:1", "content"),
+ "urn:xmpp:jingle:transports:ice-udp:1", "transport"),
+ "urn:xmpp:jingle:apps:dtls:0", "fingerprint");
+ if (fingerprint == NULL) {
+ /* todo: might be neater to check it upon receiving the
+ stanzas, instead of checking it here */
rexmpp_log(comp->s, LOG_ERR,
- "Unexpected peer certificate list size: %d",
- cert_list_size);
+ "No fingerprint in the peer's Jingle element");
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("security-error", "urn:xmpp:jingle:1"),
- "Unexpected certificate list size; expected exactly 1.");
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
+ "No fingerprint element");
+ return REXMPP_E_TLS;
} else {
- xmlNodePtr jingle = comp->session->initiator
- ? comp->session->accept
- : comp->session->initiate;
- xmlNodePtr fingerprint =
- rexmpp_xml_find_child
- (rexmpp_xml_find_child
- (rexmpp_xml_find_child
- (jingle, "urn:xmpp:jingle:1", "content"),
- "urn:xmpp:jingle:transports:ice-udp:1", "transport"),
- "urn:xmpp:jingle:apps:dtls:0", "fingerprint");
- if (fingerprint == NULL) {
- /* todo: might be neater to check it upon receiving the
- stanzas, instead of checking it here */
+ const char *hash_str = rexmpp_xml_find_attr_val(fingerprint, "hash");
+ if (hash_str == NULL) {
rexmpp_log(comp->s, LOG_ERR,
- "No fingerprint in the peer's Jingle element");
+ "No hash attribute in the peer's fingerprint element");
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
- "No fingerprint element");
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
+ "No hash attribute in the fingerprint element");
+ return REXMPP_E_TLS;
} else {
- char *hash_str = xmlGetProp(fingerprint, "hash");
- if (hash_str == NULL) {
- rexmpp_log(comp->s, LOG_ERR,
- "No hash attribute in the peer's fingerprint element");
- rexmpp_jingle_session_terminate
- (s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
- "No hash attribute in the fingerprint element");
- break;
- } else {
- gnutls_digest_algorithm_t algo = GNUTLS_DIG_UNKNOWN;
- /* gnutls_digest_get_id uses different names, so
- checking manually here. These are SDP options,
- <https://datatracker.ietf.org/doc/html/rfc4572#page-8>. */
- if (strcmp(hash_str, "sha-1") == 0) {
- algo = GNUTLS_DIG_SHA1;
- } else if (strcmp(hash_str, "sha-224") == 0) {
- algo = GNUTLS_DIG_SHA224;
- } else if (strcmp(hash_str, "sha-256") == 0) {
- algo = GNUTLS_DIG_SHA256;
- } else if (strcmp(hash_str, "sha-384") == 0) {
- algo = GNUTLS_DIG_SHA384;
- } else if (strcmp(hash_str, "sha-512") == 0) {
- algo = GNUTLS_DIG_SHA512;
- } else if (strcmp(hash_str, "md5") == 0) {
- algo = GNUTLS_DIG_MD5;
- }
- free(hash_str);
- if (algo == GNUTLS_DIG_UNKNOWN) {
+ char fp[64], fp_str[64 * 3];
+ size_t fp_size = 64;
+ if (rexmpp_tls_peer_fp(comp->s, comp->dtls, hash_str,
+ fp, fp_str, &fp_size))
+ {
+ rexmpp_jingle_session_terminate
+ (s, sess->sid,
+ rexmpp_xml_new_elem("connectivity-error",
+ "urn:xmpp:jingle:1"),
+ "Failed to obtain the DTLS certificate fingerprint");
+ return REXMPP_E_TLS;
+ } else {
+ const char *fingerprint_cont =
+ rexmpp_xml_text_child(fingerprint);
+ /* Fingerprint string should be uppercase, but
+ allowing any case for now, while Dino uses
+ lowercase. */
+ int fingerprint_mismatch = strcasecmp(fingerprint_cont, fp_str);
+ if (fingerprint_mismatch) {
rexmpp_log(comp->s, LOG_ERR,
- "Unknown hash algorithm in the peer's fingerprint");
+ "Peer's fingerprint mismatch: expected %s,"
+ " calculated %s",
+ fingerprint_cont, fp_str);
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
- "Unknown hash algorithm for a DTLS certificate fingerprint");
- break;
+ rexmpp_xml_new_elem("security-error", "urn:xmpp:jingle:1"),
+ "DTLS certificate fingerprint mismatch");
+ return REXMPP_E_TLS;
} else {
-
- char fp[64], fp_str[64 * 3];
- size_t fp_size = 64;
- gnutls_fingerprint(algo, cert_list, fp, &fp_size);
- size_t i;
- for (i = 0; i < fp_size; i++) {
- snprintf(fp_str + i * 3, 4, "%02X:", fp[i] & 0xFF);
+ /* The fingerprint is fine, proceed to SRTP. */
+ rexmpp_log(comp->s, LOG_DEBUG,
+ "Peer's fingerprint: %s", fp_str);
+
+ rexmpp_tls_srtp_get_keys(s, comp->dtls,
+ SRTP_AES_128_KEY_LEN, SRTP_SALT_LEN,
+ key_mat);
+ /* client key */
+ memcpy(client_sess_key, key_mat, SRTP_AES_128_KEY_LEN);
+ /* server key */
+ memcpy(server_sess_key, key_mat + SRTP_AES_128_KEY_LEN,
+ SRTP_AES_128_KEY_LEN);
+ /* client salt */
+ memcpy(client_sess_key + SRTP_AES_128_KEY_LEN,
+ key_mat + SRTP_AES_128_KEY_LEN * 2,
+ SRTP_SALT_LEN);
+ /* server salt */
+ memcpy(server_sess_key + SRTP_AES_128_KEY_LEN,
+ key_mat + SRTP_AES_128_KEY_LEN * 2 + SRTP_SALT_LEN,
+ SRTP_SALT_LEN);
+
+ int active_role = rexmpp_jingle_dtls_is_active(sess, 0);
+
+ srtp_policy_t inbound;
+ memset(&inbound, 0x0, sizeof(srtp_policy_t));
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
+ inbound.ssrc.type = ssrc_any_inbound;
+ inbound.key = active_role ? server_sess_key : client_sess_key;
+ inbound.window_size = 1024;
+ inbound.allow_repeat_tx = 1;
+ inbound.next = NULL;
+ err = srtp_create(&(comp->srtp_in), &inbound);
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to create srtp_in");
}
- fp_str[fp_size * 3 - 1] = 0;
-
- char *fingerprint_cont = xmlNodeGetContent(fingerprint);
- /* Fingerprint string should be uppercase, but
- allowing any case for now, while Dino uses
- lowercase. */
- int fingerprint_mismatch = strcasecmp(fingerprint_cont, fp_str);
- free(fingerprint_cont);
- if (fingerprint_mismatch) {
- rexmpp_log(comp->s, LOG_ERR,
- "Peer's fingerprint mismatch: expected %s, calculated %s",
- fingerprint_cont, fp_str);
- rexmpp_jingle_session_terminate
- (s, sess->sid,
- rexmpp_xml_new_node("security-error", "urn:xmpp:jingle:1"),
- "DTLS certificate fingerprint mismatch");
- break;
- } else {
- /* The fingerprint is fine, proceed to SRTP. */
- rexmpp_log(comp->s, LOG_DEBUG, "Peer's fingerprint: %s", fp_str);
-
- key_mat_size =
- gnutls_srtp_get_keys(comp->dtls_session, key_mat,
- SRTP_AES_ICM_128_KEY_LEN_WSALT * 2,
- &client_key, &client_salt,
- &server_key, &server_salt);
- rexmpp_log(s, LOG_DEBUG, "SRTP key material size: %d",
- key_mat_size);
- memcpy(client_sess_key, client_key.data,
- SRTP_AES_128_KEY_LEN);
- memcpy(client_sess_key + SRTP_AES_128_KEY_LEN,
- client_salt.data, SRTP_SALT_LEN);
-
- memcpy(server_sess_key, server_key.data,
- SRTP_AES_128_KEY_LEN);
- memcpy(server_sess_key + SRTP_AES_128_KEY_LEN,
- server_salt.data, SRTP_SALT_LEN);
-
- int active_role = rexmpp_jingle_dtls_is_active(sess, 0);
-
- srtp_policy_t inbound;
- memset(&inbound, 0x0, sizeof(srtp_policy_t));
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtp);
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&inbound.rtcp);
- inbound.ssrc.type = ssrc_any_inbound;
- inbound.key = active_role ? server_sess_key : client_sess_key;
- inbound.window_size = 1024;
- inbound.allow_repeat_tx = 1;
- inbound.next = NULL;
- err = srtp_create(&(comp->srtp_in), &inbound);
- if (err) {
- rexmpp_log(s, LOG_ERR, "Failed to create srtp_in");
- }
- srtp_policy_t outbound;
- memset(&outbound, 0x0, sizeof(srtp_policy_t));
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
- srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
- outbound.ssrc.type = ssrc_any_outbound;
- outbound.key = active_role ? client_sess_key : server_sess_key;
- outbound.window_size = 1024;
- outbound.allow_repeat_tx = 1;
- outbound.next = NULL;
- err = srtp_create(&(comp->srtp_out), &outbound);
- if (err) {
- rexmpp_log(s, LOG_ERR, "Failed to create srtp_out");
- }
+ srtp_policy_t outbound;
+ memset(&outbound, 0x0, sizeof(srtp_policy_t));
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtp);
+ srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&outbound.rtcp);
+ outbound.ssrc.type = ssrc_any_outbound;
+ outbound.key = active_role ? client_sess_key : server_sess_key;
+ outbound.window_size = 1024;
+ outbound.allow_repeat_tx = 1;
+ outbound.next = NULL;
+ err = srtp_create(&(comp->srtp_out), &outbound);
+ if (err) {
+ rexmpp_log(s, LOG_ERR, "Failed to create srtp_out");
}
}
}
}
}
- } else if (ret != GNUTLS_E_AGAIN) {
- rexmpp_log(s, LOG_ERR, "DTLS error for session %s, component %d: %s",
- sess->sid, comp->component_id, gnutls_strerror(ret));
+ } else if (ret != REXMPP_TLS_E_AGAIN) {
+ rexmpp_log(s, LOG_ERR, "DTLS error for session %s, component %d",
+ sess->sid, comp->component_id);
comp->dtls_state = REXMPP_TLS_ERROR;
if (comp->component_id == 1) {
rexmpp_jingle_session_terminate
(s, sess->sid,
- rexmpp_xml_new_node("connectivity-error", "urn:xmpp:jingle:1"),
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
"DTLS connection error");
- break;
+ return REXMPP_E_TLS;
}
}
}
- /* Handle outbound packets */
- srtp_ctx_t *srtp_out;
+ /* Check on the DTLS session, too. */
if (comp->dtls_state == REXMPP_TLS_ACTIVE) {
- srtp_out = comp->srtp_out;
- } else if ((comp->dtls_state == REXMPP_TLS_ERROR || comp->session->rtcp_mux) &&
- comp->session->component[0].dtls_state == REXMPP_TLS_ACTIVE) {
- /* Try to reuse the first component's session. */
- srtp_out = comp->session->component[0].srtp_out;
- } else {
- break;
+ ssize_t received;
+ rexmpp_tls_err_t err =
+ rexmpp_tls_recv(s, comp->dtls, input, 4096, &received);
+ if (err != REXMPP_TLS_SUCCESS && err != REXMPP_TLS_E_AGAIN) {
+ rexmpp_log(s, LOG_ERR,
+ "Error on rexmpp_tls_recv (component id %d), "
+ "terminating Jingle session %s",
+ comp->component_id, sess->sid);
+ rexmpp_jingle_session_terminate
+ (s, sess->sid,
+ rexmpp_xml_new_elem("connectivity-error", "urn:xmpp:jingle:1"),
+ "TLS reading error");
+ return REXMPP_E_TLS;
+ }
}
+ }
- if (FD_ISSET(comp->udp_socket, read_fds)) {
- input_len = recv(comp->udp_socket, input, 4096, 0);
- if (comp->component_id == 1) {
- err = srtp_protect(srtp_out, input, &input_len);
- } else {
- err = srtp_protect_rtcp(srtp_out, input, &input_len);
+ /* Send captured audio frames for established sessions. */
+ if ((sess->component[0].dtls_state == REXMPP_TLS_ACTIVE)
+ && (sess->component[0].srtp_out != NULL)
+ && (sess->codec != REXMPP_CODEC_UNDEFINED)) {
+ struct ring_buf *capture = &(sess->ring_buffers.capture);
+ while (capture->write_pos != capture->read_pos) {
+ if (sess->codec == REXMPP_CODEC_PCMU) {
+ for (input_len = 12;
+ input_len < 4096 && capture->write_pos != capture->read_pos;
+ input_len++)
+ {
+ input[input_len] =
+ rexmpp_pcmu_encode(capture->buf[capture->read_pos]);
+ capture->read_pos++;
+ capture->read_pos %= PA_BUF_SIZE;
+ sess->rtp_timestamp++;
+ }
+ } else if (sess->codec == REXMPP_CODEC_PCMA) {
+ for (input_len = 12;
+ input_len < 4096 && capture->write_pos != capture->read_pos;
+ input_len++)
+ {
+ input[input_len] =
+ rexmpp_pcma_encode(capture->buf[capture->read_pos]);
+ capture->read_pos++;
+ capture->read_pos %= PA_BUF_SIZE;
+ sess->rtp_timestamp++;
+ }
+ }
+#ifdef HAVE_OPUS
+ else if (sess->codec == REXMPP_CODEC_OPUS) {
+ unsigned int samples_available;
+ if (capture->write_pos > capture->read_pos) {
+ samples_available = capture->write_pos - capture->read_pos;
+ } else {
+ samples_available =
+ (PA_BUF_SIZE - capture->read_pos) + capture->write_pos;
+ }
+ unsigned int frame_size = 480;
+ if (samples_available >= frame_size * 2) {
+ opus_int16 pcm[4096];
+ /* Prepare a regular buffer */
+ unsigned int i;
+ for (i = 0;
+ (i < frame_size * 2) &&
+ (capture->write_pos != capture->read_pos);
+ i++)
+ {
+ pcm[i] = capture->buf[capture->read_pos];
+ capture->read_pos++;
+ capture->read_pos %= PA_BUF_SIZE;
+ sess->rtp_timestamp++;
+ }
+ /* Encode it */
+ int encoded_len;
+ encoded_len = opus_encode(sess->opus_enc,
+ pcm,
+ frame_size,
+ (unsigned char*)input + 12,
+ 4096);
+ if (encoded_len < 0) {
+ rexmpp_log(s, LOG_ERR, "Failed to encode an Opus frame");
+ break;
+ }
+ input_len = 12 + encoded_len;
+ } else {
+ break;
+ }
}
+#endif /* HAVE_OPUS */
+
+ /* Setup an RTP header */
+ uint32_t hl, nl;
+ hl = (2 << 30) /* version */
+ | (0 << 29) /* padding */
+ | (0 << 28) /* extension */
+ | (0 << 24) /* CSRC count */
+ | (0 << 23) /* marker */
+ | (sess->payload_type << 16) /* paylaod type, RFC 3551 */
+ | sess->rtp_seq_num;
+ sess->rtp_seq_num++;
+ nl = htonl(hl);
+ memcpy(input, &nl, sizeof(uint32_t));
+ nl = htonl(sess->rtp_timestamp);
+ memcpy(input + 4, &nl, sizeof(uint32_t));
+ nl = htonl(sess->rtp_ssrc);
+ memcpy(input + 8, &nl, sizeof(uint32_t));
+ /* The RTP header is ready */
+
+ srtp_ctx_t *srtp_out = sess->component[0].srtp_out;
+ err = srtp_protect(srtp_out, input, &input_len);
if (err) {
- rexmpp_log(s, LOG_ERR, "SRT(C)P protect error %d\n", err);
+ rexmpp_log(s, LOG_ERR, "SRTP protect error %d", err);
} else {
nice_agent_send(sess->ice_agent, sess->ice_stream_id,
- sess->rtcp_mux ? 1 : comp->component_id,
+ /* sess->rtcp_mux ? 1 : comp->component_id, */
+ 1,
input_len, input);
}
}
- /* Check on the DTLS session too. */
- if (comp->dtls_state == REXMPP_TLS_ACTIVE) {
- input_len = gnutls_record_recv(comp->dtls_session, input, 4096);
- }
}
}
- g_main_context_iteration(g_main_loop_get_context(s->jingle.gloop), 0);
-#else
+ g_main_context_iteration(g_main_loop_get_context(s->jingle->gloop), 0);
+#else /* ENABLE_CALLS */
(void)s;
(void)read_fds;
-#endif
+#endif /* ENABLE_CALLS */
return REXMPP_SUCCESS;
}