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

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);
......
This diff is collapsed.
......@@ -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--
......
This diff is collapsed.
......@@ -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;
if (agent->discovery_timer_id)
g_source_remove (agent->discovery_timer_id),
agent->discovery_timer_id = 0;
agent->discovery_unsched_items = 0;
}
if (agent->discovery_timer_id)
g_source_remove (agent->discovery_timer_id),
agent->discovery_timer_id = 0;
}
/**
......@@ -112,9 +123,45 @@ gboolean discovery_prune_stream (NiceAgent *agent, guint stream_id)
i = i->next;
}
/* return FALSE if there was a memory allocation failure */
if (agent->conncheck_list == NULL && i != NULL)
return FALSE;
if (agent->discovery_list == NULL) {
/* return FALSE if there was a memory allocation failure */
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 */
/* 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,40 +260,148 @@ 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) */
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 */
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;
/* 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;
}