Commit 5576d7e8 authored by Youness Alaoui's avatar Youness Alaoui

(WIP) Add TURN support to libnice

parent b9749015
......@@ -640,10 +640,10 @@ agent_candidate_pair_priority (NiceAgent *agent, NiceCandidate *local, NiceCandi
}
static gboolean
priv_add_new_candidate_discovery (NiceAgent *agent,
priv_add_new_candidate_discovery_stun (NiceAgent *agent,
NiceCandidate *host_candidate, NiceAddress server,
Stream *stream, guint component_id,
NiceAddress *addr, NiceCandidateType type)
NiceAddress *addr)
{
CandidateDiscovery *cdisco;
GSList *modified_list;
......@@ -656,7 +656,7 @@ priv_add_new_candidate_discovery (NiceAgent *agent,
modified_list = g_slist_append (agent->discovery_list, cdisco);
if (modified_list) {
cdisco->type = type;
cdisco->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
cdisco->socket = host_candidate->sockptr->fileno;
cdisco->nicesock = host_candidate->sockptr;
cdisco->server = server;
......@@ -675,6 +675,55 @@ priv_add_new_candidate_discovery (NiceAgent *agent,
return FALSE;
}
static gboolean
priv_add_new_candidate_discovery_turn (NiceAgent *agent,
NiceCandidate *host_candidate, NiceAddress server,
Stream *stream, guint component_id,
NiceAddress *addr, gboolean long_term)
{
CandidateDiscovery *cdisco;
GSList *modified_list;
/* note: no need to check for redundant candidates, as this is
* done later on in the process */
cdisco = g_slice_new0 (CandidateDiscovery);
if (cdisco) {
modified_list = g_slist_append (agent->discovery_list, cdisco);
if (modified_list) {
cdisco->type = NICE_CANDIDATE_TYPE_RELAYED;
cdisco->socket = host_candidate->sockptr->fileno;
cdisco->nicesock = host_candidate->sockptr;
cdisco->server = server;
cdisco->interface = addr;
cdisco->stream = stream;
cdisco->component = stream_find_component_by_id (stream, component_id);
cdisco->agent = agent;
if (agent->compatibility == NICE_COMPATIBILITY_ID19) {
stun_agent_init (&cdisco->turn_agent, STUN_ALL_KNOWN_ATTRIBUTES,
STUN_COMPATIBILITY_3489BIS,
long_term ? STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS :
STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS);
} else {
stun_agent_init (&cdisco->turn_agent, STUN_ALL_KNOWN_ATTRIBUTES,
STUN_COMPATIBILITY_RFC3489,
long_term ? STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS :
STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS);
}
nice_debug ("Agent %p : Adding new srv-rflx candidate discovery %p\n", agent, cdisco);
agent->discovery_list = modified_list;
++agent->discovery_unsched_items;
}
return TRUE;
}
return FALSE;
}
/**
* nice_agent_add_stream:
......@@ -802,17 +851,16 @@ nice_agent_gather_candidates (
if (agent->full_mode &&
agent->stun_server_ip) {
NiceAddress stun_server;
if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
nice_address_set_port (&stun_server, agent->stun_server_port);
gboolean res =
priv_add_new_candidate_discovery (agent,
priv_add_new_candidate_discovery_stun (agent,
host_candidate,
stun_server,
stream,
n + 1 /* component-id */,
addr,
NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
addr);
if (res != TRUE) {
/* note: memory allocation failure, return error */
......@@ -820,17 +868,18 @@ nice_agent_gather_candidates (
}
}
}
if (agent->full_mode &&
component && nice_address_is_valid (&component->turn_server)) {
gboolean res =
priv_add_new_candidate_discovery (agent,
priv_add_new_candidate_discovery_turn (agent,
host_candidate,
component->turn_server,
stream,
n + 1 /* component-id */,
addr,
NICE_CANDIDATE_TYPE_RELAYED);
component->turn_long_term);
if (res != TRUE) {
/* note: memory allocation failure, return error */
......
......@@ -1726,6 +1726,88 @@ static gboolean priv_map_reply_to_discovery_request (NiceAgent *agent, StunMessa
}
/**
* Tries to match STUN reply in 'buf' to an existing STUN discovery
* transaction. If found, a reply is sent.
*
* @return TRUE if a matching transaction is found
*/
static gboolean priv_map_reply_to_relay_request (NiceAgent *agent, StunMessage *resp)
{
struct sockaddr sockaddr;
socklen_t socklen = sizeof (sockaddr);
struct sockaddr alternate;
socklen_t alternatelen = sizeof (alternate);
struct sockaddr relayaddr;
socklen_t relayaddrlen = sizeof (relayaddr);
uint32_t lifetime;
uint32_t bandwidth;
GSList *i;
StunUsageTurnReturn res;
gboolean trans_found = FALSE;
stun_transid_t discovery_id;
stun_transid_t response_id;
stun_message_id (resp, response_id);
for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
CandidateDiscovery *d = i->data;
if (d->type == NICE_CANDIDATE_TYPE_RELAYED &&
d->stun_message.buffer) {
stun_message_id (&d->stun_message, discovery_id);
if (memcmp (discovery_id, response_id, sizeof(stun_transid_t)) == 0) {
res = stun_usage_turn_process (resp,
&relayaddr, &relayaddrlen, &sockaddr, &socklen, &alternate, &alternatelen,
&bandwidth, &lifetime, priv_agent_to_turn_compatibility (agent));
nice_debug ("Agent %p : stun_bind_process/disc for %p res %d.",
agent, d, (int)res);
if (res == STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER) {
/* TODO : handle alternate server */
} else if (res == STUN_USAGE_TURN_RETURN_RELAY_SUCCESS ||
res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
/* case: succesful allocate, create a new local candidate */
NiceAddress niceaddr;
/* We also received our mapped address */
if (res == STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS) {
nice_address_set_from_sockaddr (&niceaddr, &sockaddr);
discovery_add_server_reflexive_candidate (
d->agent,
d->stream->id,
d->component->id,
&niceaddr,
d->nicesock);
}
nice_address_set_from_sockaddr (&niceaddr, &relayaddr);
discovery_add_relay_candidate (
d->agent,
d->stream->id,
d->component->id,
&niceaddr,
d->nicesock);
d->stun_message.buffer = NULL;
d->stun_message.buffer_len = 0;
d->done = TRUE;
trans_found = TRUE;
} else if (res == STUN_USAGE_BIND_RETURN_ERROR) {
/* case: STUN error, the check STUN context was freed */
d->stun_message.buffer = NULL;
d->stun_message.buffer_len = 0;
d->done = TRUE;
trans_found = TRUE;
}
}
}
}
return trans_found;
}
/**
* Processing an incoming STUN message.
*
......@@ -1773,9 +1855,11 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
}
}
/* TODO : if we get a peer reflexive, we should check all local:remote possibilities */
validater_data = g_new0(stun_validater_data,
g_slist_length(component->local_candidates) + 1);
/* We have to check all the local candidates into our validater_data because a
server reflexive candidate shares the same socket as the host candidate,
so we have no idea the user/pass is coming from which candidate */
......@@ -1798,6 +1882,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
validater_data[j].password_len = strlen (stream->local_password);
}
if (agent->compatibility == NICE_COMPATIBILITY_MSN) {
validater_data[j].password =
g_base64_decode ((gchar *) validater_data[j].password,
......@@ -1995,6 +2080,10 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream,
if (trans_found != TRUE)
trans_found = priv_map_reply_to_discovery_request (agent, &req);
/* step: let's try to match the response to an existing discovery */
if (trans_found != TRUE)
trans_found = priv_map_reply_to_relay_request (agent, &req);
if (trans_found != TRUE)
nice_debug ("Agent %p : Unable to match to an existing transaction, "
"probably a keepalive.", agent);
......
......@@ -69,6 +69,15 @@ static inline int priv_timer_expired (GTimeVal *restrict timer, GTimeVal *restri
now->tv_sec >= timer->tv_sec;
}
static StunUsageTurnCompatibility priv_agent_to_turn_compatibility (NiceAgent *agent) {
return agent->compatibility == NICE_COMPATIBILITY_ID19 ?
STUN_USAGE_TURN_COMPATIBILITY_TD9 :
agent->compatibility == NICE_COMPATIBILITY_GOOGLE ?
STUN_USAGE_TURN_COMPATIBILITY_GOOGLE :
agent->compatibility == NICE_COMPATIBILITY_MSN ?
STUN_USAGE_TURN_COMPATIBILITY_MSN : STUN_USAGE_TURN_COMPATIBILITY_TD9;
}
/**
* Frees the CandidateDiscovery structure pointed to
* by 'user data'. Compatible with g_slist_foreach().
......@@ -587,14 +596,14 @@ NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
gsize local_size;
gsize remote_size;
g_free(candidate->username);
decoded_local = g_base64_decode (local->username, &local_size);
decoded_remote = g_base64_decode (remote->username, &remote_size);
new_username = g_new0(gchar, local_size + remote_size);
memcpy(new_username, decoded_remote, remote_size);
memcpy(new_username + remote_size, decoded_local, local_size);
candidate->username = g_base64_encode (new_username, local_size + remote_size);
g_free(new_username);
g_free(decoded_local);
......@@ -604,7 +613,7 @@ NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
candidate->sockptr = NULL; /* not stored for remote candidates */
/* note: candidate username and password are left NULL as stream
level ufrag/password are used */
modified_list = g_slist_append (component->remote_candidates,
candidate);
if (modified_list) {
......@@ -653,14 +662,12 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer)
if (agent->discovery_unsched_items)
--agent->discovery_unsched_items;
#ifndef NDEBUG
{
gchar tmpbuf[INET6_ADDRSTRLEN];
nice_address_to_string (&cand->server, tmpbuf);
nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s and socket %d.\n", agent,
cand->type, tmpbuf, cand->socket);
}
#endif
if (nice_address_is_valid (&cand->server) &&
(cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE ||
cand->type == NICE_CANDIDATE_TYPE_RELAYED)) {
......@@ -683,7 +690,7 @@ static gboolean priv_discovery_tick_unlocked (gpointer pointer)
(size_t) strlen (cand->component->turn_username),
(uint8_t *)cand->component->turn_password,
(size_t) strlen (cand->component->turn_password),
STUN_USAGE_TURN_COMPATIBILITY_GOOGLE);
priv_agent_to_turn_compatibility (agent));
}
if (buffer_len > 0) {
......
......@@ -56,9 +56,12 @@ struct _CandidateDiscovery
gboolean done; /**< is discovery complete? */
Stream *stream;
Component *component;
StunAgent turn_agent;
stun_timer_t timer;
uint8_t stun_buffer[STUN_MAX_MESSAGE_SIZE];
StunMessage stun_message;
uint8_t stun_resp_buffer[STUN_MAX_MESSAGE_SIZE];
StunMessage stun_resp_message;
};
void discovery_free_item (gpointer data, gpointer user_data);
......
......@@ -43,6 +43,8 @@
#include "agent.h"
#define USE_TURN 0
static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
static guint global_components_ready = 0;
......@@ -244,9 +246,21 @@ static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *bas
/* step: add one stream, with RTP+RTCP components, to each agent */
ls_id = nice_agent_add_stream (lagent, 2);
rs_id = nice_agent_add_stream (ragent, 2);
g_assert (ls_id > 0);
g_assert (rs_id > 0);
#if USE_TURN
nice_agent_set_relay_info(lagent, ls_id, 1,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
nice_agent_set_relay_info(lagent, ls_id, 2,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
nice_agent_set_relay_info(ragent, rs_id, 1,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
nice_agent_set_relay_info(ragent, rs_id, 2,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
#endif
nice_agent_gather_candidates (lagent, ls_id);
nice_agent_gather_candidates (ragent, rs_id);
......@@ -374,9 +388,21 @@ static int run_full_test_delayed_answer (NiceAgent *lagent, NiceAgent *ragent, N
/* step: add one stream, with RTP+RTCP components, to each agent */
ls_id = nice_agent_add_stream (lagent, 2);
rs_id = nice_agent_add_stream (ragent, 2);
g_assert (ls_id > 0);
g_assert (rs_id > 0);
#if USE_TURN
nice_agent_set_relay_info(lagent, ls_id, 1,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
nice_agent_set_relay_info(lagent, ls_id, 2,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
nice_agent_set_relay_info(ragent, rs_id, 1,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
nice_agent_set_relay_info(ragent, rs_id, 2,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
#endif
nice_agent_gather_candidates (lagent, ls_id);
nice_agent_gather_candidates (ragent, rs_id);
......@@ -521,9 +547,16 @@ static int run_full_test_wrong_password (NiceAgent *lagent, NiceAgent *ragent, N
/* step: add one stream, with one component, to each agent */
ls_id = nice_agent_add_stream (lagent, 1);
rs_id = nice_agent_add_stream (ragent, 1);
g_assert (ls_id > 0);
g_assert (rs_id > 0);
#if USE_TURN
nice_agent_set_relay_info(lagent, ls_id, 1,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
nice_agent_set_relay_info(ragent, rs_id, 1,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
#endif
nice_agent_gather_candidates (lagent, ls_id);
nice_agent_gather_candidates (ragent, rs_id);
......@@ -633,9 +666,16 @@ static int run_full_test_control_conflict (NiceAgent *lagent, NiceAgent *ragent,
/* step: add one stream, with one component, to each agent */
ls_id = nice_agent_add_stream (lagent, 1);
rs_id = nice_agent_add_stream (ragent, 1);
g_assert (ls_id > 0);
g_assert (rs_id > 0);
#if USE_TURN
nice_agent_set_relay_info(lagent, ls_id, 1,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
nice_agent_set_relay_info(ragent, rs_id, 1,
"64.251.22.149", 3478, "youness.alaoui@collabora.co.uk", "badger", TRUE);
#endif
nice_agent_gather_candidates (lagent, ls_id);
nice_agent_gather_candidates (ragent, rs_id);
......@@ -739,13 +779,17 @@ int main (void)
ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), NICE_COMPATIBILITY_ID19);
/* step: add a timer to catch state changes triggered by signals */
timer_id = g_timeout_add (30000, timer_cb, NULL);
timer_id = g_timeout_add (60000, timer_cb, NULL);
/* step: specify which local interface to use */
if (!nice_address_set_from_string (&baseaddr, "127.0.0.1"))
g_assert_not_reached ();
nice_agent_add_local_address (lagent, &baseaddr);
nice_agent_add_local_address (ragent, &baseaddr);
/* if (!nice_address_set_from_string (&baseaddr, "192.168.1.106"))
g_assert_not_reached ();
nice_agent_add_local_address (lagent, &baseaddr);
nice_agent_add_local_address (ragent, &baseaddr);*/
g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done",
G_CALLBACK (cb_candidate_gathering_done), (gpointer)1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment