Commit 181d9d56 authored by Kai Vehmanen's avatar Kai Vehmanen

Major NICE agent update. Added supprt for peer-reflexive candidates, media...

Major NICE agent update. Added supprt for peer-reflexive candidates, media keepalives, candidate keepalives, role conflict tie-breaking functionality, and for triggered checks. Added NICEAPI_EXPORT attributes to public functions. Includes numerous bugfixes to existing functionality.

darcs-hash:20070619080609-77cd4-d18bf44fe48a201e59556ae5a9dff2b5a2e7e073.gz
parent 99ff130b
......@@ -27,7 +27,7 @@ noinst_LTLIBRARIES = libagent.la
%-signals-marshal.c: %-signals-marshal.list Makefile.am
glib-genmarshal --body --prefix=$(subst -,_,$*)_marshal $< > $@
sed -i "1i#include \"$*-signals-marshal.h\"" $@
sed -ie 's/^}$$/(void)return_value;(void)invocation_hint;}/' $@
sed -i -e 's/^}$$/(void)return_value;(void)invocation_hint;}/' $@
BUILT_SOURCES = \
agent-signals-marshal.h \
......@@ -66,12 +66,15 @@ check_PROGRAMS = \
test-mainloop \
test-fullmode
check_SCRIPTS = \
check-test-fullmode-with-stun.sh
# disabled test programs (using the old STUN API):
noinst_PROGRAMS = \
EXTRA_PROGRAMS = \
test-stun \
test-send
TESTS = $(check_PROGRAMS)
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
test_LDADD = $(COMMON_LDADD)
......
......@@ -49,6 +49,7 @@
#include "stream.h"
#include "conncheck.h"
/** As specified in ICE spec ID-15 */
#define NICE_AGENT_TIMER_TA_DEFAULT 20 /* timer Ta, msecs */
#define NICE_AGENT_TIMER_TR_DEFAULT 15000 /* timer Tr, msecs */
......@@ -59,6 +60,7 @@
struct _NiceAgent
{
GObject parent; /**< gobject pointer */
gboolean full_mode; /**< property: full-mode */
NiceUDPSocketFactory *socket_factory; /**< property: socket factory */
GTimeVal next_check_tv; /**< property: next conncheck timestamp */
......@@ -67,6 +69,8 @@ struct _NiceAgent
gchar *turn_server_ip; /**< property: TURN server IP */
guint turn_server_port; /**< property: TURN server port */
gboolean controlling_mode; /**< property: controlling-mode */
guint timer_ta; /**< property: timer Ta */
GSList *local_addresses; /**< list of NiceAddresses for local
interfaces */
GSList *streams; /**< list of Stream objects */
......@@ -83,6 +87,10 @@ struct _NiceAgent
GSList *conncheck_list; /**< list of CandidatePair items */
guint conncheck_timer_id; /**< id of discovery timer */
NiceCheckListState conncheck_state; /**< checklist state */
guint keepalive_timer_id; /**< id of keepalive timer */
guint64 tie_breaker; /**< tie breaker (ICE ID-16 sect
5.2) */
gchar ufragtmp[NICE_STREAM_MAX_UNAME]; /**< preallocated buffer for uname processing */
/* XXX: add pointer to internal data struct for ABI-safe extensions */
};
......@@ -115,6 +123,8 @@ void agent_signal_new_candidate (
NiceAgent *agent,
NiceCandidate *candidate);
void agent_signal_new_remote_candidate (NiceAgent *agent, NiceCandidate *candidate);
void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream);
void agent_free_discovery_candidate_udp (gpointer data, gpointer user_data);
......
......@@ -36,15 +36,20 @@
* file under either the MPL or the LGPL.
*/
/**
* @file agent.c
* @brief ICE agent API implementation
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/socket.h>
#ifndef _BSD_SOURCE
#error "timercmp() macros needed"
#endif
#include <sys/time.h> /* timercmp() macro, BSD */
#include <netinet/in.h>
#include <arpa/inet.h>
......@@ -75,7 +80,8 @@ enum
PROP_TURN_SERVER,
PROP_TURN_SERVER_PORT,
PROP_CONTROLLING_MODE,
PROP_FULL_MODE
PROP_FULL_MODE,
PROP_STUN_PACING_TIMER
};
......@@ -85,6 +91,7 @@ enum
SIGNAL_CANDIDATE_GATHERING_DONE,
SIGNAL_NEW_SELECTED_PAIR,
SIGNAL_NEW_CANDIDATE,
SIGNAL_NEW_REMOTE_CANDIDATE,
SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED,
N_SIGNALS,
};
......@@ -168,11 +175,6 @@ nice_agent_class_init (NiceAgentClass *klass)
/* install properties */
/* XXX: add properties:
* - Ta-timer (construct-property, msec)
* - make the others construct-time only as well...?
*/
g_object_class_install_property (gobject_class, PROP_SOCKET_FACTORY,
g_param_spec_pointer (
"socket-factory",
......@@ -194,10 +196,10 @@ nice_agent_class_init (NiceAgentClass *klass)
"STUN server port",
"The STUN server used to obtain server-reflexive candidates",
1, 65536,
IPPORT_STUN, /* default port */
1, /* not a construct property, ignored */
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_STUN_SERVER,
g_object_class_install_property (gobject_class, PROP_TURN_SERVER,
g_param_spec_string (
"turn-server",
"TURN server",
......@@ -205,13 +207,13 @@ nice_agent_class_init (NiceAgentClass *klass)
NULL,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_STUN_SERVER_PORT,
g_object_class_install_property (gobject_class, PROP_TURN_SERVER_PORT,
g_param_spec_uint (
"turn-server-port",
"TURN server port",
"The STUN server used to obtain relay candidates",
"The TURN server used to obtain relay candidates",
1, 65536,
3478, /* no default port for TURN, use the STUN default*/
1, /* not a construct property, ignored */
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE,
......@@ -230,6 +232,15 @@ nice_agent_class_init (NiceAgentClass *klass)
TRUE, /* use full mode by default */
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (gobject_class, PROP_STUN_PACING_TIMER,
g_param_spec_uint (
"stun-pacing-timer",
"STUN pacing timer",
"Timer 'Ta' (msecs) used in the IETF ICE specification for pacing candidate gathering and sending of connectivity checks",
1, 0xffffffff,
NICE_AGENT_TIMER_TA_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/* install signals */
/* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer self) */
......@@ -292,6 +303,21 @@ nice_agent_class_init (NiceAgentClass *klass)
G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING,
G_TYPE_INVALID);
/* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, gchar *foundation) */
signals[SIGNAL_NEW_REMOTE_CANDIDATE] =
g_signal_new (
"new-remote-candidate",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL,
NULL,
agent_marshal_VOID__UINT_UINT_STRING,
G_TYPE_NONE,
3,
G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING,
G_TYPE_INVALID);
/* signature: void cb(NiceAgent *agent, guint stream_id, gpointer self) */
signals[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED] =
g_signal_new (
......@@ -309,6 +335,10 @@ nice_agent_class_init (NiceAgentClass *klass)
}
static void priv_generate_tie_breaker (NiceAgent *agent)
{
nice_rng_generate_bytes (agent->rng, 8, (gchar*)&agent->tie_breaker);
}
static void
nice_agent_init (NiceAgent *agent)
......@@ -327,8 +357,10 @@ nice_agent_init (NiceAgent *agent)
agent->discovery_unsched_items = 0;
agent->discovery_timer_id = 0;
agent->conncheck_timer_id = 0;
agent->keepalive_timer_id = 0;
agent->rng = nice_rng_new ();
priv_generate_tie_breaker (agent);
}
......@@ -340,7 +372,7 @@ nice_agent_init (NiceAgent *agent)
*
* Returns: the new agent
**/
NiceAgent *
NICEAPI_EXPORT NiceAgent *
nice_agent_new (NiceUDPSocketFactory *factory)
{
return g_object_new (NICE_TYPE_AGENT,
......@@ -388,6 +420,10 @@ nice_agent_get_property (
g_value_set_boolean (value, agent->full_mode);
break;
case PROP_STUN_PACING_TIMER:
g_value_set_uint (value, agent->timer_ta);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
......@@ -429,6 +465,10 @@ nice_agent_set_property (
agent->full_mode = g_value_get_boolean (value);
break;
case PROP_STUN_PACING_TIMER:
agent->timer_ta = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
......@@ -467,6 +507,14 @@ void agent_signal_new_candidate (NiceAgent *agent, NiceCandidate *candidate)
candidate->foundation);
}
void agent_signal_new_remote_candidate (NiceAgent *agent, NiceCandidate *candidate)
{
g_signal_emit (agent, signals[SIGNAL_NEW_REMOTE_CANDIDATE], 0,
candidate->stream_id,
candidate->component_id,
candidate->foundation);
}
void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState state)
{
Component *component;
......@@ -508,33 +556,43 @@ static void priv_signal_component_state_connecting (NiceAgent *agent, guint stre
*
* Add a data stream to @agent.
*
* @pre local addresses must be set with nice_agent_add_local_address()
*
* Returns: the ID of the new stream, 0 on failure
**/
guint
NICEAPI_EXPORT guint
nice_agent_add_stream (
NiceAgent *agent,
guint n_components)
{
Stream *stream;
GSList *i;
GSList *i, *modified_list = NULL;
g_assert (n_components == 1);
/* XXX: make memory-allocation safe */
if (!agent->streams) {
/* note: this contains a Y2038 issue */
agent->next_check_tv.tv_sec =
agent->next_check_tv.tv_usec = (long)-1;
}
if (!agent->local_addresses)
return 0;
stream = stream_new ();
if (stream) {
modified_list = g_slist_append (agent->streams, stream);
if (modified_list) {
stream->id = agent->next_stream_id++;
g_debug ("allocating stream id %u (%p)", stream->id, stream);
/* note: generate ufrag/pwd for the stream (see ICE ID-15 15.4) */
nice_rng_generate_bytes_print (agent->rng, NICE_STREAM_MAX_UFRAG_LEN, stream->local_ufrag);
nice_rng_generate_bytes_print (agent->rng, NICE_STREAM_MAX_PWD_LEN, stream->local_password);
nice_rng_generate_bytes_print (agent->rng, NICE_STREAM_DEF_UFRAG - 1, stream->local_ufrag);
nice_rng_generate_bytes_print (agent->rng, NICE_STREAM_DEF_PWD - 1, stream->local_password);
agent->streams = modified_list;
}
else
stream_free (stream);
}
agent->streams = g_slist_append (agent->streams, stream);
/* note: error in allocating objects */
if (!modified_list)
return 0;
g_debug ("In %s mode, starting candidate gathering.", agent->full_mode ? "ICE-FULL" : "ICE-LITE");
......@@ -550,28 +608,41 @@ nice_agent_add_stream (
host_candidate = discovery_add_local_host_candidate (agent, stream->id,
stream->component->id, addr);
if (host_candidate &&
agent->full_mode &&
if (!host_candidate) {
stream->id = 0;
break;
}
if (agent->full_mode &&
agent->stun_server_ip) {
/* XXX: need to check for redundant candidates? -> not yet,
* this is done later on */
/* note: no need to check for redundant candidates, as this is
* done later on in the process */
cand = g_slice_new0 (CandidateDiscovery);
if (cand) {
modified_list = g_slist_append (agent->discovery_list, cand);
if (modified_list) {
agent->discovery_list = modified_list;
cand->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
cand->socket = host_candidate->sockptr->fileno;
cand->nicesock = host_candidate->sockptr;
cand->server_addr = agent->stun_server_ip;
cand->server_port = agent->stun_server_port;
cand->interface = addr;
cand->stream = stream;
/* XXX: not multi-component ready */
cand->component = stream->component;
cand->agent = agent;
g_debug ("Adding new srv-rflx candidate %p\n", cand);
agent->discovery_list = g_slist_append (agent->discovery_list, cand);
modified_list = g_slist_append (agent->discovery_list, cand);
if (modified_list) {
agent->discovery_list = modified_list;
++agent->discovery_unsched_items;
}
}
}
else {
/* note: memory allocation failure, return error */
stream->id = 0;
......@@ -582,24 +653,34 @@ nice_agent_add_stream (
/* step: attach the newly created sockets to the mainloop
* context */
if (agent->main_context_set)
if (agent->main_context_set && stream->id > 0)
priv_attach_new_stream (agent, stream);
/* note: no async discoveries pending, signal that we are ready */
if (agent->discovery_unsched_items == 0)
agent_signal_gathering_done (agent);
else if (agent->discovery_list)
else {
g_assert (agent->discovery_list);
discovery_schedule (agent);
}
return stream->id;
}
static void priv_remove_keepalive_timer (NiceAgent *agent)
{
if (agent->keepalive_timer_id) {
g_source_remove (agent->keepalive_timer_id),
agent->keepalive_timer_id = 0;
}
}
/**
* nice_agent_remove_stream:
* @agent: a NiceAgent
* @stream_id: the ID of the stream to remove
**/
void
NICEAPI_EXPORT void
nice_agent_remove_stream (
NiceAgent *agent,
guint stream_id)
......@@ -619,8 +700,11 @@ nice_agent_remove_stream (
/* remove the stream itself */
priv_deattach_stream (stream);
stream_free (stream);
agent->streams = g_slist_remove (agent->streams, stream);
stream_free (stream);
if (!agent->streams)
priv_remove_keepalive_timer (agent);
}
/**
......@@ -628,25 +712,28 @@ nice_agent_remove_stream (
* @agent: A NiceAgent
* @addr: the address of a local IP interface
*
* Inform the agent of the presence of an address that a local network
* interface is bound to.
* Inform the agent of the presence of an address that a local
* network interface is bound to.
*
* @return FALSE on fatal (memory allocation) errors
**/
void
NICEAPI_EXPORT gboolean
nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr)
{
NiceAddress *dup;
GSList *modified_list;
dup = nice_address_dup (addr);
dup->port = 0;
agent->local_addresses = g_slist_append (agent->local_addresses, dup);
/* XXX: Should we generate local candidates for existing streams at this
* point, or require that local addresses are set before media streams are
* added?
*/
modified_list = g_slist_append (agent->local_addresses, dup);
if (modified_list) {
agent->local_addresses = modified_list;
return TRUE;
}
return FALSE;
}
static void priv_add_remote_candidate (
static gboolean priv_add_remote_candidate (
NiceAgent *agent,
guint stream_id,
guint component_id,
......@@ -661,14 +748,22 @@ static void priv_add_remote_candidate (
{
Component *component;
NiceCandidate *candidate;
/* XXX: dear compiler, these are for you: */
(void)username; (void)password; (void)priority;
gchar *username_dup = NULL, *password_dup = NULL;
gboolean error_flag = FALSE;
if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
return;
return FALSE;
if (username)
username_dup = g_strdup (username);
if (password)
password_dup = g_strdup (password);
candidate = nice_candidate_new (type);
if (candidate) {
GSList *modified_list = g_slist_append (component->remote_candidates, candidate);
if (modified_list) {
component->remote_candidates = modified_list;
candidate->stream_id = stream_id;
candidate->component_id = component_id;
......@@ -676,26 +771,47 @@ static void priv_add_remote_candidate (
candidate->type = type;
if (addr)
candidate->addr = *addr;
#ifndef NDEBUG
{
gchar tmpbuf[INET6_ADDRSTRLEN];
nice_address_to_string ((NiceAddress *)addr, tmpbuf);
g_debug ("Adding remote candidate with addr %s:%u.", tmpbuf, addr->port);
}
#endif
if (related_addr)
candidate->base_addr = *related_addr;
candidate->transport = transport;
if (username)
candidate->username = g_strdup (username);
if (password)
candidate->password = g_strdup (password);
candidate->priority = priority;
candidate->username = username_dup;
candidate->password = password_dup;
if (foundation)
candidate->foundation = g_strdup (foundation);
component->remote_candidates = g_slist_append (component->remote_candidates,
candidate);
g_strlcpy (candidate->foundation, foundation, NICE_CANDIDATE_MAX_FOUNDATION);
/* XXX: may be called before candidate-gathering-done is signalled,
* make sure this is handled correctly! */
conn_check_add_for_candidate (agent, stream_id, component, candidate);
if (conn_check_add_for_candidate (agent, stream_id, component, candidate) < 0)
error_flag = TRUE;
}
else /* memory alloc error: list insert */
error_flag = TRUE;
}
else /* memory alloc error: candidate creation */
error_flag = TRUE;
if (error_flag) {
if (candidate)
nice_candidate_free (candidate);
g_free (username_dup);
g_free (password_dup);
return FALSE;
}
return TRUE;
}
/**
......@@ -711,7 +827,7 @@ static void priv_add_remote_candidate (
*
* @return TRUE on success
*/
gboolean
NICEAPI_EXPORT gboolean
nice_agent_set_remote_credentials (
NiceAgent *agent,
guint stream_id,
......@@ -723,8 +839,8 @@ nice_agent_set_remote_credentials (
/* note: oddly enough, ufrag and pwd can be empty strings */
if (stream && ufrag && pwd) {
strncpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG_LEN);
strncpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD_LEN);
g_strlcpy (stream->remote_ufrag, ufrag, NICE_STREAM_MAX_UFRAG);
g_strlcpy (stream->remote_password, pwd, NICE_STREAM_MAX_PWD);
return TRUE;
}
......@@ -744,7 +860,7 @@ nice_agent_set_remote_credentials (
*
* @return TRUE on success
*/
gboolean
NICEAPI_EXPORT gboolean
nice_agent_get_local_credentials (
NiceAgent *agent,
guint stream_id,
......@@ -776,8 +892,12 @@ nice_agent_get_local_credentials (
* @password: the new candidate's password (XXX: candidates don't have usernames)
*
* Add a candidate our peer has informed us about to the agent's list.
*
* Note: NICE_AGENT_MAX_REMOTE_CANDIDATES is the absolute
* maximum limit for remote candidates
* @return FALSE on fatal (memory alloc) errors
**/
void
NICEAPI_EXPORT gboolean
nice_agent_add_remote_candidate (
NiceAgent *agent,
guint stream_id,
......@@ -787,6 +907,7 @@ nice_agent_add_remote_candidate (
const gchar *username,
const gchar *password)
{
return
priv_add_remote_candidate (agent,
stream_id,
component_id,
......@@ -799,7 +920,7 @@ nice_agent_add_remote_candidate (
password,
NULL);
/* later: for each component, generate a new check with the new
/* XXX/later: for each component, generate a new check with the new
candidate, see below set_remote_candidates() */
}
......@@ -812,17 +933,25 @@ nice_agent_add_remote_candidate (
*
* Sets the remote candidates for a component of a stream. Replaces
* any existing remote candidates.
*
* Note: NICE_AGENT_MAX_REMOTE_CANDIDATES is the absolute
* maximum limit for remote candidates
*
* @return number of candidates added, negative on fatal (memory
* allocs) errors
**/
void
nice_agent_set_remote_candidates (NiceAgent *agent, guint stream_id, guint component_id, GSList *candidates)
NICEAPI_EXPORT int
nice_agent_set_remote_candidates (NiceAgent *agent, guint stream_id, guint component_id, const GSList *candidates)
{
GSList *i;
const GSList *i;
int added = 0;
/* XXX: clean up existing remote candidates, and abort any
* connectivity checks using these candidates */
for (i = candidates; i; i = i->next) {
for (i = candidates; i && added >= 0; i = i->next) {
NiceCandidateDesc *d = (NiceCandidateDesc*) i->data;
gboolean res =
priv_add_remote_candidate (agent,
stream_id,
component_id,
......@@ -834,29 +963,25 @@ nice_agent_set_remote_candidates (NiceAgent *agent, guint stream_id, guint compo
NULL,
NULL,
d->foundation);
if (res)
++added;
else
added = -1;
}
if (added > 0)
conn_check_schedule_next (agent);
}
#if 0
static NiceCandidate *
_local_candidate_lookup (NiceAgent *agent, guint candidate_id)
{
GSList *i;
for (i = agent->local_candidates; i; i = i->next)
{
NiceCandidate *c = i->data;
if (c->id == candidate_id)
return c;
}
return NULL;
return added;
}
#endif
/**
* Reads data from a ready, nonblocking socket attached to an ICE
* stream component.
*
* @return number of octets received, or zero on error
*/
static guint
_nice_agent_recv (
NiceAgent *agent,
......@@ -869,11 +994,17 @@ _nice_agent_recv (
NiceAddress from;
guint len;
g_debug ("Packet received on local socket %d.", udp_socket->fileno);
len = nice_udp_socket_recv (udp_socket, &from,
buf_len, buf);
#ifndef NDEBUG
{
gchar tmpbuf[INET6_ADDRSTRLEN];
nice_address_to_string (&from, tmpbuf);
g_debug ("Packet received on local socket %u from %s:%u (%u octets).", udp_socket->fileno, tmpbuf, from.port, len);
}
#endif
if (len == 0)
return 0;
......@@ -884,32 +1015,11 @@ _nice_agent_recv (
return 0;
}
/* XXX: verify sender; maybe:
*
* if (candidate->other != NULL)
* {
* if (from != candidate->other.addr)
* // ignore packet from unexpected sender
* return;
* }
* else
* {
* // go through remote candidates, looking for one matching packet from
* // address; if found, assign it to candidate->other and call handler,
* // otherwise ignore it
* }
/* step: check for a RTP fingerprint
*
* Perhaps remote socket affinity is superfluous and all we need is the
* second part.
* Perhaps we should also check whether this candidate is supposed to be
* active.
*/
/* The top two bits of an RTP message are the version number; the current
* The top two bits of an RTP message are the version number; the current
* version number is 2. The top two bits of a STUN message are always 0.
*/
/* step: check for a RTP fingerprint
*
* - XXX: should use a two-phase check, first a lightweight check,
* and then full validation */
if ((buf[0] & 0xc0) == 0x80)
......@@ -918,10 +1028,10 @@ _nice_agent_recv (
return len;
}
/* step: validate using the new STUN API */
/* - XXX: old check '((buf[0] & 0xc0) == 0)' */
/* - note: old check '((buf[0] & 0xc0) == 0)' */
else if (stun_validate ((uint8_t*)buf, len) > 0)
{
conn_check_handle_inbound_stun (agent, stream, component, &from, buf, len);
conn_check_handle_inbound_stun (agent, stream, component, udp_socket, &from, buf, len);
}
else
{
......@@ -929,33 +1039,9 @@ _nice_agent_recv (
return len;
}
/* code using the old SUTN API */
#if 0
{
/* looks like a STUN message (connectivity check) */
/* connectivity checks are described in ICE-13 §7. */
/* XXX: still using the old STUN API below
* - with new API, call stun_bind_process() or some such
* to process the incoming STUN packet */
StunMessage *msg;
msg = stun_message_unpack (len, buf);
if (msg != NULL)
{
conn_check_handle_inbound_stun_old (agent, stream, component, candidate, from, msg);
stun_message_free (msg);
}
}
#endif
/* anything else is ignored */
return 0;
}
/**
* nice_agent_recv:
* @agent: a NiceAgent
......@@ -968,7 +1054,7 @@ _nice_agent_recv (
*
* Returns: the amount of data read into @buf
**/
guint
NICEAPI_EXPORT guint
nice_agent_recv (
NiceAgent *agent,
guint stream_id,
......@@ -1027,11 +1113,12 @@ nice_agent_recv (
}
}
g_assert_not_reached ();
/* note: commented out to avoid compiler warnings
*
* g_assert_not_reached (); */
}
guint
NICEAPI_EXPORT guint
nice_agent_recv_sock (
NiceAgent *agent,
guint stream_id,
......@@ -1068,7 +1155,7 @@ nice_agent_recv_sock (
*
* Returns: A list of file descriptors from @other_fds that are readable
**/
GSList *
NICEAPI_EXPORT GSList *
nice_agent_poll_read (
NiceAgent *agent,
GSList *other_fds,
......@@ -1118,11 +1205,17 @@ nice_agent_poll_read (
for (j = 0; j <= max_fd; j++)
if (FD_ISSET (j, &fds))
{
if (g_slist_find (other_fds, GUINT_TO_POINTER (j)))
ret = g_slist_append (ret, GUINT_TO_POINTER (j));
if (g_slist_find (other_fds, GUINT_TO_POINTER (j))) {
GSList *modified_list = g_slist_append (ret, GUINT_TO_POINTER (j));
if (modified_list == NULL) {
g_slist_free (ret);
return NULL;
}
ret = modified_list;
}
else
{
NiceUDPSocket *socket;
NiceUDPSocket *socket = NULL;
Stream *stream = NULL;
gchar buf[MAX_STUN_DATAGRAM_PAYLOAD];
guint len;
......@@ -1157,7 +1250,15 @@ nice_agent_poll_read (
}
void
/**
* Sends a data payload over a stream component.
*
* @pre component state MUST be NICE_COMPONENT_STATE_READY
*
* @return number of bytes sent, or negative error code
*/
NICEAPI_EXPORT gint
nice_agent_send (
NiceAgent *agent,
guint stream_id,
......@@ -1168,7 +1269,7 @@ nice_agent_send (
Stream *stream;
Component *component;
/* XXX: dear compiler, these are for you: */
/* note: dear compiler, these are for you: */
(void)component_id;
stream = agent_find_stream (agent, stream_id);
......@@ -1181,15 +1282,19 @@ nice_agent_send (
NiceUDPSocket *sock;
NiceAddress *addr;
#if 1
#ifndef NDEBUG
g_debug ("s%d:%d: sending %d bytes to %08x:%d", stream_id, component_id,
len, component->selected_pair.remote->addr.addr_ipv4, component->selected_pair.remote->addr.port);
len, component->selected_pair.remote->addr.addr.addr_ipv4, component->selected_pair.remote->addr.port);
#endif
sock = component->selected_pair.local->sockptr;
addr = &component->selected_pair.remote->addr;
nice_udp_socket_send (sock, addr, len, buf);
component->media_after_tick = TRUE;
return len;
}
return -1;
}
......@@ -1201,9 +1306,9 @@ nice_agent_send (
* it. To get full results, the client should wait for the
* 'candidates-gathering-done' signal.
*
* Returns: a GSList of local candidates belonging to @agent
* Returns: a GSList of local candidates (NiceCandidate) belonging to @agent
**/
GSList *
NICEAPI_EXPORT GSList *
nice_agent_get_local_candidates (
NiceAgent *agent,
guint stream_id,
......@@ -1225,9 +1330,13 @@ nice_agent_get_local_candidates (
* The caller owns the returned GSList but not the candidates contained within
* it.
*
* Returns: a GSList of remote candidates belonging to @agent
* Note: the list of remote candidates can change during processing.
* The client should register for the "new-remote-candidate" signal to
* get notification of new remote candidates.
*
* Returns: a GSList of remote candidates (NiceCandidate) belonging to @agent
**/
GSList *
NICEAPI_EXPORT GSList *
nice_agent_get_remote_candidates (
NiceAgent *agent,
guint stream_id,
......@@ -1244,7 +1353,6 @@ nice_agent_get_remote_candidates (
return g_slist_copy (component->remote_candidates);
}
static void
nice_agent_dispose (GObject *object)
{
......@@ -1259,6 +1367,8 @@ nice_agent_dispose (GObject *object)
conn_check_free (agent);
g_assert (agent->conncheck_list == NULL);
priv_remove_keepalive_timer (agent);
for (i = agent->local_addresses; i; i = i->next)
{
NiceAddress *a = i->data;
......@@ -1345,7 +1455,7 @@ nice_agent_g_source_cb (
gchar buf[MAX_STUN_DATAGRAM_PAYLOAD];
guint len;
/* XXX: dear compiler, these are for you: */
/* note: dear compiler, these are for you: */
(void)source;
len = _nice_agent_recv (agent, stream, component, ctx->socket,
......@@ -1375,6 +1485,7 @@ static gboolean priv_attach_new_stream (NiceAgent *agent, Stream *stream)
GIOChannel *io;
GSource *source;
IOCtx *ctx;
GSList *modified_list;
io = g_io_channel_unix_new (udp_socket->fileno);
source = g_io_create_watch (io, G_IO_IN);
......@@ -1383,11 +1494,12 @@ static gboolean priv_attach_new_stream (NiceAgent *agent, Stream *stream)
ctx, (GDestroyNotify) io_ctx_free);
g_debug ("Attach source %p (stream %u).", source, stream->id);
g_source_attach (source, NULL);
component->gsources = g_slist_append (component->gsources, source);
if (!component->gsources) {
modified_list = g_slist_append (component->gsources, source);
if (!modified_list) {
g_source_destroy (source);
return FALSE;
}
component->gsources = modified_list;
}
return TRUE;
......@@ -1415,7 +1527,7 @@ static void priv_deattach_stream (Stream *stream)
component->gsources = NULL;
}
gboolean
NICEAPI_EXPORT gboolean
nice_agent_main_context_attach (
NiceAgent *agent,
GMainContext *ctx,
......@@ -1427,9 +1539,6 @@ nice_agent_main_context_attach (
if (agent->main_context_set)
return FALSE;
/* XXX: when sockets are not yet created, or new streams are added,
* the mainloop integration won't then work anymore! */
/* attach candidates */
for (i = agent->streams; i; i = i->next) {
......
......@@ -70,6 +70,13 @@ G_BEGIN_DECLS
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
NICE_TYPE_AGENT, NiceAgentClass))
/**
* A hard limit for number for remote candidates. This
* limit is enforced to protect against malevolent remote
* clients.
*/
#define NICE_AGENT_MAX_REMOTE_CANDIDATES 25
typedef enum
{
NICE_COMPONENT_STATE_DISCONNECTED, /* no activity scheduled */
......@@ -92,13 +99,13 @@ typedef enum
typedef struct _NiceCandidateDesc NiceCandidateDesc;
struct _NiceCandidateDesc {
const gchar *foundation;
gchar *foundation;
guint component_id;
NiceCandidateTransport transport;
guint32 priority;
const NiceAddress *addr;
NiceAddress *addr;
NiceCandidateType type;
const NiceAddress *related_addr; /* optional */
NiceAddress *related_addr; /* optional */
};
typedef struct _NiceAgent NiceAgent;
......@@ -120,7 +127,7 @@ GType nice_agent_get_type (void);
NiceAgent *
nice_agent_new (NiceUDPSocketFactory *factory);
void
gboolean
nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr);
guint
......@@ -145,7 +152,7 @@ nice_agent_get_local_credentials (
guint stream_id,
const gchar **ufrag, const gchar **pwd);
void
gboolean
nice_agent_add_remote_candidate (
NiceAgent *agent,
guint stream_id,
......@@ -155,12 +162,12 @@ nice_agent_add_remote_candidate (
const gchar *username,
const gchar *password);
void
int
nice_agent_set_remote_candidates (
NiceAgent *agent,
guint stream_id,
guint component_id,
GSList *candidates);
const GSList *candidates);
guint
nice_agent_recv (
......@@ -186,7 +193,7 @@ nice_agent_poll_read (
NiceAgentRecvFunc func,
gpointer data);
void
gint
nice_agent_send (
NiceAgent *agent,
guint stream_id,
......
......@@ -36,6 +36,15 @@
* file under either the MPL or the LGPL.
*/
/**
* @file candidate.c
* @brief ICE candidate functions
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "agent.h"
#include "component.h"
......@@ -44,7 +53,7 @@
* candidates, server reflexive candidates, and relayed candidates. */
NiceCandidate *
NICEAPI_EXPORT NiceCandidate *
nice_candidate_new (NiceCandidateType type)
{
NiceCandidate *candidate;
......@@ -55,28 +64,22 @@ nice_candidate_new (NiceCandidateType type)
}
void
NICEAPI_EXPORT void
nice_candidate_free (NiceCandidate *candidate)
{
/* better way of checking if socket is allocated? */
if (candidate->source)
g_source_destroy (candidate->source);
if (candidate->username)
g_free (candidate->username);
if (candidate->password)
g_free (candidate->password);
if (candidate->foundation)
g_free (candidate->foundation);
g_slice_free (NiceCandidate, candidate);
}
gfloat
NICEAPI_EXPORT gfloat
nice_candidate_jingle_priority (NiceCandidate *candidate)
{
switch (candidate->type)
......@@ -94,7 +97,7 @@ nice_candidate_jingle_priority (NiceCandidate *candidate)
/* ICE-15 §4.1.2.1; returns number between 1 and 0x7effffff */
G_GNUC_CONST
guint32
NICEAPI_EXPORT guint32
nice_candidate_ice_priority_full (
// must be ∈ (0, 126) (max 2^7 - 2)
guint type_preference,
......@@ -111,7 +114,7 @@ nice_candidate_ice_priority_full (
G_GNUC_CONST
guint32
NICEAPI_EXPORT guint32
nice_candidate_ice_priority (const NiceCandidate *candidate)
{
guint8 type_preference = 0;
......@@ -135,7 +138,7 @@ nice_candidate_ice_priority (const NiceCandidate *candidate)
/**
* Calculates the pair priority as specified in ICE -15 spec 5.7.2.
*/
guint64
NICEAPI_EXPORT guint64
nice_candidate_pair_priority (guint32 o_prio, guint32 a_prio)
{
guint32 max = o_prio > a_prio ? o_prio : a_prio;
......
......@@ -48,6 +48,8 @@ G_BEGIN_DECLS
#define NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE 100
#define NICE_CANDIDATE_TYPE_PREF_RELAYED 60
#define NICE_CANDIDATE_MAX_FOUNDATION 16
typedef enum
{
NICE_CANDIDATE_TYPE_HOST,
......@@ -72,11 +74,10 @@ struct _NiceCandidate
guint32 priority;
guint stream_id;
guint component_id;
gchar *foundation;
NiceUDPSocket *sockptr; /* XXX: to replace 'sock', see comment above */
gchar foundation[NICE_CANDIDATE_MAX_FOUNDATION];
NiceUDPSocket *sockptr;
gchar *username; /* pointer to a NULL-terminated username string */
gchar *password; /* pointer to a NULL-terminated password string */
GSource *source;
};
......
#! /bin/sh
STUND=../stun/stund
echo "Starting ICE full-mode with STUN unit test."
[ -e "$STUND" ] || {
echo "STUN server not found: Cannot run unit test!" >&2
exit 77
}
set -x
pidfile=./stund.pid
export NICE_STUN_SERVER=127.0.0.1
export NICE_STUN_SERVER_PORT=3800
echo "Launching stund on port ${NICE_STUN_SERVER_PORT}."
rm -f -- "$pidfile"
(sh -c "echo \$\$ > \"$pidfile\" && exec ../stun/stund ${NICE_STUN_SERVER_PORT}") &
sleep 1
./test-fullmode
error=$?
kill -- "$(cat "$pidfile")"
wait
exit ${error}
......@@ -35,6 +35,15 @@
* file under either the MPL or the LGPL.
*/
/**
* @file component.c
* @brief ICE component functions
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "component.h"
Component *
......
......@@ -80,6 +80,8 @@ struct _Component
GSList *gsources; /**< list of GSource objs */
CandidatePair selected_pair; /**< independent from checklists,
see ICE 11.1.1 (ID-15) */
gboolean media_after_tick; /**< true if media received since last
keepalive tick */
/* XXX: **to be removed**
* --cut--
......
......@@ -36,22 +36,33 @@
* file under either the MPL or the LGPL.
*/
/**
* @file conncheck.c
* @brief ICE connectivity checks
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <errno.h>
#include <string.h>
#include <glib.h>
#ifndef _BSD_SOURCE
#error "timercmp() macros needed"
#endif
#include <sys/time.h> /* timercmp() macro, BSD */
#include "agent.h"
#include "agent-priv.h"
#include "conncheck.h"
#include "discovery.h"
#include "stun.h"
#include "stun-msg.h"
static inline int priv_timer_expired (GTimeVal *restrict timer, GTimeVal *restrict now)
{
return (now->tv_sec == timer->tv_sec) ?
now->tv_usec >= timer->tv_usec :
now->tv_sec >= timer->tv_sec;
}
static void priv_update_check_list_state (NiceAgent *agent, Stream *stream);
......@@ -62,8 +73,8 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
{
GSList *i;
/* XXX: should return the highest priority check on the
* waiting list! */
/* note: list is sorted in priority order to first waiting check has
* the highest priority */
for (i = conn_check_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
......@@ -82,7 +93,7 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
{
g_get_current_time (&pair->next_tick);
g_time_val_add (&pair->next_tick, 6000); /* XXX: 600msec */
g_time_val_add (&pair->next_tick, agent->timer_ta * 10);
pair->state = NICE_CHECK_IN_PROGRESS;
conn_check_send (agent, pair);
return TRUE;
......@@ -101,6 +112,9 @@ static gboolean priv_conn_check_unfreeze_next (GSList *conncheck_list)
GSList *i;
int c;
/* note: the pair list is already sorted so separate prio check
* is not needed */
for (c = NICE_COMPONENT_TYPE_RTP; (pair == NULL) && c < NICE_COMPONENT_TYPE_RTCP; c++) {
for (i = conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
......@@ -128,7 +142,6 @@ static gboolean priv_conn_check_unfreeze_next (GSList *conncheck_list)
* This function is designed for the g_timeout_add() interface.
*
* @return will return FALSE when no more pending timers.
*/
static gboolean priv_conn_check_tick (gpointer pointer)
{
......@@ -136,9 +149,12 @@ static gboolean priv_conn_check_tick (gpointer pointer)
NiceAgent *agent = pointer;
GSList *i;
gboolean keep_timer_going = FALSE;
GTimeVal now;
int frozen = 0, inprogress = 0, waiting = 0, succeeded = 0, nominated = 0;
pair = priv_conn_check_find_next_waiting (agent->conncheck_list);
#ifdef DEBUG
#ifndef NDEBUG
{
static int tick_counter = 0;
if (++tick_counter % 20 == 0)
......@@ -158,10 +174,6 @@ static gboolean priv_conn_check_tick (gpointer pointer)
keep_timer_going = TRUE;
}
{
GTimeVal now;
int frozen = 0, inprogress = 0, waiting = 0;
g_get_current_time (&now);
for (i = agent->conncheck_list; i ; i = i->next) {
......@@ -173,10 +185,10 @@ static gboolean priv_conn_check_tick (gpointer pointer)
g_debug ("STUN connectivity check was cancelled, marking as done.");
p->state = NICE_CHECK_FAILED;
}
else if (timercmp(&p->next_tick, &now, <=)) {
else if (priv_timer_expired (&p->next_tick, &now)) {
int res = stun_bind_elapse (p->stun_ctx);
if (res == EAGAIN) {
/* case: not ready complete, so schedule next timeout */
/* case: not ready, so schedule a new timeout */
unsigned int timeout = stun_bind_timeout (p->stun_ctx);
/* note: convert from milli to microseconds for g_time_val_add() */
......@@ -184,49 +196,143 @@ static gboolean priv_conn_check_tick (gpointer pointer)
g_time_val_add (&p->next_tick, timeout * 10);
keep_timer_going = TRUE;
p->traffic_after_tick = TRUE; /* for keepalive timer */
}
else {
/* case: error, abort processing */
g_debug ("Retransmissions failed, giving up on connectivity check %p", p);
p->state = NICE_CHECK_FAILED;
p->stun_ctx = NULL;
}
}
}
if (p->state == NICE_CHECK_FROZEN)
++frozen;
if (p->state == NICE_CHECK_IN_PROGRESS)
else if (p->state == NICE_CHECK_IN_PROGRESS)
++inprogress;
else if (p->state == NICE_CHECK_WAITING)
++waiting;
else if (p->state == NICE_CHECK_SUCCEEDED)
++succeeded;
if (p->nominated)
++nominated;
}
#ifdef DEBUG
#ifndef NDEBUG
{
static int tick_counter = 0;
if (++tick_counter % 20 == 0 || keep_timer_going != TRUE)
g_debug ("timer: %d frozen, %d in-progress, %d waiting.", frozen, inprogress, waiting);
g_debug ("timer: %d frozen, %d in-progress, %d waiting, %d succeeded, %d nominated.",
frozen, inprogress, waiting, succeeded, nominated);
}
#endif
/* note: keep the timer going as long as there is work to be done */
if (frozen || inprogress || waiting)
if (frozen || inprogress || waiting ||
(succeeded && nominated == 0))
keep_timer_going = TRUE;
/* step: nominate some candidate
* - no work left but still no nominated checks (possibly
* caused by a controlling-controlling role conflict)
*/
if (agent->controlling_mode &&
(!frozen && !inprogress && !waiting && !nominated)) {
g_debug ("no nominated checks, resending checks to select one");
for (i = agent->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
/* XXX: should we pick highest priority one? probably yes. */
if (p->state == NICE_CHECK_SUCCEEDED ||
p->state == NICE_CHECK_DISCOVERED) {
g_debug ("restarting check %p as the nominated pair.", p);
p->nominated = TRUE;
priv_conn_check_initiate (agent, p);
keep_timer_going = TRUE;
}
}
}
/* step: stop timer if no work left */
if (keep_timer_going != TRUE) {
g_debug ("%s: stopping conncheck timer", G_STRFUNC);
for (i = agent->streams; i; i = i->next) {
Stream *stream = i->data;
priv_update_check_list_state (agent, stream);
}
conn_check_free (agent);
agent->conncheck_state = NICE_CHECKLIST_COMPLETED;
/* XXX: what to signal, is all processing now really done? */
g_debug ("changing conncheck state to COMPLETED.");
}
return keep_timer_going;
}
/**
* Timer callback that handles initiating and managing connectivity
* checks (paced by the Ta timer).
*
* This function is designed for the g_timeout_add() interface.
*
* @return will return FALSE when no more pending timers.
*/
static gboolean priv_conn_keepalive_tick (gpointer pointer)
{
NiceAgent *agent = pointer;
GSList *i;
int errors = 0;
g_debug ("%s", G_STRFUNC);
/* case 1: session established and media flowing
* (ref ICE sect 10 ID-16) */
for (i = agent->streams; i; i = i->next) {
/* XXX: not multi-component ready */
Stream *stream = i->data;
Component *component = stream->component;
if (component->selected_pair.local != NULL &&
component->media_after_tick != TRUE) {
CandidatePair *p = &component->selected_pair;
struct sockaddr sockaddr;
int res;
memset (&sockaddr, 0, sizeof (sockaddr));
nice_address_copy_to_sockaddr (&p->remote->addr, &sockaddr);
res = stun_bind_keepalive (p->local->sockptr->fileno,
&sockaddr, sizeof (sockaddr));
g_debug ("stun_bind_keepalive for pair %p res %d.", p, res);
if (res < 0)
++errors;
}
component->media_after_tick = FALSE;
}
/* case 2: connectivity establishment ongoing
* (ref ICE sect 4.1.1.5 ID-15) */
if (agent->conncheck_state == NICE_CHECKLIST_RUNNING) {
for (i = agent->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->traffic_after_tick != TRUE) {
g_debug ("resending STUN-CC to keep the candidate alive (pair %p).", p);
conn_check_send (agent, p);
}
p->traffic_after_tick = FALSE;
}
}
if (errors) {
g_debug ("%s: stopping keepalive timer", G_STRFUNC);
return FALSE;
}
return TRUE;
}
/**
* Initiates the next pending connectivity check.
*/
......@@ -234,18 +340,85 @@ void conn_check_schedule_next (NiceAgent *agent)
{
gboolean c = priv_conn_check_unfreeze_next (agent->conncheck_list);
if (agent->discovery_unsched_items > 0)
g_debug ("WARN: starting conn checks before local candidate gathering is finished.");
if (c == TRUE) {
/* step: call once imediately */
gboolean res = priv_conn_check_tick ((gpointer) agent);
/* step: schedule timer if not running yet */
/* XXX: make timeout Ta configurable */
if (agent->conncheck_timer_id == 0 && res != FALSE)
agent->conncheck_timer_id =
g_timeout_add (NICE_AGENT_TIMER_TA_DEFAULT, priv_conn_check_tick, agent);
g_timeout_add (agent->timer_ta, priv_conn_check_tick, agent);
/* step: also start the keepalive timer */
if (agent->keepalive_timer_id == 0)
agent->keepalive_timer_id =
g_timeout_add (NICE_AGENT_TIMER_TR_DEFAULT, priv_conn_keepalive_tick, agent);
}
}
/**
* Compares two connectivity check items. Checkpairs are sorted
* in descending priority order, with highest priority item at
* the start of the list.
*/
gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b)
{
if (a->priority > b->priority)
return -1;
else if (a->priority < b->priority)
return 1;
return 0;
}
static gboolean priv_add_new_check_pair (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *local, NiceCandidate *remote, NiceCheckState initial_state, gboolean use_candidate)
{
gboolean result = FALSE;
CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
if (pair) {
pair->agent = agent;
pair->stream_id = stream_id;
pair->component_id = component->id;;
pair->local = local;
pair->remote = remote;
g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation);
if (agent->controlling_mode == TRUE)
pair->priority = nice_candidate_pair_priority (local->priority, remote->priority);
else
pair->priority = nice_candidate_pair_priority (remote->priority, local->priority);
pair->state = NICE_CHECK_FROZEN;
pair->nominated = use_candidate;
/* note: for the first added check */
if (!agent->conncheck_list)
agent->conncheck_state = NICE_CHECKLIST_RUNNING;
{
GSList *modified_list =
g_slist_insert_sorted (agent->conncheck_list, pair, (GCompareFunc)conn_check_compare);
if (modified_list) {
agent->conncheck_list = modified_list;
result = TRUE;
g_debug ("added a new conncheck item with foundation of '%s'.", pair->foundation);
}
else { /* memory alloc failed: list insert */
conn_check_free_item (pair, NULL);
agent->conncheck_state = NICE_CHECKLIST_FAILED;
}
}
}
else { /* memory alloc failed: new pair */
agent->conncheck_state = NICE_CHECKLIST_FAILED;
}
return result;
}
/**
* Forms new candidate pairs by matching the new remote candidate
* 'remote_cand' with all existing local candidates of 'component'.
......@@ -255,58 +428,40 @@ void conn_check_schedule_next (NiceAgent *agent)
* @param component pointer to the component
* @param remote remote candidate to match with
*
* @return non-zero on error
* @return number of checks added, negative on fatal errors
*/
int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote)
{
GSList *i;
int res = 0;
int added = 0;
for (i = component->local_candidates; i ; i = i->next) {
NiceCandidate *local = i->data;
CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
if (pair) {
/* XXX: as per -15 5.7.3, filter pairs where local candidate is
srvrflx and base matches a local candidate for which there
already is a check pair
*/
pair->agent = agent;
pair->stream_id = stream_id;
pair->component_id = component->id;;
pair->local = local; /* XXX: hmm, do we need reference counting,
or we just make sure all connchecks are
destroyed before any components of a stream...? */
pair->remote = remote;
pair->foundation = g_strdup_printf ("%s%s", local->foundation, remote->foundation);
if (!pair->foundation) {
g_slice_free (CandidateCheckPair, pair);
res = -1;
break;
}
/* note: match pairs only if transport and address family are the same */
if (local->transport == remote->transport &&
local->addr.type == remote->addr.type) {
pair->priority = nice_candidate_pair_priority (local->priority, remote->priority);
pair->state = NICE_CHECK_FROZEN;
gboolean result;
if (!agent->conncheck_list)
agent->conncheck_state = NICE_CHECKLIST_RUNNING;
agent->conncheck_list = g_slist_append (agent->conncheck_list, pair);
if (!agent->conncheck_list)
agent->conncheck_state = NICE_CHECKLIST_FAILED;
/* note: do not create pairs where local candidate is
* a srv-reflexive (ICE 5.7.3 ID-15) */
if (local->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE)
continue;
g_debug ("added a new conncheck item with foundation of '%s'.", pair->foundation);
result = priv_add_new_check_pair (agent, stream_id, component, local, remote, NICE_CHECK_FROZEN, FALSE);
if (result) {
++added;
}
else {
res = -1;
added = -1;
break;
}
}
}
return res;
return added;
}
/**
......@@ -317,9 +472,6 @@ void conn_check_free_item (gpointer data, gpointer user_data)
{
CandidateCheckPair *pair = data;
g_assert (user_data == NULL);
if (pair->foundation)
g_free (pair->foundation),
pair->foundation = NULL;
if (pair->stun_ctx)
stun_bind_cancel (pair->stun_ctx),
pair->stun_ctx = NULL;
......@@ -335,12 +487,12 @@ void conn_check_free (NiceAgent *agent)
g_slist_foreach (agent->conncheck_list, conn_check_free_item, NULL);
g_slist_free (agent->conncheck_list),
agent->conncheck_list = NULL;
agent->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
}
if (agent->conncheck_timer_id) {
g_source_remove (agent->conncheck_timer_id),
agent->conncheck_timer_id = 0;
}
agent->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
}
}
/**
......@@ -373,8 +525,10 @@ gboolean conn_check_prune_stream (NiceAgent *agent, guint stream_id)
i = i->next;
}
if (!agent->conncheck_list)
if (!agent->conncheck_list) {
agent->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
conn_check_free (agent);
}
/* return FALSE if there was a memory allocation failure */
if (agent->conncheck_list == NULL && i != NULL)
......@@ -385,24 +539,28 @@ gboolean conn_check_prune_stream (NiceAgent *agent, guint stream_id)
/**
* Returns a username string for use in an outbound connectivity
* check. The caller is responsible for freeing the returned
* string.
* Fills 'dest' with a username string for use in an outbound connectivity
* checks. No more than 'dest_len' characters (including terminating
* NULL) is ever written to the 'dest'.
*/
static gchar *priv_create_check_username (NiceAgent *agent, CandidateCheckPair *pair)
static gboolean priv_create_check_username (NiceAgent *agent, CandidateCheckPair *pair, gchar *dest, guint dest_len)
{
Stream *stream;
if (pair &&
pair->remote && pair->remote->username &&
pair->local && pair->local->username)
return g_strconcat (pair->remote->username, ":", pair->local->username, NULL);
pair->local && pair->local->username) {
g_snprintf (dest, dest_len, "%s:%s", pair->remote->username, pair->local->username);
return TRUE;
}
stream = agent_find_stream (agent, pair->stream_id);
if (stream)
return g_strconcat (stream->remote_ufrag, ":", stream->local_ufrag, NULL);
if (stream) {
g_snprintf (dest, dest_len, "%s:%s", stream->remote_ufrag, stream->local_ufrag);
return TRUE;
}
return NULL;
return FALSE;
}
/**
......@@ -426,6 +584,8 @@ static const gchar *priv_create_check_password (NiceAgent *agent, CandidateCheck
/**
* Sends a connectivity check over candidate pair 'pair'.
*
* @return zero on success, non-zero on error
*/
int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
{
......@@ -443,46 +603,59 @@ int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE,
1,
pair->local->component_id);
/* XXX: memory allocs: */
gchar *username = priv_create_check_username (agent, pair);
gboolean username_filled =
priv_create_check_username (agent, pair, agent->ufragtmp, NICE_STREAM_MAX_UNAME);
const gchar *password = priv_create_check_password (agent, pair);
bool controlling = agent->controlling_mode;
/* XXX: add API to support different nomination modes: */
bool cand_use = controlling;
uint64_t tie;
struct sockaddr sockaddr;
unsigned int timeout;
memset (&sockaddr, 0, sizeof (sockaddr));
nice_rng_generate_bytes (agent->rng, 4, (gchar*)&tie);
nice_address_copy_to_sockaddr (&pair->remote->addr, &sockaddr);
g_debug ("sending STUN conncheck, port:%u, socket:%u, tie:%llu, username:'%s', password:'%s', priority:%u.",
#ifndef NDEBUG
{
gchar tmpbuf[INET6_ADDRSTRLEN];
nice_address_to_string (&pair->remote->addr, tmpbuf);
g_debug ("STUN-CC REQ to '%s:%u', socket=%u, pair=%s, tie=%llu, username='%s', password='%s', priority=%u.",
tmpbuf,
ntohs(((struct sockaddr_in*)(&sockaddr))->sin_port),
pair->local->sockptr->fileno,
(unsigned long long)tie,
username, password, priority);
pair->foundation,
(unsigned long long)agent->tie_breaker,
agent->ufragtmp, password, priority);
}
#endif
if (cand_use)
pair->nominated = TRUE;
pair->nominated = controlling;
if (username_filled) {
if (pair->stun_ctx)
stun_bind_cancel (pair->stun_ctx);
stun_conncheck_start (&pair->stun_ctx, pair->local->sockptr->fileno,
&sockaddr, sizeof (sockaddr),
username, password,
agent->ufragtmp, password,
cand_use, controlling, priority,
tie);
agent->tie_breaker);
timeout = stun_bind_timeout (pair->stun_ctx);
/* note: convert from milli to microseconds for g_time_val_add() */
g_get_current_time (&pair->next_tick);
g_time_val_add (&pair->next_tick, timeout * 10);
g_debug ("set timeout for conncheck %p to %u.", pair, timeout);
if (username)
g_free (username);
pair->traffic_after_tick = TRUE; /* for keepalive timer */
}
return 0;
}
......@@ -500,41 +673,35 @@ static void priv_update_check_list_state (NiceAgent *agent, Stream *stream)
/* note: iterate the conncheck list for each component separately */
for (c = 0; c < stream->n_components; c++) {
guint not_failed = 0;
guint not_failed = 0, checks = 0;
for (i = agent->conncheck_list; i; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->stream_id == stream->id &&
p->component_id == (c + 1)) {
++checks;
if (p->state != NICE_CHECK_FAILED)
++not_failed;
if (p->state == NICE_CHECK_SUCCEEDED &&
if ((p->state == NICE_CHECK_SUCCEEDED ||
p->state == NICE_CHECK_DISCOVERED) &&
p->nominated == TRUE)
break;
}
}
/* note: all checks have failed */
if (!not_failed)
if (checks > 0 && not_failed == 0)
agent_signal_component_state_change (agent,
stream->id,
(c + 1), /* component-id */
NICE_COMPONENT_STATE_FAILED);
/* note: no pair was ready&nominated */
/* note: no pair was ready/discovered and nominated */
if (i == NULL)
++completed;
}
if (completed == stream->n_components) {
/* note: all components completed */
/* XXX: not really true as there can be checks for multiple
* streams in the conncheck list... :o */
agent->conncheck_state = NICE_CHECKLIST_COMPLETED;
/* XXX: what to signal, is all processing now really done? */
g_debug ("changing conncheck state to COMPLETED)");
}
}
/**
......@@ -554,7 +721,8 @@ static void priv_update_check_list_state_for_component (NiceAgent *agent, Stream
for (i = agent->conncheck_list; i; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->component_id == component->id) {
if (p->state == NICE_CHECK_SUCCEEDED) {
if (p->state == NICE_CHECK_SUCCEEDED ||
p->state == NICE_CHECK_DISCOVERED) {
++succeeded;
if (p->nominated == TRUE) {
++nominated;
......@@ -612,6 +780,78 @@ static gboolean priv_update_selected_pair (NiceAgent *agent, Component *componen
return TRUE;
}
/**
* Schedules a triggered check after a succesfully inbound
* connectivity check. Implements ICE sect 7.2.1.4 (ID-16).
*
* @param agent self pointer
* @param component the check is related to
* @param local_socket socket from which the inbound check was received
* @param remote_cand remote candidate from which the inbound check was sent
* @param use_candidate whether the original check had USE-CANDIDATE attribute set
*/
static gboolean priv_schedule_triggered_check (NiceAgent *agent, Stream *stream, Component *component, NiceUDPSocket *local_socket, NiceCandidate *remote_cand, gboolean use_candidate)
{
GSList *i;
for (i = agent->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->component_id == component->id &&
p->remote == remote_cand &&
p->local->sockptr == local_socket) {
g_debug ("Found a matching pair %p for triggered check.", p);
if (p->state == NICE_CHECK_WAITING ||
p->state == NICE_CHECK_FROZEN)
priv_conn_check_initiate (agent, p);
if (p->state == NICE_CHECK_IN_PROGRESS) {
/* XXX: according to ICE 7.2.1.4 (ID-16), we should cancel
* the existing one, and send a new one...? :P */
g_debug ("Skipping triggered check, already in progress..");
}
else if (p->state == NICE_CHECK_SUCCEEDED ||
p->state == NICE_CHECK_DISCOVERED) {
g_debug ("Skipping triggered check, already completed..");
/* note: this is a bit unsure corner-case -- let's do the
same state update as for processing responses to our own checks */
priv_update_check_list_state_for_component (agent, stream, component);
/* note: to take care of the controlling-controlling case in
* aggressive nomination mode, send a new triggered
* check to nominate the pair */
if (agent->controlling_mode)
priv_conn_check_initiate (agent, p);
}
/* note: the spec says the we SHOULD retransmit in-progress
* checks immediately, but we won't do that now */
return TRUE;
}
}
{
gboolean result = FALSE;
NiceCandidate *local = NULL;
for (i = component->local_candidates; i ; i = i->next) {
local = i->data;
if (local->sockptr == local_socket)
break;
}
if (local) {
g_debug ("Adding a triggered check to conn.check list.");
result = priv_add_new_check_pair (agent, stream->id, component, local, remote_cand, NICE_CHECK_WAITING, use_candidate);
}
else
g_debug ("Didn't find a matching pair for triggered check (remote-cand=%p).", remote_cand);
}
return FALSE;
}
/**
* The remote party has signalled that the candidate pair
* described by 'component' and 'remotecand' is nominated
......@@ -626,112 +866,171 @@ static void priv_mark_pair_nominated (NiceAgent *agent, Component *component, Ni
/* step: search for at least one nominated pair */
for (i = agent->conncheck_list; i; i = i->next) {
CandidateCheckPair *pair = i->data;
/* XXX: hmm, how figure out to which local candidate the
/* XXX: hmm, how to figure out to which local candidate the
* check was sent to? let's mark all matching pairs
* as nominated instead */
if (pair->remote == remotecand) {
g_debug ("marking pair %p (%s) as nominated", pair, pair->foundation);
pair->nominated = TRUE;
if (pair->state == NICE_CHECK_SUCCEEDED)
if (pair->state == NICE_CHECK_SUCCEEDED ||
pair->state == NICE_CHECK_DISCOVERED)
priv_update_selected_pair (agent, component, pair);
}
}
}
gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Component *component, const NiceAddress *from, gchar *buf, guint len)
/**
* Sends a reply to an succesfully received STUN connectivity
* check request. Implements parts of the ICE spec section 7.2 (STUN
* Server Procedures).
*/
static void priv_reply_to_conn_check (NiceAgent *agent, Stream *stream, Component *component, NiceCandidate *cand, NiceUDPSocket *udp_socket, size_t rbuf_len, uint8_t *rbuf, gboolean use_candidate)
{
struct sockaddr sockaddr;
uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
ssize_t res;
size_t rbuf_len = sizeof (rbuf);
bool control = agent->controlling_mode;
uint64_t tie = -1; /* XXX: fix properly */
nice_address_copy_to_sockaddr (from, &sockaddr);
/* note: contents of 'buf' already validated, so it is
* a valid and full received STUN message */
/* note: ICE ID-15, 7.2 */
res = stun_conncheck_reply (rbuf, &rbuf_len, (const uint8_t*)buf, &sockaddr, sizeof (sockaddr),
stream->local_password, &control, tie);
if (res == 0) {
/* case 1: valid incoming request, send a reply */
GSList *i;
NiceUDPSocket *local_sock = NULL;
bool use_candidate =
stun_conncheck_use_candidate ((const uint8_t*)buf);
if (stream->initial_binding_request_received != TRUE)
agent_signal_initial_binding_request_received (agent, stream);
if (control != agent->controlling_mode) {
g_debug ("Conflict in controller selection, switching to mode %d.", control);
agent->controlling_mode = control;
#ifndef NDEBUG
{
gchar tmpbuf[INET6_ADDRSTRLEN];
nice_address_to_string (&cand->addr, tmpbuf);
g_debug ("STUN-CC RESP to '%s:%u', socket=%u, len=%u, cand=%p, use-cand=%d.",
tmpbuf,
cand->addr.port,
udp_socket->fileno,
rbuf_len,
cand,
(int)use_candidate);
}
#endif
/* XXX/hack: until the socket refactoring is done, use the below hack */
for (i = component->local_candidates; i; i = i->next) {
NiceCandidate *cand = i->data;
if (cand->sockptr->fileno > 0)
local_sock = cand->sockptr;
}
for (i = component->remote_candidates; local_sock && i; i = i->next) {
NiceCandidate *cand = i->data;
if (nice_address_equal (from, &cand->addr)) {
g_debug ("Sending a conncheck reply (fileno=%u, addr=%p, res=%u, buf=%p).",
local_sock->fileno, &cand->addr, rbuf_len, rbuf);
nice_udp_socket_send (local_sock, &cand->addr, rbuf_len, (const gchar*)rbuf);
nice_udp_socket_send (udp_socket, &cand->addr, rbuf_len, (const gchar*)rbuf);
if (use_candidate)
priv_mark_pair_nominated (agent, component, cand);
/* XXX: perform a triggered check */
/* note: upon succesful check, make the reserve check immediately */
priv_schedule_triggered_check (agent, stream, component, udp_socket, cand, use_candidate);
}
break;
/**
* Adds a new pair, discovered from an incoming STUN response, to
* the connectivity check list.
*
* @return created pair, or NULL on fatal (memory allocation) errors
*/
static CandidateCheckPair *priv_add_peer_reflexive_pair (NiceAgent *agent, guint stream_id, guint component_id, NiceCandidate *local_cand, CandidateCheckPair *parent_pair)
{
CandidateCheckPair *pair = g_slice_new0 (CandidateCheckPair);
if (pair) {
GSList *modified_list = g_slist_append (agent->conncheck_list, pair);
if (modified_list) {
agent->conncheck_list = modified_list;
pair->agent = agent;
pair->stream_id = stream_id;
pair->component_id = component_id;;
pair->local = local_cand;
pair->remote = parent_pair->remote;
pair->state = NICE_CHECK_DISCOVERED;
g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local_cand->foundation, parent_pair->remote->foundation);
if (agent->controlling_mode == TRUE)
pair->priority = nice_candidate_pair_priority (local_cand->priority, parent_pair->remote->priority);
else
pair->priority = nice_candidate_pair_priority (parent_pair->remote->priority, local_cand->priority);
pair->nominated = FALSE;
g_debug ("added a new peer-discovered pair with foundation of '%s'.", pair->foundation);
return pair;
}
}
/* XXX: add support for discovering peer-reflexive candidates */
if (i == NULL)
g_debug ("No matching remote candidate for incoming STUN conncheck.");
return NULL;
}
}
else if (res == EINVAL) {
/* case 2: not a valid, new request -- continue processing */
GSList *i;
int res;
/**
* Tries to match STUN reply in 'buf' to an existing STUN connectivity
* check transaction. If found, the reply is processed. Implements
* section 7.1.2 (Processing the Response) of ICE spec (ID-16).
*
* @return TRUE if a matching transaction is found
*/
static gboolean priv_map_reply_to_conn_check_request (NiceAgent *agent, Stream *stream, Component *component, NiceUDPSocket *sockptr, gchar *buf, guint len)
{
struct sockaddr sockaddr;
socklen_t socklen = sizeof (sockaddr);
GSList *i;
ssize_t res;
gboolean trans_found = FALSE;
g_debug ("Not a STUN connectivity check request -- might be a reply...");
/* note: ICE ID-15, 7.1.2 */
/* step: let's try to match the response to an existing check context */
for (i = agent->conncheck_list; i && trans_found != TRUE; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->stun_ctx) {
res = stun_bind_process (p->stun_ctx, buf, len, &sockaddr, &socklen);
g_debug ("stun_bind_process/conncheck for %p res %d.", p, res);
g_debug ("stun_bind_process/conncheck for %p res %d (controlling=%d).", p, res, agent->controlling_mode);
if (res == 0) {
/* case: succesful connectivity check */
g_debug ("conncheck %p SUCCEED.", p);
p->state = NICE_CHECK_SUCCEEDED;
/* case: found a matching connectivity check request */
CandidateCheckPair *ok_pair = p;
gboolean local_cand_matches = FALSE;
g_debug ("conncheck %p MATCHED.", p);
p->stun_ctx = NULL;
/* note: CONNECTED but not yet READY, see docs */
/* step: handle the possible case of a peer-reflexive
* candidate where the mapped-address in response does
* not match any local candidate, see 7.1.2.2.1 in ICE ID-16) */
{
NiceAddress mapped;
GSList *j;
nice_address_set_from_sockaddr (&mapped, &sockaddr);
for (j = component->local_candidates; j; j = j->next) {
NiceCandidate *cand = j->data;
if (nice_address_equal (&mapped, &cand->addr)) {
local_cand_matches = TRUE;
break;
}
}
if (local_cand_matches == TRUE) {
/* note: this is same as "adding to VALID LIST" in the spec
text */
p->state = NICE_CHECK_SUCCEEDED;
g_debug ("conncheck %p SUCCEEDED.", p);
}
else {
NiceCandidate *cand =
discovery_add_peer_reflexive_candidate (
agent,
stream->id,
component->id,
&mapped,
sockptr);
CandidateCheckPair *new_pair;
p->state = NICE_CHECK_FAILED;
/* step: add a new discovered pair (see ICE 7.1.2.2.2 ID-16) */
new_pair = priv_add_peer_reflexive_pair (agent, stream->id, component->id, cand, p);
ok_pair = new_pair;
g_debug ("conncheck %p FAILED, %p DISCOVERED.", p, new_pair);
}
}
/* step: notify the client of a new component state (must be done
* before the possible check list state update step */
agent_signal_component_state_change (agent,
stream->id,
component->id,
NICE_COMPONENT_STATE_CONNECTED);
/* step: update pair states (ICE 7.1.2.2.3, ID-16) */
priv_update_check_list_state_for_component (agent, stream, component);
if (p->nominated == TRUE) {
priv_update_selected_pair (agent, component, p);
}
/* step: updating nominated flag (ICE 7.1.2.2.4, ID-15 */
if (ok_pair->nominated == TRUE)
priv_update_selected_pair (agent, component, ok_pair);
trans_found = TRUE;
}
else if (res != EAGAIN) {
......@@ -740,13 +1039,37 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Compo
p->stun_ctx = NULL;
trans_found = TRUE;
}
else
/* case: invalid/incomplete message */
else {
g_assert (res == EAGAIN);
/* XXX: stun won't restart automatically, so cancel and
* restart, should do this only for 487 errors (role conflict) */
g_debug ("cancelling and restarting check for pair %p", p);
stun_bind_cancel (p->stun_ctx),
p->stun_ctx = NULL;
p->state = NICE_CHECK_WAITING;
}
}
}
/* step: let's try to match the response to an existing discovery */
return trans_found;
}
/**
* 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_discovery_request (NiceAgent *agent, gchar *buf, guint len)
{
struct sockaddr sockaddr;
socklen_t socklen = sizeof (sockaddr);
GSList *i;
ssize_t res;
gboolean trans_found = FALSE;
for (i = agent->discovery_list; i && trans_found != TRUE; i = i->next) {
CandidateDiscovery *d = i->data;
res = stun_bind_process (d->stun_ctx, buf, len, &sockaddr, &socklen);
......@@ -756,7 +1079,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Compo
NiceAddress niceaddr;
struct sockaddr_in *mapped = (struct sockaddr_in *)&sockaddr;
niceaddr.type = NICE_ADDRESS_TYPE_IPV4;
niceaddr.addr_ipv4 = ntohl(mapped->sin_addr.s_addr);
niceaddr.addr.addr_ipv4 = ntohl(mapped->sin_addr.s_addr);
niceaddr.port = ntohs(mapped->sin_port);
discovery_add_server_reflexive_candidate (
d->agent,
......@@ -764,6 +1087,7 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Compo
d->component->id,
&niceaddr,
d->nicesock);
d->stun_ctx = NULL;
d->done = TRUE;
trans_found = TRUE;
......@@ -771,249 +1095,168 @@ gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Compo
else if (res != EAGAIN) {
/* case: STUN error, the check STUN context was freed */
d->stun_ctx = NULL;
d->done = TRUE;
trans_found = TRUE;
}
}
}
else {
g_debug ("Invalid STUN connectivity check request. Ignoring... %s", strerror(errno));
g_assert (res == EAGAIN);
}
}
return TRUE;
return trans_found;
}
/* -----------------------------------------------------------------
* Code using the old STUN API
* ----------------------------------------------------------------- */
static void
_handle_stun_binding_request (
NiceAgent *agent,
Stream *stream,
Component *component,
NiceCandidate *local,
NiceAddress from,
StunMessage *msg)
static gboolean priv_verify_inbound_username (NiceAgent *agent, Stream *stream, const char *uname)
{
GSList *i;
StunAttribute *attr;
gchar *username = NULL;
NiceCandidate *remote = NULL;
const char *colon;
/* msg should have either:
*
* Jingle P2P:
* username = local candidate username + remote candidate username
* ICE:
* username = local candidate username + ":" + remote candidate username
* password = local candidate pwd
* priority = priority to use if a new candidate is generated
*
* Note that:
*
* - "local"/"remote" are from the perspective of the receiving side
* - the remote candidate username is not necessarily unique; Jingle seems
* to always generate a unique username/password for each candidate, but
* ICE makes no guarantees
*
* There are three cases we need to deal with:
*
* - valid username with a known address
* --> send response
* - valid username with an unknown address
* --> send response
* --> later: create new remote candidate
* - invalid username
* --> send error
*/
if (uname == NULL)
return FALSE;
attr = stun_message_find_attribute (msg, STUN_ATTRIBUTE_USERNAME);
colon = strchr (uname, ':');
if (colon == NULL)
return FALSE;
if (attr == NULL)
/* no username attribute found */
goto ERROR;
if (strncmp (uname, stream->local_ufrag, colon - uname) == 0)
return TRUE;
username = attr->username;
return FALSE;
}
/* validate username */
/* XXX-old_stun_code: Should first try and find a remote candidate with a matching
* transport address, and fall back to matching on username only after that.
* That way, we know to always generate a new remote candidate if the
* transport address didn't match.
/**
* Recalculates priorities of all candidate pairs. This
* is required after a conflict in ICE roles.
*/
static void priv_recalculate_pair_priorities (NiceAgent *agent)
{
GSList *i;
for (i = component->remote_candidates; i; i = i->next)
{
guint len;
remote = i->data;
#if 0
g_debug ("uname check: %s :: %s -- %s", username, local->username,
remote->username);
#endif
if (!g_str_has_prefix (username, local->username))
continue;
len = strlen (local->username);
if (0 != strcmp (username + len, remote->username))
continue;
#if 0
/* usernames match; check address */
if (rtmp->addr.addr_ipv4 == ntohl (from.sin_addr.s_addr) &&
rtmp->port == ntohs (from.sin_port))
{
/* this is a candidate we know about, just send a reply */
/* is candidate pair active now? */
remote = rtmp;
}
#endif
/* send response */
goto RESPOND;
}
/* username is not valid */
goto ERROR;
RESPOND:
#ifdef DEBUG
{
gchar ip[NICE_ADDRESS_STRING_LEN];
nice_address_to_string (&remote->addr, ip);
g_debug ("s%d:%d: got valid connectivity check for candidate %d (%s:%d)",
stream->id, component->id, remote->id, ip, remote->addr.port);
for (i = agent->conncheck_list; i; i = i->next) {
CandidateCheckPair *p = i->data;
if (agent->controlling_mode == TRUE)
p->priority = nice_candidate_pair_priority (p->local->priority, p->remote->priority);
else
p->priority = nice_candidate_pair_priority (p->remote->priority, p->local->priority);
}
#endif
}
/* update candidate/peer affinity */
/* Note that @from might be different to @remote->addr; for ICE, this
* (always?) creates a new peer-reflexive remote candidate (§7.2).
/**
* Processing an incoming STUN message.
*
* @param agent self pointer
* @param stream stream the packet is related to
* @param component component the packet is related to
* @param udp_socket UDP socket from which the packet was received
* @param from address of the sender
* @param buf message contents
* @param buf message length
*
* @pre contents of 'buf' is a STUN message
*
* @return XXX (what FALSE means exactly?)
*/
/* XXX-old_stun_code: test case where @from != @remote->addr. */
component->active_candidate = local;
component->peer_addr = from;
gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Component *component, NiceUDPSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len)
{
struct sockaddr sockaddr;
uint8_t rbuf[MAX_STUN_DATAGRAM_PAYLOAD];
ssize_t res;
size_t rbuf_len = sizeof (rbuf);
bool control = agent->controlling_mode;
/* send STUN response */
nice_address_copy_to_sockaddr (from, &sockaddr);
{
StunMessage *response;
guint len;
gchar *packed;
/* note: contents of 'buf' already validated, so it is
* a valid and full received STUN message */
response = stun_message_new (STUN_MESSAGE_BINDING_RESPONSE,
msg->transaction_id, 2);
response->attributes[0] = stun_attribute_mapped_address_new (
from.addr_ipv4, from.port);
response->attributes[1] = stun_attribute_username_new (username);
len = stun_message_pack (response, &packed);
nice_udp_socket_send (local->sockptr, &from, len, packed);
/* note: ICE ID-16, 7.2 */
g_free (packed);
stun_message_free (response);
res = stun_conncheck_reply (rbuf, &rbuf_len, (const uint8_t*)buf, &sockaddr, sizeof (sockaddr),
stream->local_password, &control, agent->tie_breaker);
if (res == EACCES) {
/* role conflict, change mode and regenarate reply */
if (control != agent->controlling_mode) {
g_debug ("Conflict in controller selection, switching to mode %d.", control);
agent->controlling_mode = control;
/* the pair priorities depend on the roles, so recalculation
* is needed */
priv_recalculate_pair_priorities (agent);
}
}
/* send reciprocal ("triggered") connectivity check */
/* XXX-old_stun_code: possibly we shouldn't do this if we're being an ICE Lite agent */
{
StunMessage *extra;
gchar *username;
guint len;
gchar *packed;
extra = stun_message_new (STUN_MESSAGE_BINDING_REQUEST,
NULL, 1);
if (res == 0 || res == EACCES) {
/* case 1: valid incoming request, send a reply */
username = g_strconcat (remote->username, local->username, NULL);
extra->attributes[0] = stun_attribute_username_new (username);
g_free (username);
GSList *i;
bool use_candidate =
stun_conncheck_use_candidate ((const uint8_t*)buf);
nice_rng_generate_bytes (agent->rng, 16, extra->transaction_id);
/* XXX: uses stun-msg.h, remove later */
bool error_response =
(stun_get_class ((const uint8_t*)rbuf) == STUN_ERROR);
g_debug ("error in request %d.", error_response);
len = stun_message_pack (extra, &packed);
nice_udp_socket_send (local->sockptr, &from, len, packed);
g_free (packed);
if (agent->controlling_mode)
use_candidate = TRUE;
stun_message_free (extra);
/* XXX: verify the USERNAME with stun_connecheck_username() */
if (stun_conncheck_username ((const uint8_t*)buf, agent->ufragtmp, NICE_STREAM_MAX_UNAME) == NULL) {
g_debug ("No USERNAME attribute in incoming STUN request, ignoring.");
return FALSE;
}
/* emit component-state-changed(connected) */
/* XXX-old_stun_code: probably better do this when we get the binding response */
agent_signal_component_state_change (agent,
stream->id,
component->id,
NICE_COMPONENT_STATE_CONNECTED);
return;
if (priv_verify_inbound_username (agent, stream, agent->ufragtmp) != TRUE) {
g_debug ("USERNAME does not match local streams, ignoring.");
return FALSE;
}
ERROR:
if (stream->initial_binding_request_received != TRUE)
agent_signal_initial_binding_request_received (agent, stream);
#ifdef DEBUG
{
gchar ip[NICE_ADDRESS_STRING_LEN];
for (i = component->remote_candidates; i; i = i->next) {
NiceCandidate *cand = i->data;
if (nice_address_equal (from, &cand->addr)) {
priv_reply_to_conn_check (agent, stream, component, cand, udp_socket, rbuf_len, rbuf, use_candidate);
break;
}
}
nice_address_to_string (&remote->addr, ip);
g_debug (
"s%d:%d: got invalid connectivity check for candidate %d (%s:%d)",
stream->id, component->id, remote->id, ip, remote->addr.port);
if (i == NULL) {
NiceCandidate *candidate;
g_debug ("No matching remote candidate for incoming check -> peer-reflexive candidate.");
candidate = discovery_learn_remote_peer_reflexive_candidate (
agent, stream, component, stun_conncheck_priority ((const uint8_t*)buf), from, udp_socket);
if (candidate)
priv_reply_to_conn_check (agent, stream, component, candidate, udp_socket, rbuf_len, rbuf, use_candidate);
}
#endif
}
else if (res == EINVAL) {
/* case 2: not a new request, might be a reply... */
/* XXX-old_stun_code: add ERROR-CODE parameter */
gboolean trans_found = FALSE;
{
StunMessage *response;
guint len;
gchar *packed;
/* XXX: uses stun-msg.h, remove later */
bool error_response =
(stun_get_class ((const uint8_t*)buf) == STUN_ERROR);
g_debug ("error in response %d", error_response);
response = stun_message_new (STUN_MESSAGE_BINDING_ERROR_RESPONSE,
msg->transaction_id, 0);
len = stun_message_pack (response, &packed);
nice_udp_socket_send (local->sockptr, &from, len, packed);
g_debug ("Not a STUN connectivity check request, might be a reply or keepalive...");
g_free (packed);
stun_message_free (response);
}
/* note: ICE ID-15, 7.1.2 */
/* XXX-old_stun_code: we could be clever and keep around STUN packets that we couldn't
* validate, then re-examine them when we get new remote candidates -- would
* this fix some timing problems (i.e. TCP being slower than UDP)
*/
/* XXX-old_stun_code: if the peer is the controlling agent, it may include a USE-CANDIDATE
* attribute in the binding request
*/
}
/* step: let's try to match the response to an existing check context */
if (trans_found != TRUE)
trans_found =
priv_map_reply_to_conn_check_request (agent, stream, component, udp_socket, buf, len);
void conn_check_handle_inbound_stun_old (
NiceAgent *agent,
Stream *stream,
Component *component,
NiceCandidate *local,
NiceAddress from,
StunMessage *msg)
{
switch (msg->type)
{
case STUN_MESSAGE_BINDING_REQUEST:
_handle_stun_binding_request (agent, stream, component, local, from,
msg);
break;
case STUN_MESSAGE_BINDING_RESPONSE:
/* XXX-old_stun_code: check it matches a request we sent */
break;
default:
/* a message type we don't know how to handle */
/* XXX-old_stun_code: send error response */
break;
/* step: let's try to match the response to an existing discovery */
if (trans_found != TRUE)
trans_found =
priv_map_reply_to_discovery_request (agent, buf, len);
}
else {
g_debug ("Invalid STUN connectivity check request. Ignoring... %s", strerror(errno));
return FALSE;
}
}
return TRUE;
}
......@@ -44,7 +44,9 @@
#include "agent.h"
#include "stream.h"
#include "stun.h" /* XXX: using the old STUN API, to be removed */
#include "stun/conncheck.h" /* note: the new STUN API */
#include "stun-ice.h" /* note: the new STUN API */
#define NICE_CANDIDATE_PAIR_MAX_FOUNDATION NICE_CANDIDATE_MAX_FOUNDATION*2
typedef enum
{
......@@ -53,7 +55,8 @@ typedef enum
NICE_CHECK_SUCCEEDED, /**< conn. succesfully checked */
NICE_CHECK_FAILED, /**< no connectivity, retransmissions ceased */
NICE_CHECK_FROZEN, /**< waiting to be scheduled to WAITING */
NICE_CHECK_CANCELLED /**< check cancelled */
NICE_CHECK_CANCELLED, /**< check cancelled */
NICE_CHECK_DISCOVERED /**< a valid candidate pair not on check list */
} NiceCheckState;
typedef enum
......@@ -73,11 +76,12 @@ struct _CandidateCheckPair
guint component_id;
NiceCandidate *local;
NiceCandidate *remote;
gchar *foundation;
gchar foundation[NICE_CANDIDATE_PAIR_MAX_FOUNDATION];
NiceCheckState state;
gboolean nominated;
guint64 priority;
GTimeVal next_tick; /* next tick timestamp */
gboolean traffic_after_tick;
stun_bind_t *stun_ctx;
};
......@@ -87,17 +91,7 @@ void conn_check_free (NiceAgent *agent);
void conn_check_schedule_next (NiceAgent *agent);
int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair);
gboolean conn_check_prune_stream (NiceAgent *agent, guint stream_id);
gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Component *component, const NiceAddress *from, gchar *buf, guint len);
/* functions using the old STUN API:
* ---------------------------------*/
void conn_check_handle_inbound_stun_old (
NiceAgent *agent,
Stream *stream,
Component *component,
NiceCandidate *local,
NiceAddress from,
StunMessage *msg);
gboolean conn_check_handle_inbound_stun (NiceAgent *agent, Stream *stream, Component *component, NiceUDPSocket *udp_socket, const NiceAddress *from, gchar *buf, guint len);
gint conn_check_compare (const CandidateCheckPair *a, const CandidateCheckPair *b);
#endif /*_NICE_CONNCHECK_H */
......@@ -33,13 +33,18 @@
* file under either the MPL or the LGPL.
*/
#include <string.h>
#include <errno.h>
/**
* @file discovery.c
* @brief ICE candidate discovery functions
*/
#ifndef _BSD_SOURCE
#error "timercmp() macros needed"
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/time.h> /* timercmp() macro, BSD */
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
......@@ -52,6 +57,13 @@
#include "component.h"
#include "discovery.h"
static inline int priv_timer_expired (GTimeVal *restrict timer, GTimeVal *restrict now)
{
return (now->tv_sec == timer->tv_sec) ?
now->tv_usec >= timer->tv_usec :
now->tv_sec >= timer->tv_sec;
}
/**
* Frees the CandidateDiscovery structure pointed to
* by 'user data'. Compatible with g_slist_foreach().
......@@ -74,12 +86,11 @@ void discovery_free (NiceAgent *agent)
g_slist_free (agent->discovery_list),
agent->discovery_list = NULL;
agent->discovery_unsched_items = 0;
}
if (agent->discovery_timer_id)
g_source_remove (agent->discovery_timer_id),
agent->discovery_timer_id = 0;
agent->discovery_unsched_items = 0;
}
}
/**
......@@ -112,9 +123,45 @@ gboolean discovery_prune_stream (NiceAgent *agent, guint stream_id)
i = i->next;
}
if (agent->discovery_list == NULL) {
/* return FALSE if there was a memory allocation failure */
if (agent->conncheck_list == NULL && i != NULL)
if (i != NULL)
return FALSE;
/* noone using the timer anymore, clean it up */
discovery_free (agent);
}
return TRUE;
}
/**
* Adds a new local candidate. Implements the candidate pruning
* defined in ICE spec section 4.1.1.3 (ID-16).
*/
static gboolean priv_add_local_candidate_pruned (Component *component, NiceCandidate *candidate)
{
GSList *modified_list, *i;
for (i = component->local_candidates; i ; i = i->next) {
NiceCandidate *c = i->data;
if (nice_address_equal (&c->base_addr, &candidate->base_addr) &&
nice_address_equal (&c->addr, &candidate->addr)) {
g_debug ("Candidate %p (component-id %u) redundant, ignoring.", candidate, component->id);
return FALSE;
}
}
modified_list= g_slist_append (component->local_candidates,
candidate);
if (modified_list) {
component->local_candidates = modified_list;
/* note: candidate username and password are left NULL as stream
level ufrag/password are used */
g_assert (candidate->username == NULL);
g_assert (candidate->password == NULL);
}
return TRUE;
}
......@@ -135,6 +182,7 @@ NiceCandidate *discovery_add_local_host_candidate (
Component *component;
Stream *stream;
NiceUDPSocket *udp_socket = NULL;
gboolean errors = FALSE;
if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
return NULL;
......@@ -143,49 +191,51 @@ NiceCandidate *discovery_add_local_host_candidate (
if (candidate) {
NiceUDPSocket *udp_socket = g_slice_new0 (NiceUDPSocket);
if (udp_socket) {
candidate->foundation = g_strdup_printf ("%u", agent->next_candidate_id++);
/* XXX: implement the foundation assignment as defined in
* ICE sect 4.1.1.4 ID-15: */
g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", agent->next_candidate_id++);
candidate->stream_id = stream_id;
candidate->component_id = component_id;
candidate->addr = *address;
candidate->base_addr = *address;
candidate->priority = nice_candidate_ice_priority (candidate);
/* note: username and password set to NULL as stream
ufrag/password are used */
/* note: candidate username and password are left NULL as stream
level ufrag/password are used */
if (nice_udp_socket_factory_make (agent->socket_factory,
udp_socket, address)) {
component->local_candidates = g_slist_append (component->local_candidates,
candidate);
if (component->local_candidates) {
component->sockets = g_slist_append (component->sockets, udp_socket);
if (component->sockets) {
gboolean result = priv_add_local_candidate_pruned (component, candidate);
if (result == TRUE) {
GSList *modified_list = g_slist_append (component->sockets, udp_socket);
if (modified_list) {
/* success: store a pointer to the sockaddr */
component->sockets = modified_list;
candidate->sockptr = udp_socket;
candidate->addr = udp_socket->addr;
candidate->base_addr = udp_socket->addr;
agent_signal_new_candidate (agent, candidate);
}
else { /* error: list memory allocation */
candidate = NULL;
/* note: candidate already owner by component */
candidate = NULL; /* note: candidate already owned by component */
}
}
else { /* error: memory alloc / list */
nice_candidate_free (candidate), candidate = NULL;
}
}
else { /* error: socket factory make */
nice_candidate_free (candidate), candidate = NULL;
else /* error: memory allocation, or duplicate candidatet */
errors = TRUE;
}
else /* error: socket factory make */
errors = TRUE;
}
else /* error: udp socket memory allocation */
nice_candidate_free (candidate), candidate = NULL;
errors = TRUE;
}
if (!candidate) {
/* clean up after errors */
if (errors) {
if (candidate)
nice_candidate_free (candidate), candidate = NULL;
if (udp_socket)
g_slice_free (NiceUDPSocket, udp_socket);
}
......@@ -199,7 +249,6 @@ NiceCandidate *discovery_add_local_host_candidate (
*
* @return pointer to the created candidate, or NULL on error
*/
NiceCandidate*
discovery_add_server_reflexive_candidate (
NiceAgent *agent,
......@@ -211,41 +260,149 @@ discovery_add_server_reflexive_candidate (
NiceCandidate *candidate;
Component *component;
Stream *stream;
gboolean result = FALSE;
if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
return NULL;
candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE);
if (candidate) {
candidate->foundation = g_strdup_printf ("%u", agent->next_candidate_id++);
candidate->priority =
nice_candidate_ice_priority_full
(NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE, 0, component_id);
g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", agent->next_candidate_id++);
candidate->stream_id = stream_id;
candidate->component_id = component_id;
candidate->addr = *address;
candidate->base_addr = *address;
/* step: link to the base candidate+socket */
candidate->sockptr = base_socket;
candidate->base_addr = base_socket->addr;
result = priv_add_local_candidate_pruned (component, candidate);
if (result != TRUE) {
/* error: memory allocation, or duplicate candidatet */
nice_candidate_free (candidate), candidate = NULL;
}
}
return candidate;
}
/**
* Creates a peer reflexive candidate for 'component_id' of stream
* 'stream_id'.
*
* @return pointer to the created candidate, or NULL on error
*/
NiceCandidate*
discovery_add_peer_reflexive_candidate (
NiceAgent *agent,
guint stream_id,
guint component_id,
NiceAddress *address,
NiceUDPSocket *base_socket)
{
NiceCandidate *candidate;
Component *component;
Stream *stream;
if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
return NULL;
candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
if (candidate) {
gboolean result;
candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
candidate->priority =
0x1000000 * 125 + 0x100 * 0 + 256 - component_id; /* sect:4.1.2.1(-14) */
nice_candidate_ice_priority_full
(NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE, 0, component_id);
candidate->stream_id = stream_id;
candidate->component_id = component_id;
g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", agent->next_candidate_id++);
candidate->addr = *address;
candidate->base_addr = base_socket->addr;
component->local_candidates = g_slist_append (component->local_candidates,
candidate);
if (component->local_candidates) {
/* note: username and password left to NULL as stream-evel
* credentials are used by default */
/* step: link to the base candidate+socket */
candidate->sockptr = base_socket;
candidate->base_addr = base_socket->addr;
g_assert (candidate->username == NULL);
g_assert (candidate->password == NULL);
}
else /* error: memory allocation - list */
result = priv_add_local_candidate_pruned (component, candidate);
if (result != TRUE) {
/* error: memory allocation, or duplicate candidatet */
nice_candidate_free (candidate), candidate = NULL;
}
}
return candidate;
}
static guint priv_highest_remote_foundation (Component *component)
{
GSList *i;
guint highest = 0;
for (i = component->remote_candidates; i; i = i->next) {
NiceCandidate *cand = i->data;
guint foundation_id = (guint)atoi (cand->foundation);
if (foundation_id > highest)
highest = foundation_id;
}
return highest;
}
/**
* Adds a new peer reflexive candidate to the list of known
* remote candidates. The candidate is however not paired with
* existing local candidates.
*
* See ICE ID-16 sect 7.2.1.3.
*
* @return pointer to the created candidate, or NULL on error
*/
NiceCandidate *discovery_learn_remote_peer_reflexive_candidate (
NiceAgent *agent,
Stream *stream,
Component *component,
guint32 priority,
const NiceAddress *remote_address,
NiceUDPSocket *udp_socket)
{
NiceCandidate *candidate;
candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
if (candidate) {
GSList *modified_list;
guint next_remote_id = priv_highest_remote_foundation (component);
candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
candidate->addr = *remote_address;
candidate->base_addr = *remote_address;
candidate->priority = priority;;
candidate->stream_id = stream->id;
candidate->component_id = component->id;
g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", next_remote_id);
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) {
component->remote_candidates = modified_list;
agent_signal_new_remote_candidate (agent, candidate);
}
else { /* error: memory alloc / list */
nice_candidate_free (candidate), candidate = NULL;
}
}
return candidate;
}
/**
* Timer callback that handles scheduling new candidate discovery
* processes (paced by the Ta timer), and handles running of the
......@@ -262,10 +419,10 @@ static gboolean priv_discovery_tick (gpointer pointer)
GSList *i;
int not_done = 0; /* note: track whether to continue timer */
#ifdef DEBUG
#ifndef NDEBUG
{
static int tick_counter = 0;
if (++tick_counter % 20 == 0)
if (++tick_counter % 1 == 0)
g_debug ("discovery tick #%d with list %p (1)", tick_counter, agent->discovery_list);
}
#endif
......@@ -289,8 +446,17 @@ static gboolean priv_discovery_tick (gpointer pointer)
memset (&stun_server, 0, sizeof(stun_server));
if (strchr (cand->server_addr, ':') == NULL)
stun_server.sin_family = AF_INET;
else
stun_server.sin_family = AF_INET6;
stun_server.sin_addr.s_addr = inet_addr(cand->server_addr);
stun_server.sin_port = htons(IPPORT_STUN);
stun_server.sin_port = htons(cand->server_port);
agent_signal_component_state_change (agent,
cand->stream->id,
cand->component->id,
NICE_COMPONENT_STATE_GATHERING);
res = stun_bind_start (&cand->stun_ctx, cand->socket,
(struct sockaddr*)&stun_server, sizeof(stun_server));
......@@ -299,11 +465,6 @@ static gboolean priv_discovery_tick (gpointer pointer)
/* case: success, start waiting for the result */
g_get_current_time (&cand->next_tick);
agent_signal_component_state_change (agent,
cand->stream->id,
cand->component->id,
NICE_COMPONENT_STATE_GATHERING);
}
else {
/* case: error in starting discovery, start the next discovery */
......@@ -329,7 +490,7 @@ static gboolean priv_discovery_tick (gpointer pointer)
cand->done = TRUE;
}
/* note: macro from sys/time.h but compatible with GTimeVal */
else if (timercmp(&cand->next_tick, &now, <=)) {
else if (priv_timer_expired (&cand->next_tick, &now)) {
int res = stun_bind_elapse (cand->stun_ctx);
if (res == EAGAIN) {
/* case: not ready complete, so schedule next timeout */
......@@ -339,11 +500,6 @@ static gboolean priv_discovery_tick (gpointer pointer)
g_get_current_time (&cand->next_tick);
g_time_val_add (&cand->next_tick, timeout * 10);
/* note: macro from sys/time.h but compatible with GTimeVal */
if (timercmp(&cand->next_tick, &agent->next_check_tv, <)) {
agent->next_check_tv = cand->next_tick;
}
++not_done; /* note: retry later */
}
else {
......@@ -373,7 +529,8 @@ static gboolean priv_discovery_tick (gpointer pointer)
}
/**
* Initiates the active candidate discovery process.
* Initiates the candidate discovery process by starting
* the necessary timers.
*
* @pre agent->discovery_list != NULL // unsched discovery items available
*/
......@@ -381,23 +538,15 @@ void discovery_schedule (NiceAgent *agent)
{
g_assert (agent->discovery_list != NULL);
g_debug ("Scheduling discovery...");
if (agent->discovery_unsched_items > 0) {
/* XXX: make timeout Ta configurable */
guint next = NICE_AGENT_TIMER_TA_DEFAULT;
/* XXX: send a component state-change, but, but, how do we
* actually do this? back to the drawing board... */
/* step 1: run first iteration immediately */
priv_discovery_tick (agent);
g_debug ("Scheduling a discovery timeout of %u msec.", next);
/* step 2: scheduling timer */
if (agent->discovery_timer_id == 0) {
/* step: run first iteration immediately */
gboolean res = priv_discovery_tick (agent);
if (res == TRUE) {
agent->discovery_timer_id =
g_timeout_add (next, priv_discovery_tick, agent);
g_timeout_add (agent->timer_ta, priv_discovery_tick, agent);
}
}
}
}
......@@ -50,6 +50,7 @@ struct _CandidateDiscovery
guint socket; /**< XXX: should be taken from local cand: existing socket to use */
NiceUDPSocket *nicesock; /**< XXX: should be taken from local cand: existing socket to use */
const gchar *server_addr; /**< STUN/TURN server address, not owned */
guint server_port; /**< STUN/TURN server port */
NiceAddress *interface; /**< Address of local interface */
stun_bind_t *stun_ctx;
GTimeVal next_tick; /**< next tick timestamp */
......@@ -64,7 +65,8 @@ void discovery_free (NiceAgent *agent);
gboolean discovery_prune_stream (NiceAgent *agent, guint stream_id);
void discovery_schedule (NiceAgent *agent);
NiceCandidate *discovery_add_local_host_candidate (
NiceCandidate *
discovery_add_local_host_candidate (
NiceAgent *agent,
guint stream_id,
guint component_id,
......@@ -78,4 +80,21 @@ discovery_add_server_reflexive_candidate (
NiceAddress *address,
NiceUDPSocket *base_socket);
NiceCandidate*
discovery_add_peer_reflexive_candidate (
NiceAgent *agent,
guint stream_id,
guint component_id,
NiceAddress *address,
NiceUDPSocket *base_socket);
NiceCandidate *
discovery_learn_remote_peer_reflexive_candidate (
NiceAgent *agent,
Stream *stream,
Component *component,
guint32 priority,
const NiceAddress *remote_address,
NiceUDPSocket *udp_socket);
#endif /*_NICE_CONNCHECK_H */
......@@ -34,11 +34,19 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
#include "stream.h"
/**
* @file stream.c
* @brief ICE stream functionality
*/
Stream *
stream_new (void)
{
......
......@@ -44,10 +44,13 @@
G_BEGIN_DECLS
/* Following do not including the terminating NULL */
/* Following include the terminating NULL */
#define NICE_STREAM_MAX_UFRAG_LEN 4
#define NICE_STREAM_MAX_PWD_LEN 22
#define NICE_STREAM_MAX_UFRAG 1024 + 1
#define NICE_STREAM_MAX_UNAME 1024 + 1024 + 1 + 1 /* colon plus NULL */
#define NICE_STREAM_MAX_PWD 1024 + 1
#define NICE_STREAM_DEF_UFRAG 4 + 1
#define NICE_STREAM_DEF_PWD 22 + 1
typedef struct _Stream Stream;
......@@ -58,10 +61,10 @@ struct _Stream
gboolean initial_binding_request_received;
/* XXX: streams can have multiple components */
Component *component;
gchar local_ufrag[NICE_STREAM_MAX_UFRAG_LEN + 1];
gchar local_password[NICE_STREAM_MAX_PWD_LEN + 1];
gchar remote_ufrag[NICE_STREAM_MAX_UFRAG_LEN + 1];
gchar remote_password[NICE_STREAM_MAX_PWD_LEN + 1];
gchar local_ufrag[NICE_STREAM_MAX_UFRAG];
gchar local_password[NICE_STREAM_MAX_PWD];
gchar remote_ufrag[NICE_STREAM_MAX_UFRAG];
gchar remote_password[NICE_STREAM_MAX_PWD];
};
Stream *
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "agent.h"
#include "agent-priv.h"
......
......@@ -32,6 +32,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
......@@ -44,36 +47,26 @@ static const guint test_component_id = 1;
static NiceComponentState global_lagent_state = NICE_COMPONENT_STATE_LAST;
static NiceComponentState global_ragent_state = NICE_COMPONENT_STATE_LAST;
static GMainLoop *global_mainloop = NULL;
static gboolean global_candidate_gathering_done = FALSE;
static gboolean global_lagent_gathering_done = FALSE;
static gboolean global_ragent_gathering_done = FALSE;
static gboolean global_lagent_ibr_received = FALSE;
static gboolean global_ragent_ibr_received = FALSE;
static int global_lagent_cands = 0;
static int global_ragent_cands = 0;
static gint global_ragent_read = 0;
static gboolean timer_cb (gpointer pointer)
static void priv_print_global_status (void)
{
g_debug ("%s: %p", G_STRFUNC, pointer);
/* signal status via a global variable */
g_debug ("\tgathering_done=%d", global_candidate_gathering_done);
g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done);
g_debug ("\tlstate=%d", global_lagent_state);
g_debug ("\trstate=%d", global_ragent_state);
}
static gboolean timer_cb (gpointer pointer)
{
g_debug ("%s: %p", G_STRFUNC, pointer);
if (global_candidate_gathering_done != TRUE &&
global_lagent_state == NICE_COMPONENT_STATE_GATHERING &&
global_ragent_state == NICE_COMPONENT_STATE_GATHERING) {
global_candidate_gathering_done = TRUE;
g_main_loop_quit (global_mainloop);
return TRUE;
}
/* signal status via a global variable */
else if (global_candidate_gathering_done == TRUE &&
global_lagent_state == NICE_COMPONENT_STATE_READY &&
global_ragent_state == NICE_COMPONENT_STATE_READY) {
g_main_loop_quit (global_mainloop);
return TRUE;
}
/* note: should not be reached, abort */
g_debug ("ERROR: test has got stuck, aborting...");
......@@ -86,8 +79,12 @@ static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id,
g_debug ("%s: %p", G_STRFUNC, user_data);
/* XXX: dear compiler, these are for you: */
(void)agent; (void)stream_id; (void)component_id; (void)len; (void)buf;
(void)user_data;
(void)agent; (void)stream_id; (void)component_id; (void)buf;
if ((int)user_data == 2) {
global_ragent_read = len;
g_main_loop_quit (global_mainloop);
}
}
static void cb_candidate_gathering_done(NiceAgent *agent, gpointer data)
......@@ -95,9 +92,13 @@ static void cb_candidate_gathering_done(NiceAgent *agent, gpointer data)
g_debug ("%s: %p", G_STRFUNC, data);
if ((int)data == 1)
global_lagent_state = NICE_COMPONENT_STATE_GATHERING;
global_lagent_gathering_done = TRUE;
else if ((int)data == 2)
global_ragent_state = NICE_COMPONENT_STATE_GATHERING;
global_ragent_gathering_done = TRUE;
if (global_lagent_gathering_done &&
global_ragent_gathering_done)
g_main_loop_quit (global_mainloop);
/* XXX: dear compiler, these are for you: */
(void)agent;
......@@ -114,6 +115,20 @@ static void cb_component_state_changed(NiceAgent *agent, guint stream_id, guint
else if ((int)data == 2)
global_ragent_state = state;
/* signal status via a global variable */
if (global_lagent_state == NICE_COMPONENT_STATE_READY &&
global_ragent_state == NICE_COMPONENT_STATE_READY) {
g_main_loop_quit (global_mainloop);
return;
}
/* signal status via a global variable */
if (global_lagent_state == NICE_COMPONENT_STATE_FAILED &&
global_ragent_state == NICE_COMPONENT_STATE_FAILED) {
g_main_loop_quit (global_mainloop);
return;
}
/* XXX: dear compiler, these are for you: */
(void)agent; (void)stream_id; (void)data;
}
......@@ -159,7 +174,7 @@ static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *bas
{
NiceAddress laddr, raddr;
NiceCandidateDesc cdes = { /* candidate description (no ports) */
"1", /* foundation */
(gchar *)"1", /* foundation */
test_component_id,
NICE_CANDIDATE_TRANSPORT_UDP, /* transport */
100000, /* priority */
......@@ -170,20 +185,33 @@ static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *bas
GSList *cands, *i;
guint ls_id, rs_id;
global_lagent_state = NICE_COMPONENT_STATE_LAST;
global_ragent_state = NICE_COMPONENT_STATE_LAST;
global_lagent_gathering_done = FALSE;
global_ragent_gathering_done = FALSE;
global_lagent_ibr_received =
global_ragent_ibr_received = FALSE;
global_lagent_cands =
global_ragent_cands = 0;
g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
/* 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);
g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
g_object_set (G_OBJECT (lagent), "controlling-mode", FALSE, NULL);
/* step: run mainloop until local candidates are ready
* (see timer_cb() above) */
if (global_lagent_gathering_done != TRUE ||
global_ragent_gathering_done != TRUE) {
g_debug ("test-fullmode: Added streams, running mainloop until 'candidate-gathering-done'...");
g_main_loop_run (global_mainloop);
g_assert (global_candidate_gathering_done == TRUE);
g_assert (global_lagent_gathering_done == TRUE);
g_assert (global_ragent_gathering_done == TRUE);
}
/* step: find out the local candidates of each agent */
cands = nice_agent_get_local_candidates(lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
......@@ -238,6 +266,214 @@ static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *bas
g_assert (global_lagent_cands == 1);
g_assert (global_ragent_cands == 1);
/* note: test payload send and receive */
global_ragent_read = 0;
g_assert (nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678") == 16);
g_main_loop_run (global_mainloop);
g_assert (global_ragent_read == 16);
g_debug ("test-fullmode: Ran mainloop, removing streams...");
/* step: clean up resources and exit */
nice_agent_remove_stream (lagent, ls_id);
nice_agent_remove_stream (ragent, rs_id);
return 0;
}
static int run_full_test_wrong_password (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr)
{
NiceAddress laddr, raddr;
NiceCandidateDesc cdes = { /* candidate description (no ports) */
(gchar *)"1", /* foundation */
test_component_id,
NICE_CANDIDATE_TRANSPORT_UDP, /* transport */
100000, /* priority */
NULL, /* address */
NICE_CANDIDATE_TYPE_HOST, /* type */
NULL /* base-address */
};
GSList *cands, *i;
guint ls_id, rs_id;
global_lagent_state =
global_ragent_state = NICE_COMPONENT_STATE_LAST;
global_lagent_gathering_done =
global_ragent_gathering_done = FALSE;
global_lagent_cands =
global_ragent_cands = 0;
g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
/* 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);
/* step: run mainloop until local candidates are ready
* (see timer_cb() above) */
if (global_lagent_gathering_done != TRUE ||
global_ragent_gathering_done != TRUE) {
g_debug ("test-fullmode: Added streams, running mainloop until 'candidate-gathering-done'...");
g_main_loop_run (global_mainloop);
g_assert (global_lagent_gathering_done == TRUE);
g_assert (global_ragent_gathering_done == TRUE);
}
/* step: find out the local candidates of each agent */
cands = nice_agent_get_local_candidates(lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
for (i = cands; i; i = i->next) {
NiceCandidate *cand = i->data;
if (cand) {
g_debug ("test-fullmode: local port L %u", cand->addr.port);
laddr = cand->addr;
}
}
g_slist_free (cands);
cands = nice_agent_get_local_candidates(ragent, rs_id, NICE_COMPONENT_TYPE_RTP);
for (i = cands; i; i = i->next) {
NiceCandidate *cand = i->data;
if (cand) {
g_debug ("test-fullmode: local port R %u", cand->addr.port);
raddr = cand->addr;
}
}
g_slist_free (cands);
g_debug ("test-fullmode: Got local candidates...");
/* step: pass the remote candidates to agents */
cands = g_slist_append (NULL, &cdes);
{
const gchar *ufrag = NULL, *password = NULL;
nice_agent_get_local_credentials(lagent, ls_id, &ufrag, &password);
nice_agent_set_remote_credentials (ragent,
rs_id, "wrong", password);
nice_agent_get_local_credentials(ragent, rs_id, &ufrag, &password);
nice_agent_set_remote_credentials (lagent,
ls_id, ufrag, "wrong2");
}
cdes.addr = &raddr;
nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, cands);
cdes.addr = &laddr;
nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, cands);
g_slist_free (cands);
g_debug ("test-fullmode: Set properties, next running mainloop until connectivity checks succeed...");
/* step: run the mainloop until connectivity checks succeed
* (see timer_cb() above) */
g_main_loop_run (global_mainloop);
/* note: verify that correct number of local candidates were reported */
g_assert (global_lagent_cands == 0);
g_assert (global_ragent_cands == 0);
g_debug ("test-fullmode: Ran mainloop, removing streams...");
/* step: clean up resources and exit */
nice_agent_remove_stream (lagent, ls_id);
nice_agent_remove_stream (ragent, rs_id);
return 0;
}
static int run_full_test_control_conflict (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, gboolean role)
{
NiceAddress laddr, raddr;
NiceCandidateDesc cdes = { /* candidate description (no ports) */
(gchar *)"1", /* foundation */
test_component_id,
NICE_CANDIDATE_TRANSPORT_UDP, /* transport */
100000, /* priority */
NULL, /* address */
NICE_CANDIDATE_TYPE_HOST, /* type */
NULL /* base-address */
};
GSList *cands, *i;
guint ls_id, rs_id;
global_lagent_state =
global_ragent_state = NICE_COMPONENT_STATE_LAST;
global_lagent_gathering_done =
global_ragent_gathering_done = FALSE;
global_lagent_cands =
global_ragent_cands = 0;
global_lagent_ibr_received =
global_ragent_ibr_received = FALSE;
g_object_set (G_OBJECT (lagent), "controlling-mode", role, NULL);
g_object_set (G_OBJECT (ragent), "controlling-mode", role, NULL);
/* 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);
/* step: run mainloop until local candidates are ready
* (see timer_cb() above) */
if (global_lagent_gathering_done != TRUE ||
global_ragent_gathering_done != TRUE) {
g_debug ("test-fullmode: Added streams, running mainloop until 'candidate-gathering-done'...");
g_main_loop_run (global_mainloop);
g_assert (global_lagent_gathering_done == TRUE);
g_assert (global_ragent_gathering_done == TRUE);
}
/* step: find out the local candidates of each agent */
cands = nice_agent_get_local_candidates(lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
for (i = cands; i; i = i->next) {
NiceCandidate *cand = i->data;
if (cand) {
g_debug ("test-fullmode: local port L %u", cand->addr.port);
laddr = cand->addr;
}
}
g_slist_free (cands);
cands = nice_agent_get_local_candidates(ragent, rs_id, NICE_COMPONENT_TYPE_RTP);
for (i = cands; i; i = i->next) {
NiceCandidate *cand = i->data;
if (cand) {
g_debug ("test-fullmode: local port R %u", cand->addr.port);
raddr = cand->addr;
}
}
g_slist_free (cands);
g_debug ("test-fullmode: Got local candidates...");
/* step: pass the remote candidates to agents */
cands = g_slist_append (NULL, &cdes);
{
const gchar *ufrag = NULL, *password = NULL;
nice_agent_get_local_credentials(lagent, ls_id, &ufrag, &password);
nice_agent_set_remote_credentials (ragent,
rs_id, ufrag, password);
nice_agent_get_local_credentials(ragent, rs_id, &ufrag, &password);
nice_agent_set_remote_credentials (lagent,
ls_id, ufrag, password);
}
cdes.addr = &raddr;
nice_agent_set_remote_candidates (lagent, ls_id, NICE_COMPONENT_TYPE_RTP, cands);
cdes.addr = &laddr;
nice_agent_set_remote_candidates (ragent, rs_id, NICE_COMPONENT_TYPE_RTP, cands);
g_slist_free (cands);
g_debug ("test-fullmode: Set properties, next running mainloop until connectivity checks succeed...");
/* step: run the mainloop until connectivity checks succeed
* (see timer_cb() above) */
g_main_loop_run (global_mainloop);
/* note: verify that correct number of local candidates were reported */
g_assert (global_lagent_cands == 1);
g_assert (global_ragent_cands == 1);
g_debug ("test-fullmode: Ran mainloop, removing streams...");
/* step: clean up resources and exit */
......@@ -255,6 +491,7 @@ int main (void)
NiceAddress baseaddr;
int result;
guint timer_id;
const char *stun_server = NULL, *stun_server_port = NULL;
g_type_init ();
global_mainloop = g_main_loop_new (NULL, FALSE);
......@@ -271,11 +508,11 @@ int main (void)
ragent = nice_agent_new (&udpfactory);
/* step: attach to mainloop (needed to register the fds) */
nice_agent_main_context_attach (lagent, g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
nice_agent_main_context_attach (ragent, g_main_loop_get_context (global_mainloop), cb_nice_recv, NULL);
nice_agent_main_context_attach (lagent, g_main_loop_get_context (global_mainloop), cb_nice_recv, (gpointer)1);
nice_agent_main_context_attach (ragent, g_main_loop_get_context (global_mainloop), cb_nice_recv, (gpointer)2);
/* step: add a timer to catch state changes triggered by signals */
timer_id = g_timeout_add (2000, timer_cb, NULL);
timer_id = g_timeout_add (30000, timer_cb, NULL);
/* step: specify which local interface to use */
if (!nice_address_set_ipv4_from_string (&baseaddr, "127.0.0.1"))
......@@ -304,27 +541,69 @@ int main (void)
g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received",
G_CALLBACK (cb_initial_binding_request_received), (gpointer)2);
g_debug ("test-fullmode: running test for the 1st time");
result = run_full_test (lagent, ragent, &baseaddr);
stun_server = getenv ("NICE_STUN_SERVER");
stun_server_port = getenv ("NICE_STUN_SERVER_PORT");
if (stun_server) {
g_object_set (G_OBJECT (lagent), "stun-server", stun_server, NULL);
g_object_set (G_OBJECT (lagent), "stun-server-port", atoi (stun_server_port), NULL);
g_object_set (G_OBJECT (ragent), "stun-server", stun_server, NULL);
g_object_set (G_OBJECT (ragent), "stun-server-port", atoi (stun_server_port), NULL);
}
/* step: check results of first run */
{
gpointer pointer;
gchar *string = NULL;
guint port = 0;
gboolean mode = FALSE;
g_object_get (G_OBJECT (lagent), "socket-factory", &pointer, NULL);
g_assert (pointer == (gpointer)&udpfactory);
g_object_get (G_OBJECT (lagent), "stun-server", &string, NULL);
g_assert (stun_server == NULL || strcmp (string, stun_server) == 0);
g_object_get (G_OBJECT (lagent), "stun-server-port", &port, NULL);
g_assert (stun_server_port == NULL || port == (guint)atoi (stun_server_port));
g_object_get (G_OBJECT (lagent), "turn-server", &string, NULL);
g_object_get (G_OBJECT (lagent), "turn-server-port", &port, NULL);
g_object_get (G_OBJECT (lagent), "controlling-mode", &mode, NULL);
g_assert (mode == TRUE);
}
/* step: run test the first time */
g_debug ("test-fullmode: TEST STARTS / running test for the 1st time");
result = run_full_test (lagent, ragent, &baseaddr);
priv_print_global_status ();
g_assert (result == 0);
g_assert (global_lagent_state == NICE_COMPONENT_STATE_READY);
g_assert (global_ragent_state == NICE_COMPONENT_STATE_READY);
/* step: run test again without unref'ing agents */
g_debug ("test-fullmode: TEST STARTS / running test for the 2nd time");
result = run_full_test (lagent, ragent, &baseaddr);
priv_print_global_status ();
g_assert (result == 0);
g_assert (global_lagent_state == NICE_COMPONENT_STATE_READY);
g_assert (global_ragent_state == NICE_COMPONENT_STATE_READY);
global_lagent_state = NICE_COMPONENT_STATE_LAST;
global_ragent_state = NICE_COMPONENT_STATE_LAST;
global_candidate_gathering_done = FALSE;
global_lagent_ibr_received =
global_ragent_ibr_received = FALSE;
global_lagent_cands =
global_ragent_cands = 0;
/* run test with incorrect credentials (make sure process fails) */
g_debug ("test-fullmode: TEST STARTS / incorrect credentials");
result = run_full_test_wrong_password (lagent, ragent, &baseaddr);
priv_print_global_status ();
g_assert (result == 0);
g_assert (global_lagent_state == NICE_COMPONENT_STATE_FAILED);
g_assert (global_ragent_state == NICE_COMPONENT_STATE_FAILED);
g_debug ("test-fullmode: running test the 2nd time");
result = run_full_test (lagent, ragent, &baseaddr);
/* run test with a conflict in controlling mode: controlling-controlling */
g_debug ("test-fullmode: TEST STARTS / controlling mode conflict case-1");
result = run_full_test_control_conflict (lagent, ragent, &baseaddr, TRUE);
priv_print_global_status ();
g_assert (result == 0);
g_assert (global_lagent_state == NICE_COMPONENT_STATE_READY);
g_assert (global_ragent_state == NICE_COMPONENT_STATE_READY);
/* run test with a conflict in controlling mode: controlled-controlled */
g_debug ("test-fullmode: TEST STARTS / controlling mode conflict case-2");
result = run_full_test_control_conflict (lagent, ragent, &baseaddr, FALSE);
priv_print_global_status ();
g_assert (result == 0);
g_assert (global_lagent_state == NICE_COMPONENT_STATE_READY);
g_assert (global_ragent_state == NICE_COMPONENT_STATE_READY);
......
......@@ -35,6 +35,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
......
......@@ -35,6 +35,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
......@@ -73,6 +76,7 @@ main (void)
gint pipe_fds[2];
GSList *fds = NULL;
GSList *readable;
ssize_t w;
memset (&addr, 0, sizeof (addr));
g_type_init ();
......@@ -102,7 +106,8 @@ main (void)
if (pipe (pipe_fds) != 0)
g_assert_not_reached ();
write (pipe_fds[1], "hello", 5);
w = write (pipe_fds[1], "hello", 5);
g_assert (w == 5);
fds = g_slist_append (fds, GUINT_TO_POINTER (pipe_fds[0]));
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "agent.h"
......@@ -53,10 +56,10 @@ main (void)
/* 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0)
= 2^32*1 + 2*5000 + 0
= 4294977296 */
g_assert (nice_candidate_pair_priority (1,5000) == 4294977296);
g_assert (nice_candidate_pair_priority (1,5000) == 4294977296LL);
/* 2^32*1 + 2*5000 + 1 = 4294977297 */
g_assert (nice_candidate_pair_priority (5000, 1) == 4294977297);
g_assert (nice_candidate_pair_priority (5000, 1) == 4294977297LL);
return 0;
}
......
......@@ -35,6 +35,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
......
......@@ -35,6 +35,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
......
......@@ -35,6 +35,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
......@@ -187,7 +190,7 @@ test_stun_valid_password (
bres = stun_message_new (STUN_MESSAGE_BINDING_RESPONSE,
"0123456789abcdef", 2);
bres->attributes[0] = stun_attribute_mapped_address_new (
from.addr_ipv4, 5678);
from.addr.addr_ipv4, 5678);
bres->attributes[1] = stun_attribute_username_new (username);
packed_len = stun_message_pack (bres, &packed);
stun_message_free (bres);
......
......@@ -34,6 +34,9 @@
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <string.h>
......
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