Commit 355dafd0 authored by Kai Vehmanen's avatar Kai Vehmanen

Major update to the nice/agent interface: Added full-mode API and initial...

Major update to the nice/agent interface: Added full-mode API and initial implementation using the new nice/stun interface. Added unit test test-fullmode.c to cover the added functionality. Some public APIs of nice/agent/agent.h have been modified, making this change API/ABI incompatible.

darcs-hash:20070521153033-77cd4-c76ab583d06839e601f46b6734355dd8b66f7494.gz
parent 91d4e0a1
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2006, 2007 Collabora Ltd.
* Contact: Dafydd Harries
* (C) 2006, 2007 Nokia Corporation. All rights reserved.
* Contact: Kai Vehmanen
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifndef _NICE_AGENT_PRIV_H
#define _NICE_AGENT_PRIV_H
/* note: this is a private header part of agent.h */
#include <glib.h>
#include "agent.h"
#include "component.h"
#include "candidate.h"
#include "stream.h"
#include "conncheck.h"
#define NICE_AGENT_TIMER_TA_DEFAULT 20 /* timer Ta, msecs */
#define NICE_AGENT_TIMER_TR_DEFAULT 15000 /* timer Tr, msecs */
/** An upper limit to size of STUN packets handled (based on Ethernet
* MTU and estimated typical sizes of ICE STUN packet */
#define MAX_STUN_DATAGRAM_PAYLOAD 1300
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 */
gchar *stun_server_ip; /**< property: STUN server IP */
guint stun_server_port; /**< property: STUN server port */
gchar *turn_server_ip; /**< property: TURN server IP */
guint turn_server_port; /**< property: TURN server port */
gboolean controlling_mode; /**< property: controlling-mode */
GSList *local_addresses; /**< list of NiceAddresses for local
interfaces */
GSList *streams; /**< list of Stream objects */
gboolean main_context_set; /**< is the main context set */
GMainContext *main_context; /**< main context pointer */
NiceAgentRecvFunc read_func; /**< callback for media deliver */
gpointer read_func_data; /**< media delivery callback context */
guint next_candidate_id; /**< id of next created candidate */
guint next_stream_id; /**< id of next created candidate */
NiceRNG *rng; /**< random number generator */
GSList *discovery_list; /**< list of CandidateDiscovery items */
guint discovery_unsched_items; /**< number of discovery items unscheduled */
guint discovery_timer_id; /**< id of discovery timer */
GSList *conncheck_list; /**< list of CandidatePair items */
guint conncheck_timer_id; /**< id of discovery timer */
NiceCheckListState conncheck_state; /**< checklist state */
/* XXX: add pointer to internal data struct for ABI-safe extensions */
};
gboolean
agent_find_component (
NiceAgent *agent,
guint stream_id,
guint component_id,
Stream **stream,
Component **component);
Stream *agent_find_stream (NiceAgent *agent, guint stream_id);
void agent_signal_gathering_done (NiceAgent *agent);
void agent_signal_new_selected_pair (
NiceAgent *agent,
guint stream_id,
guint component_id,
const gchar *local_foundation,
const gchar *remote_foundation);
void agent_signal_component_state_change (
NiceAgent *agent,
guint stream_id,
guint component_id,
NiceComponentState state);
void agent_signal_new_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);
#endif /*_NICE_AGENT_PRIV_H */
...@@ -2,3 +2,10 @@ ...@@ -2,3 +2,10 @@
VOID:UINT,UINT,UINT VOID:UINT,UINT,UINT
# candidate-gathering-done # candidate-gathering-done
VOID:VOID VOID:VOID
# new-selected-pair
VOID:UINT,UINT,STRING,STRING
# new-candidate
VOID:UINT,UINT,STRING
# initial-binding-request-received
VOID:UINT
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -50,74 +51,31 @@ ...@@ -50,74 +51,31 @@
#include <glib.h> #include <glib.h>
#include "stun/bind.h" #include "stun/bind.h"
#include "stun.h" #include "stun.h"
#include "udp.h" #include "udp.h"
#include "candidate.h"
#include "component.h" #include "component.h"
#include "conncheck.h"
#include "discovery.h"
#include "agent.h" #include "agent.h"
#include "agent-priv.h"
#include "agent-signals-marshal.h" #include "agent-signals-marshal.h"
typedef enum
{
NICE_CHECK_WAITING = 1,
NICE_CHECK_IN_PROGRESS,
NICE_CHECK_SUCCEEDED,
NICE_CHECK_FAILED,
NICE_CHECK_FROZEN
} NiceCheckState;
typedef struct _CandidateDiscoveryUDP CandidateDiscoveryUDP;
struct _CandidateDiscoveryUDP
{
NiceAgent *agent; /* back pointer to owner */
NiceCandidateType type; /* candidate type STUN or TURN */
int socket; /* existing socket to use */
gchar *server_addr; /* STUN/TURN server address */
NiceAddress *interface; /* Address of local interface */
stun_bind_t *ctx;
GTimeVal next_tick; /* next tick timestamp */
gboolean pending; /* is discovery in progress? */
gboolean done; /* is discovery complete? */
guint stream_id;
guint component_id;
};
typedef struct _CandidatePair CandidatePair;
struct _CandidatePair
{
NiceAgent *agent; /* back pointer to owner */
guint stream_id;
guint component_id;
NiceCandidate *local;
NiceCandidate *remote;
gchar *foundation;
NiceCheckState state;
};
#include "stream.h" #include "stream.h"
typedef enum
{
CHECK_LIST_STATE_RUNNING,
CHECK_LIST_STATE_COMPLETED,
} CheckListState;
#define NICE_AGENT_TIMER_TA_DEFAULT 20; /* timer Ta, msecs */
#define NICE_AGENT_TIMER_TR_DEFAULT 15000; /* timer Tr, msecs */
G_DEFINE_TYPE (NiceAgent, nice_agent, G_TYPE_OBJECT); G_DEFINE_TYPE (NiceAgent, nice_agent, G_TYPE_OBJECT);
enum enum
{ {
PROP_SOCKET_FACTORY = 1, PROP_SOCKET_FACTORY = 1,
PROP_STUN_SERVER, PROP_STUN_SERVER,
PROP_STUN_SERVER_PORT, PROP_STUN_SERVER_PORT,
PROP_TURN_SERVER, PROP_TURN_SERVER,
PROP_TURN_SERVER_PORT PROP_TURN_SERVER_PORT,
PROP_CONTROLLING_MODE,
PROP_FULL_MODE
}; };
...@@ -125,15 +83,19 @@ enum ...@@ -125,15 +83,19 @@ enum
{ {
SIGNAL_COMPONENT_STATE_CHANGED, SIGNAL_COMPONENT_STATE_CHANGED,
SIGNAL_CANDIDATE_GATHERING_DONE, SIGNAL_CANDIDATE_GATHERING_DONE,
SIGNAL_NEW_SELECTED_PAIR,
SIGNAL_NEW_CANDIDATE,
SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED,
N_SIGNALS, N_SIGNALS,
}; };
static guint signals[N_SIGNALS]; static guint signals[N_SIGNALS];
static gboolean priv_attach_new_stream (NiceAgent *agent, Stream *stream);
static void priv_deattach_stream (Stream *stream);
static Stream * Stream *agent_find_stream (NiceAgent *agent, guint stream_id)
find_stream (NiceAgent *agent, guint stream_id)
{ {
GSList *i; GSList *i;
...@@ -149,8 +111,8 @@ find_stream (NiceAgent *agent, guint stream_id) ...@@ -149,8 +111,8 @@ find_stream (NiceAgent *agent, guint stream_id)
} }
static gboolean gboolean
find_component ( agent_find_component (
NiceAgent *agent, NiceAgent *agent,
guint stream_id, guint stream_id,
guint component_id, guint component_id,
...@@ -162,7 +124,7 @@ find_component ( ...@@ -162,7 +124,7 @@ find_component (
if (component_id != 1) if (component_id != 1)
return FALSE; return FALSE;
s = find_stream (agent, stream_id); s = agent_find_stream (agent, stream_id);
if (s == NULL) if (s == NULL)
return FALSE; return FALSE;
...@@ -205,6 +167,11 @@ nice_agent_class_init (NiceAgentClass *klass) ...@@ -205,6 +167,11 @@ nice_agent_class_init (NiceAgentClass *klass)
gobject_class->dispose = nice_agent_dispose; gobject_class->dispose = nice_agent_dispose;
/* install properties */ /* 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_object_class_install_property (gobject_class, PROP_SOCKET_FACTORY,
g_param_spec_pointer ( g_param_spec_pointer (
...@@ -227,7 +194,7 @@ nice_agent_class_init (NiceAgentClass *klass) ...@@ -227,7 +194,7 @@ nice_agent_class_init (NiceAgentClass *klass)
"STUN server port", "STUN server port",
"The STUN server used to obtain server-reflexive candidates", "The STUN server used to obtain server-reflexive candidates",
1, 65536, 1, 65536,
3478, /* default port */ IPPORT_STUN, /* default port */
G_PARAM_READWRITE)); G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_STUN_SERVER, g_object_class_install_property (gobject_class, PROP_STUN_SERVER,
...@@ -247,8 +214,25 @@ nice_agent_class_init (NiceAgentClass *klass) ...@@ -247,8 +214,25 @@ nice_agent_class_init (NiceAgentClass *klass)
3478, /* no default port for TURN, use the STUN default*/ 3478, /* no default port for TURN, use the STUN default*/
G_PARAM_READWRITE)); G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_CONTROLLING_MODE,
g_param_spec_boolean (
"controlling-mode",
"ICE controlling mode",
"Whether the agent is in controlling mode",
FALSE, /* not a construct property, ignored */
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_FULL_MODE,
g_param_spec_boolean (
"full-mode",
"ICE full mode",
"Whether agent runs in ICE full mode",
TRUE, /* use full mode by default */
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/* install signals */ /* install signals */
/* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer self) */
signals[SIGNAL_COMPONENT_STATE_CHANGED] = signals[SIGNAL_COMPONENT_STATE_CHANGED] =
g_signal_new ( g_signal_new (
"component-state-changed", "component-state-changed",
...@@ -263,6 +247,7 @@ nice_agent_class_init (NiceAgentClass *klass) ...@@ -263,6 +247,7 @@ nice_agent_class_init (NiceAgentClass *klass)
G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT,
G_TYPE_INVALID); G_TYPE_INVALID);
/* signature: void cb(NiceAgent *agent, gpointer self) */
signals[SIGNAL_CANDIDATE_GATHERING_DONE] = signals[SIGNAL_CANDIDATE_GATHERING_DONE] =
g_signal_new ( g_signal_new (
"candidate-gathering-done", "candidate-gathering-done",
...@@ -275,6 +260,53 @@ nice_agent_class_init (NiceAgentClass *klass) ...@@ -275,6 +260,53 @@ nice_agent_class_init (NiceAgentClass *klass)
G_TYPE_NONE, G_TYPE_NONE,
0, 0,
G_TYPE_INVALID); G_TYPE_INVALID);
/* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id,
gchar *lfoundation, gchar* rfoundation, gpointer self) */
signals[SIGNAL_NEW_SELECTED_PAIR] =
g_signal_new (
"new-selected-pair",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL,
NULL,
agent_marshal_VOID__UINT_UINT_STRING_STRING,
G_TYPE_NONE,
4,
G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_INVALID);
/* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, gchar *foundation) */
signals[SIGNAL_NEW_CANDIDATE] =
g_signal_new (
"new-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 (
"initial-binding-request-received",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
0,
NULL,
NULL,
agent_marshal_VOID__UINT,
G_TYPE_NONE,
1,
G_TYPE_UINT,
G_TYPE_INVALID);
} }
...@@ -284,11 +316,17 @@ nice_agent_init (NiceAgent *agent) ...@@ -284,11 +316,17 @@ nice_agent_init (NiceAgent *agent)
agent->next_candidate_id = 1; agent->next_candidate_id = 1;
agent->next_stream_id = 1; agent->next_stream_id = 1;
agent->discovery_unsched_items = 0; /* set defaults; not construct params, so set here */
g_assert (agent->stun_server_port != IPPORT_STUN);
g_assert (agent->turn_server_port != IPPORT_STUN);
agent->stun_server_port = IPPORT_STUN;
agent->turn_server_port = IPPORT_STUN;
agent->controlling_mode = TRUE;
/* XXX: make configurable */
agent->full_mode = TRUE;
agent->discovery_list = NULL; agent->discovery_list = NULL;
agent->discovery_unsched_items = 0;
agent->discovery_timer_id = 0;
agent->conncheck_timer_id = 0;
agent->rng = nice_rng_new (); agent->rng = nice_rng_new ();
} }
...@@ -328,15 +366,27 @@ nice_agent_get_property ( ...@@ -328,15 +366,27 @@ nice_agent_get_property (
case PROP_STUN_SERVER: case PROP_STUN_SERVER:
g_value_set_string (value, agent->stun_server_ip); g_value_set_string (value, agent->stun_server_ip);
break;
case PROP_STUN_SERVER_PORT: case PROP_STUN_SERVER_PORT:
g_value_set_uint (value, agent->stun_server_port); g_value_set_uint (value, agent->stun_server_port);
break;
case PROP_TURN_SERVER: case PROP_TURN_SERVER:
g_value_set_string (value, agent->turn_server_ip); g_value_set_string (value, agent->turn_server_ip);
break;
case PROP_TURN_SERVER_PORT: case PROP_TURN_SERVER_PORT:
g_value_set_uint (value, agent->turn_server_port); g_value_set_uint (value, agent->turn_server_port);
break;
case PROP_CONTROLLING_MODE:
g_value_set_boolean (value, agent->controlling_mode);
break;
case PROP_FULL_MODE:
g_value_set_boolean (value, agent->full_mode);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
...@@ -371,138 +421,63 @@ nice_agent_set_property ( ...@@ -371,138 +421,63 @@ nice_agent_set_property (
agent->turn_server_ip = g_value_dup_string (value); agent->turn_server_ip = g_value_dup_string (value);
break; break;
case PROP_CONTROLLING_MODE:
agent->controlling_mode = g_value_get_boolean (value);
break;
case PROP_FULL_MODE:
agent->full_mode = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
} }
} }
static int void agent_signal_gathering_done (NiceAgent *agent)
nice_agent_get_local_host_candidate_sockfd (
NiceAgent *agent,
guint stream_id,
guint component_id,
NiceAddress *address)
{ {
Component *component; g_signal_emit (agent, signals[SIGNAL_CANDIDATE_GATHERING_DONE], 0);
GSList *i;
if (!find_component (agent, stream_id, component_id, NULL, &component)) {
return -1;
}
for (i = component->local_candidates; i; i = i->next) {
NiceCandidate *candidate = i->data;
/* note nice_address_equal() also compares the ports and
* we don't want that here */
if (address->type == NICE_ADDRESS_TYPE_IPV4 &&
address->addr_ipv4 == candidate->base_addr.addr_ipv4)
return candidate->sock.fileno;
}
return -1;
} }
static void
nice_agent_add_local_host_candidate (
NiceAgent *agent,
guint stream_id,
guint component_id,
NiceAddress *address)
{
NiceCandidate *candidate;
Component *component;
if (!find_component (agent, stream_id, component_id, NULL, &component))
return;
candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST); void agent_signal_initial_binding_request_received (NiceAgent *agent, Stream *stream)
candidate->id = agent->next_candidate_id++;
candidate->stream_id = stream_id;
candidate->component_id = component_id;
candidate->addr = *address;
candidate->base_addr = *address;
component->local_candidates = g_slist_append (component->local_candidates,
candidate);
candidate->priority =
0x1000000 * 126 + 0x100 * 0 + 256 - component_id; /* sect:4.1.2.1(-14) */
/* generate username/password */
nice_rng_generate_bytes_print (agent->rng, 8, candidate->username);
nice_rng_generate_bytes_print (agent->rng, 8, candidate->password);
/* allocate socket */
/* XXX: handle error */
if (!nice_udp_socket_factory_make (agent->socket_factory,
&(candidate->sock), address))
g_assert_not_reached ();
candidate->addr = candidate->sock.addr;
candidate->base_addr = candidate->sock.addr;
}
/* compiles but is not called yet */
static void
nice_agent_generate_username_and_password(NiceCandidate *candidate)
{ {
NiceRNG *rng; if (stream->initial_binding_request_received != TRUE) {
/* generate username/password */ stream->initial_binding_request_received = TRUE;
rng = nice_rng_new (); g_signal_emit (agent, signals[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED], 0, stream->id);
nice_rng_generate_bytes_print (rng, 8, candidate->username); }
nice_rng_generate_bytes_print (rng, 8, candidate->password);
nice_rng_free (rng);
} }
static void void agent_signal_new_selected_pair (NiceAgent *agent, guint stream_id, guint component_id, const gchar *local_foundation, const gchar *remote_foundation)
nice_agent_add_server_reflexive_candidate (
NiceAgent *agent,
guint stream_id,
guint component_id,
NiceAddress *address)
{ {
NiceCandidate *candidate;
Component *component; Component *component;
if (!find_component (agent, stream_id, component_id, NULL, &component)) if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
return; return;
candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE); g_signal_emit (agent, signals[SIGNAL_NEW_SELECTED_PAIR], 0,
candidate->id = agent->next_candidate_id++; stream_id, component_id,
candidate->stream_id = stream_id; local_foundation, remote_foundation);
candidate->component_id = component_id;
candidate->addr = *address;
candidate->base_addr = *address;
component->local_candidates = g_slist_append (component->local_candidates,
candidate);
candidate->priority =
0x1000000 * 125 + 0x100 * 0 + 256 - component_id; /* sect:4.1.2.1(-14) */
/* generate username/password */
nice_agent_generate_username_and_password (candidate);
/* XXX: how to link to the socket of a local candidate? */
#if 0
candidate->base_addr = candidate->sock.addr;
#endif
} }
static void nice_agent_free_discovery_candidate_udp (gpointer data, gpointer user_data) void agent_signal_new_candidate (NiceAgent *agent, NiceCandidate *candidate)
{ {
CandidateDiscoveryUDP *cand_udp = data; g_signal_emit (agent, signals[SIGNAL_NEW_CANDIDATE], 0,
g_free (cand_udp->server_addr); candidate->stream_id,
g_slice_free (CandidateDiscoveryUDP, cand_udp); candidate->component_id,
candidate->foundation);
} }
static void priv_signal_component_state_gathering (NiceAgent *agent, guint stream_id, guint component_id) void agent_signal_component_state_change (NiceAgent *agent, guint stream_id, guint component_id, NiceComponentState state)
{ {
Component *component; Component *component;
if (!find_component (agent, stream_id, component_id, NULL, &component)) if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
return; return;
if (component->state != NICE_COMPONENT_STATE_GATHERING) { if (component->state != state && state < NICE_COMPONENT_STATE_LAST) {
component->state = NICE_COMPONENT_STATE_GATHERING; g_debug ("stream %u component %u state change %u -> %u.",
stream_id, component_id, component->state, state);
component->state = state;
g_signal_emit (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], 0, g_signal_emit (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], 0,
stream_id, component_id, component->state); stream_id, component_id, component->state);
...@@ -514,7 +489,7 @@ static void priv_signal_component_state_connecting (NiceAgent *agent, guint stre ...@@ -514,7 +489,7 @@ static void priv_signal_component_state_connecting (NiceAgent *agent, guint stre
{ {
Component *component; Component *component;
if (!find_component (agent, stream_id, component_id, NULL, &component)) if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
return; return;
if (component->state != NICE_COMPONENT_STATE_CONNECTING) { if (component->state != NICE_COMPONENT_STATE_CONNECTING) {
...@@ -526,197 +501,14 @@ static void priv_signal_component_state_connecting (NiceAgent *agent, guint stre ...@@ -526,197 +501,14 @@ static void priv_signal_component_state_connecting (NiceAgent *agent, guint stre
} }
#endif #endif
/**
* Timer callback that handles scheduling new candidate discovery
* processes (paced by the Ta timer), and handles running of the
* existing discovery processes.
*
* This function is designed for the g_timeout_add() interface.
*
* @return will return FALSE when no more pending timers.
*/
static gboolean nice_agent_discovery_tick (gpointer pointer)
{
CandidateDiscoveryUDP *cand_udp;
NiceAgent *agent = pointer;
GSList *i;
int not_done = 0;
g_debug ("check tick with list %p (1)", agent->discovery_list);
for (i = agent->discovery_list; i ; i = i->next) {
cand_udp = i->data;
if (cand_udp->pending != TRUE) {
cand_udp->pending = TRUE;
if (agent->discovery_unsched_items)
--agent->discovery_unsched_items;
g_debug ("scheduling cand type %u addr %s and socket %d.\n", cand_udp->type, cand_udp->server_addr, cand_udp->socket);
if (cand_udp->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) {
struct sockaddr_in stun_server;
memset (&stun_server, 0, sizeof(stun_server));
/* XXX: using hardcoded server address for now */
stun_server.sin_addr.s_addr = inet_addr("127.0.0.1");
stun_server.sin_port = htons(3478);
int res;
res = stun_bind_start (&cand_udp->ctx, cand_udp->socket,
(struct sockaddr*)&stun_server, sizeof(stun_server));
if (res == 0) {
/* case: success, start waiting for the result */
g_get_current_time (&cand_udp->next_tick);
priv_signal_component_state_gathering (agent,
cand_udp->stream_id,
cand_udp->component_id);
}
else {
/* case: error in starting discovery, start the next discovery */
cand_udp->done = TRUE;
continue;
}
}
else
/* allocate relayed candidates */
g_assert_not_reached ();
++not_done;
}
if (cand_udp->done != TRUE) {
struct sockaddr_in mapped_addr;
socklen_t socklen = sizeof(mapped_addr);
GTimeVal now;
g_get_current_time (&now);
/* note: macro from sys/time.h but compatible with GTimeVal */
if (timercmp(&cand_udp->next_tick, &now, <)) {
int res = stun_bind_resume (cand_udp->ctx, (struct sockaddr*)&mapped_addr, &socklen);
if (res == 0) {
/* case: discovery process succesfully completed */
NiceAddress *niceaddr;
niceaddr = nice_address_new();
niceaddr->type = NICE_ADDRESS_TYPE_IPV4;
niceaddr->addr_ipv4 = ntohl(mapped_addr.sin_addr.s_addr);
niceaddr->port = ntohs(mapped_addr.sin_port);
{
gchar ip[NICE_ADDRESS_STRING_LEN];
nice_address_to_string (niceaddr, ip);
g_debug("%s: our public contact address is %s\n",
__func__, ip);
}
/* XXX: add
* g_signal_emit (agent, signals[SIGNAL_NEW_CANDIDATE], 0); */
nice_agent_add_server_reflexive_candidate (
cand_udp->agent,
cand_udp->stream_id,
cand_udp->component_id,
niceaddr);
nice_address_free (niceaddr);
cand_udp->done = TRUE;
}
else if (res == EAGAIN) {
/* case: not ready complete, so schedule next timeout */
unsigned int timeout = stun_bind_timeout (cand_udp->ctx);
g_get_current_time (&cand_udp->next_tick);
g_time_val_add (&cand_udp->next_tick, timeout * 10);
/* note: macro from sys/time.h but compatible with GTimeVal */
if (timercmp(&cand_udp->next_tick, &agent->next_check_tv, <)) {
agent->next_check_tv = cand_udp->next_tick;
}
++not_done;
}
else {
/* case: error, abort processing */
cand_udp->done = TRUE;
}
}
}
}
if (not_done == 0) {
g_debug ("Candidate gathering FINISHED, stopping Ta timer.");
g_slist_foreach (agent->discovery_list, nice_agent_free_discovery_candidate_udp, NULL);
g_slist_free (agent->discovery_list),
agent->discovery_list = NULL;
g_signal_emit (agent, signals[SIGNAL_CANDIDATE_GATHERING_DONE], 0);
/* note: no pending timers, return FALSE to stop timer */
return FALSE;
}
return TRUE;
}
static void nice_agent_schedule_discovery (NiceAgent *agent)
{
if (agent->discovery_list) {
GTimeVal now;
/* XXX: make timeout Ta configurable */
guint next = NICE_AGENT_TIMER_TA_DEFAULT;
if (agent->discovery_unsched_items == 0)
next = (guint)-1;
/* XXX: send a component state-change, but, but, how do we
* actually do this? back to the drawing board... */
/*
* Component *component;
* if (!find_component (agent, stream_id, component_id, &stream, &component))
* return 0;
*
* component->state = NICE_COMPONENT_STATE_GATHERING;
*
* g_signal_emit (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], 0,
* cand_stream->id, component->id, component->state);
*/
nice_agent_discovery_tick (agent);
g_get_current_time (&now);
guint msecs = (agent->next_check_tv.tv_sec - now.tv_sec) * 1000;
msecs += (agent->next_check_tv.tv_usec - now.tv_usec) / 1000;
if (msecs < next)
next = msecs;
g_debug ("Scheduling a timeout of %u msec.", next);
g_timeout_add (next, nice_agent_discovery_tick, agent);
}
}
/** /**
* nice_agent_add_stream: * nice_agent_add_stream:
* @agent: a NiceAgent * @agent: a NiceAgent
* @handle_recv: a function called when the stream recieves data * @n_components: number of components
* @handle_recv_data: data passed as last parameter to @handle_recv
* *
* Add a data stream to @agent. * Add a data stream to @agent.
* *
* Returns: the ID of the new stream * Returns: the ID of the new stream, 0 on failure
**/ **/
guint guint
nice_agent_add_stream ( nice_agent_add_stream (
...@@ -728,6 +520,8 @@ nice_agent_add_stream ( ...@@ -728,6 +520,8 @@ nice_agent_add_stream (
g_assert (n_components == 1); g_assert (n_components == 1);
/* XXX: make memory-allocation safe */
if (!agent->streams) { if (!agent->streams) {
/* note: this contains a Y2038 issue */ /* note: this contains a Y2038 issue */
agent->next_check_tv.tv_sec = agent->next_check_tv.tv_sec =
...@@ -736,47 +530,66 @@ nice_agent_add_stream ( ...@@ -736,47 +530,66 @@ nice_agent_add_stream (
stream = stream_new (); stream = stream_new ();
stream->id = agent->next_stream_id++; stream->id = agent->next_stream_id++;
/* 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);
agent->streams = g_slist_append (agent->streams, stream); agent->streams = g_slist_append (agent->streams, stream);
/* generate a local host candidate for each local address */ g_debug ("In %s mode, starting candidate gathering.", agent->full_mode ? "ICE-FULL" : "ICE-LITE");
if (agent->full_mode) { /* generate a local host candidate for each local address */
g_debug ("In FULL mode, starting candidate gathering.\n");
}
for (i = agent->local_addresses; i; i = i->next) for (i = agent->local_addresses; i; i = i->next)
{ {
NiceAddress *addr = i->data; NiceAddress *addr = i->data;
CandidateDiscoveryUDP *cand_udp; CandidateDiscovery *cand;
int sockfd; NiceCandidate *host_candidate;
nice_agent_add_local_host_candidate (agent, stream->id,
stream->component->id, addr);
if (agent->full_mode) { /* XXX: not multi-component ready */
sockfd = nice_agent_get_local_host_candidate_sockfd (agent, stream->id, stream->component->id, addr); host_candidate = discovery_add_local_host_candidate (agent, stream->id,
stream->component->id, addr);
if (host_candidate &&
agent->full_mode &&
agent->stun_server_ip) {
/* XXX: need to check for redundant candidates? -> not yet, /* XXX: need to check for redundant candidates? -> not yet,
* this is done later on */ * this is done later on */
cand_udp = g_slice_new0 (CandidateDiscoveryUDP); cand = g_slice_new0 (CandidateDiscovery);
cand_udp->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE; if (cand) {
cand_udp->socket = sockfd; cand->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
cand_udp->server_addr = g_strdup ("127.0.0.1" /*"stunserver.org"*/); cand->socket = host_candidate->sockptr->fileno;
cand_udp->interface = addr; cand->nicesock = host_candidate->sockptr;
cand_udp->stream_id = stream->id; cand->server_addr = agent->stun_server_ip;
cand_udp->component_id = stream->component->id; cand->interface = addr;
cand_udp->agent = agent; cand->stream = stream;
g_debug ("Adding srv-rflx candidate %p\n", cand_udp); /* XXX: not multi-component ready */
agent->discovery_list = g_slist_append (agent->discovery_list, cand_udp); cand->component = stream->component;
++agent->discovery_unsched_items; cand->agent = agent;
g_debug ("Adding new srv-rflx candidate %p\n", cand);
agent->discovery_list = g_slist_append (agent->discovery_list, cand);
++agent->discovery_unsched_items;
}
else {
/* note: memory allocation failure, return error */
stream->id = 0;
break;
}
} }
/* XXX-later: send STUN requests to obtain server-reflexive candidates */
} }
if (agent->discovery_list) /* step: attach the newly created sockets to the mainloop
nice_agent_schedule_discovery (agent); * context */
if (agent->main_context_set)
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)
discovery_schedule (agent);
return stream->id; return stream->id;
} }
...@@ -795,18 +608,21 @@ nice_agent_remove_stream ( ...@@ -795,18 +608,21 @@ nice_agent_remove_stream (
Stream *stream; Stream *stream;
stream = find_stream (agent, stream_id); stream = agent_find_stream (agent, stream_id);
if (!stream) if (!stream)
return; return;
/* remove stream */ /* note: remove items with matching stream_ids from both lists */
conn_check_prune_stream (agent, stream_id);
discovery_prune_stream (agent, stream_id);
/* remove the stream itself */
priv_deattach_stream (stream);
stream_free (stream); stream_free (stream);
agent->streams = g_slist_remove (agent->streams, stream); agent->streams = g_slist_remove (agent->streams, stream);
} }
/** /**
* nice_agent_add_local_address: * nice_agent_add_local_address:
* @agent: A NiceAgent * @agent: A NiceAgent
...@@ -830,48 +646,6 @@ nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr) ...@@ -830,48 +646,6 @@ nice_agent_add_local_address (NiceAgent *agent, NiceAddress *addr)
*/ */
} }
static gboolean nice_agent_conn_check_tick (gpointer pointer)
{
/* XXX: placeholder, implementation not done */
g_debug ("%s: stopping timer", G_STRFUNC);
return FALSE;
}
/**
* Schedules the next timer tick for connectivity checks.
*/
static void priv_schedule_conn_checks (NiceAgent *agent)
{
/* XXX: placeholder, implementation not done */
g_timeout_add (1, nice_agent_conn_check_tick, agent);
}
/**
* Forms new candidate pairs by matching the new remote candidate
* 'remote_cand' with all existing local candidates of 'component'.
* Implements the logic described in sect 5.7.1 of ICE -15 spec.
*/
static void priv_add_conn_checks (NiceAgent *agent, Component *component, NiceCandidate *remote_cand)
{
GSList *i;
for (i = component->local_candidates; i ; i = i->next) {
/* XXX: steps:
* - form a new candidate pair item (CandidatePair)
* - add it, filtered/pruned, to the check list
* - schedule the next timeout (priv_schedule_conn_checks())
*/
/* XXX: to keep the compiler happy until implementation is done */
NiceCandidate *cand = i->data;
cand = NULL;
}
priv_schedule_conn_checks (agent);
}
static void priv_add_remote_candidate ( static void priv_add_remote_candidate (
NiceAgent *agent, NiceAgent *agent,
guint stream_id, guint stream_id,
...@@ -888,7 +662,10 @@ static void priv_add_remote_candidate ( ...@@ -888,7 +662,10 @@ static void priv_add_remote_candidate (
Component *component; Component *component;
NiceCandidate *candidate; NiceCandidate *candidate;
if (!find_component (agent, stream_id, component_id, NULL, &component)) /* XXX: dear compiler, these are for you: */
(void)username; (void)password; (void)priority;
if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
return; return;
candidate = nice_candidate_new (type); candidate = nice_candidate_new (type);
...@@ -896,9 +673,6 @@ static void priv_add_remote_candidate ( ...@@ -896,9 +673,6 @@ static void priv_add_remote_candidate (
candidate->stream_id = stream_id; candidate->stream_id = stream_id;
candidate->component_id = component_id; candidate->component_id = component_id;
/* note: always zero, foundation used to identify remote cands */
candidate->id = 0;
candidate->type = type; candidate->type = type;
if (addr) if (addr)
candidate->addr = *addr; candidate->addr = *addr;
...@@ -907,19 +681,87 @@ static void priv_add_remote_candidate ( ...@@ -907,19 +681,87 @@ static void priv_add_remote_candidate (
candidate->transport = transport; candidate->transport = transport;
/* XXX: ugh, ugh, fixed size fields */
if (username) if (username)
strncpy (candidate->username, username, sizeof (candidate->username)); candidate->username = g_strdup (username);
if (password) if (password)
strncpy (candidate->password, password, sizeof (candidate->password)); candidate->password = g_strdup (password);
if (foundation) if (foundation)
candidate->foundation = g_strdup (foundation); candidate->foundation = g_strdup (foundation);
component->remote_candidates = g_slist_append (component->remote_candidates, component->remote_candidates = g_slist_append (component->remote_candidates,
candidate); candidate);
priv_add_conn_checks (agent, component, candidate); /* 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);
}
/**
* Sets the remote credentials for stream 'stream_id'.
*
* Note: stream credentials do not override per-candidate
* credentials if set
*
* @agent: a NiceAgent
* @stream_id: identifier returnedby nice_agent_add_stream()
* @ufrag: NULL-terminated string containing an ICE username fragment
* @pwd: NULL-terminated string containing an ICE password
*
* @return TRUE on success
*/
gboolean
nice_agent_set_remote_credentials (
NiceAgent *agent,
guint stream_id,
const gchar *ufrag, const gchar *pwd)
{
Stream *stream;
stream = agent_find_stream (agent, stream_id);
/* 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);
return TRUE;
}
return FALSE;
}
/**
* Gets the local credentials for stream 'stream_id'.
*
* @agent: a NiceAgent
* @stream_id: identifier returnedby nice_agent_add_stream()
* @ufrag: a pointer to a NULL-terminated string containing
* an ICE username fragment [OUT]
* @pwd: a pointer to a NULL-terminated string containing an ICE
* password [OUT]
*
* @return TRUE on success
*/
gboolean
nice_agent_get_local_credentials (
NiceAgent *agent,
guint stream_id,
const gchar **ufrag, const gchar **pwd)
{
Stream *stream = agent_find_stream (agent, stream_id);
if (stream == NULL)
return FALSE;
if (!ufrag || !pwd)
return FALSE;
*ufrag = stream->local_ufrag;
*pwd = stream->local_password;
return TRUE;
} }
/** /**
...@@ -930,8 +772,8 @@ static void priv_add_remote_candidate ( ...@@ -930,8 +772,8 @@ static void priv_add_remote_candidate (
* @type: the type of the new candidate * @type: the type of the new candidate
* @addr: the new candidate's IP address * @addr: the new candidate's IP address
* @port: the new candidate's port * @port: the new candidate's port
* @username: the new candidate's username * @username: the new candidate's username (XXX: candidates don't have usernames)
* @password: the new candidate's password * @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. * Add a candidate our peer has informed us about to the agent's list.
**/ **/
...@@ -958,7 +800,7 @@ nice_agent_add_remote_candidate ( ...@@ -958,7 +800,7 @@ nice_agent_add_remote_candidate (
NULL); NULL);
/* later: for each component, generate a new check with the new /* later: for each component, generate a new check with the new
candidate */ candidate, see below set_remote_candidates() */
} }
/** /**
...@@ -993,6 +835,8 @@ nice_agent_set_remote_candidates (NiceAgent *agent, guint stream_id, guint compo ...@@ -993,6 +835,8 @@ nice_agent_set_remote_candidates (NiceAgent *agent, guint stream_id, guint compo
NULL, NULL,
d->foundation); d->foundation);
} }
conn_check_schedule_next (agent);
} }
#if 0 #if 0
...@@ -1013,283 +857,21 @@ _local_candidate_lookup (NiceAgent *agent, guint candidate_id) ...@@ -1013,283 +857,21 @@ _local_candidate_lookup (NiceAgent *agent, guint candidate_id)
} }
#endif #endif
static NiceCandidate *
find_candidate_by_fd (Component *component, guint fd)
{
GSList *i;
for (i = component->local_candidates; i; i = i->next)
{
NiceCandidate *c = i->data;
if (c->sock.fileno == fd)
return c;
}
return NULL;
}
static void
_handle_stun_binding_request (
NiceAgent *agent,
Stream *stream,
Component *component,
NiceCandidate *local,
NiceAddress from,
StunMessage *msg)
{
GSList *i;
StunAttribute *attr;
gchar *username = NULL;
NiceCandidate *remote = NULL;
/* 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
*/
/* XXX-KV: update to use Remi's implementation */
attr = stun_message_find_attribute (msg, STUN_ATTRIBUTE_USERNAME);
if (attr == NULL)
/* no username attribute found */
goto ERROR;
username = attr->username;
/* validate username */
/* XXX: 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.
*/
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);
}
#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).
*/
/* XXX: test case where @from != @remote->addr. */
component->active_candidate = local;
component->peer_addr = from;
/* send STUN response */
{
StunMessage *response;
guint len;
gchar *packed;
/* XXX-KV: update to use Remi's implementation */
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->sock, &from, len, packed);
g_free (packed);
stun_message_free (response);
}
/* send reciprocal ("triggered") connectivity check */
/* XXX: possibly we shouldn't do this if we're being an ICE Lite agent */
{
StunMessage *extra;
gchar *username;
guint len;
gchar *packed;
/* XXX-KV: update to use Remi's implementation */
extra = stun_message_new (STUN_MESSAGE_BINDING_REQUEST,
NULL, 1);
username = g_strconcat (remote->username, local->username, NULL);
extra->attributes[0] = stun_attribute_username_new (username);
g_free (username);
nice_rng_generate_bytes (agent->rng, 16, extra->transaction_id);
len = stun_message_pack (extra, &packed);
nice_udp_socket_send (&local->sock, &from, len, packed);
g_free (packed);
stun_message_free (extra);
}
/* emit component-state-changed(connected) */
/* XXX: probably better do this when we get the binding response */
{
if (component->state != NICE_COMPONENT_STATE_CONNECTED)
{
component->state = NICE_COMPONENT_STATE_CONNECTED;
g_signal_emit (agent, signals[SIGNAL_COMPONENT_STATE_CHANGED], 0,
stream->id, component->id, component->state);
}
}
return;
ERROR:
#ifdef DEBUG
{
gchar ip[NICE_ADDRESS_STRING_LEN];
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);
}
#endif
/* XXX: add ERROR-CODE parameter */
{
StunMessage *response;
guint len;
gchar *packed;
/* XXX-KV: update to use Remi's implementation */
response = stun_message_new (STUN_MESSAGE_BINDING_ERROR_RESPONSE,
msg->transaction_id, 0);
len = stun_message_pack (response, &packed);
nice_udp_socket_send (&local->sock, &from, len, packed);
g_free (packed);
stun_message_free (response);
}
/* XXX: 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: if the peer is the controlling agent, it may include a USE-CANDIDATE
* attribute in the binding request
*/
}
static void
_handle_stun (
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: check it matches a request we sent */
break;
default:
/* a message type we don't know how to handle */
/* XXX: send error response */
break;
}
}
static guint static guint
_nice_agent_recv ( _nice_agent_recv (
NiceAgent *agent, NiceAgent *agent,
Stream *stream, Stream *stream,
Component *component, Component *component,
NiceCandidate *candidate, NiceUDPSocket *udp_socket,
guint buf_len, guint buf_len,
gchar *buf) gchar *buf)
{ {
NiceAddress from; NiceAddress from;
guint len; guint len;
len = nice_udp_socket_recv (&(candidate->sock), &from, g_debug ("Packet received on local socket %d.", udp_socket->fileno);
len = nice_udp_socket_recv (udp_socket, &from,
buf_len, buf); buf_len, buf);
if (len == 0) if (len == 0)
...@@ -1327,27 +909,47 @@ _nice_agent_recv ( ...@@ -1327,27 +909,47 @@ _nice_agent_recv (
* version number is 2. The top two bits of a STUN message are always 0. * 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) if ((buf[0] & 0xc0) == 0x80)
{ {
/* looks like RTP */ /* looks like RTP */
return len; return len;
} }
else if ((buf[0] & 0xc0) == 0) /* step: validate using the new STUN API */
/* - XXX: 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);
}
else
{
/* not RTP nor STUN, pass to client */
return len;
}
/* code using the old SUTN API */
#if 0
{ {
/* looks like a STUN message (connectivity check) */ /* looks like a STUN message (connectivity check) */
/* connectivity checks are described in ICE-13 §7. */ /* connectivity checks are described in ICE-13 §7. */
StunMessage *msg;
/* XXX-KV: update to use Remi's implementation */ /* 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); msg = stun_message_unpack (len, buf);
if (msg != NULL) if (msg != NULL)
{ {
_handle_stun (agent, stream, component, candidate, from, msg); conn_check_handle_inbound_stun_old (agent, stream, component, candidate, from, msg);
stun_message_free (msg); stun_message_free (msg);
} }
} }
#endif
/* anything else is ignored */ /* anything else is ignored */
return 0; return 0;
...@@ -1362,7 +964,7 @@ _nice_agent_recv ( ...@@ -1362,7 +964,7 @@ _nice_agent_recv (
* @buf_len: the size of @buf * @buf_len: the size of @buf
* @buf: the buffer to read data into * @buf: the buffer to read data into
* *
* Recieve data on a particular component. * Receive data on a particular component.
* *
* Returns: the amount of data read into @buf * Returns: the amount of data read into @buf
**/ **/
...@@ -1382,17 +984,17 @@ nice_agent_recv ( ...@@ -1382,17 +984,17 @@ nice_agent_recv (
Stream *stream; Stream *stream;
Component *component; Component *component;
if (!find_component (agent, stream_id, component_id, &stream, &component)) if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
return 0; return 0;
FD_ZERO (&fds); FD_ZERO (&fds);
for (i = component->local_candidates; i; i = i->next) for (i = component->sockets; i; i = i->next)
{ {
NiceCandidate *candidate = i->data; NiceUDPSocket *sockptr = i->data;
FD_SET (candidate->sock.fileno, &fds); FD_SET (sockptr->fileno, &fds);
max_fd = MAX (candidate->sock.fileno, max_fd); max_fd = MAX (sockptr->fileno, max_fd);
} }
/* Loop on candidate sockets until we find one that has non-STUN data /* Loop on candidate sockets until we find one that has non-STUN data
...@@ -1411,12 +1013,13 @@ nice_agent_recv ( ...@@ -1411,12 +1013,13 @@ nice_agent_recv (
for (j = 0; j <= max_fd; j++) for (j = 0; j <= max_fd; j++)
if (FD_ISSET (j, &fds)) if (FD_ISSET (j, &fds))
{ {
NiceCandidate *candidate; NiceUDPSocket *socket;
candidate = find_candidate_by_fd (component, j); socket = component_find_udp_socket_by_fd (component, j);
g_assert (candidate); g_assert (socket);
len = _nice_agent_recv (agent, stream, component, candidate,
buf_len, buf); len = _nice_agent_recv (agent, stream, component, socket,
buf_len, buf);
if (len > 0) if (len > 0)
return len; return len;
...@@ -1437,18 +1040,19 @@ nice_agent_recv_sock ( ...@@ -1437,18 +1040,19 @@ nice_agent_recv_sock (
guint buf_len, guint buf_len,
gchar *buf) gchar *buf)
{ {
NiceCandidate *candidate; NiceUDPSocket *socket;
Stream *stream; Stream *stream;
Component *component; Component *component;
if (!find_component (agent, stream_id, component_id, &stream, &component)) if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
return 0; return 0;
candidate = find_candidate_by_fd (component, sock); socket = component_find_udp_socket_by_fd (component, sock);
g_assert (candidate); g_assert (socket);
/* XXX: not multi-component ready */
return _nice_agent_recv (agent, stream, stream->component, return _nice_agent_recv (agent, stream, stream->component,
candidate, buf_len, buf); socket, buf_len, buf);
} }
...@@ -1484,14 +1088,15 @@ nice_agent_poll_read ( ...@@ -1484,14 +1088,15 @@ nice_agent_poll_read (
{ {
GSList *j; GSList *j;
Stream *stream = i->data; Stream *stream = i->data;
/* XXX: not multi-component ready */
Component *component = stream->component; Component *component = stream->component;
for (j = component->local_candidates; j; j = j->next) for (j = component->sockets; j; j = j->next)
{ {
NiceCandidate *candidate = j->data; NiceUDPSocket *sockptr = j->data;
FD_SET (candidate->sock.fileno, &fds); FD_SET (sockptr->fileno, &fds);
max_fd = MAX (candidate->sock.fileno, max_fd); max_fd = MAX (sockptr->fileno, max_fd);
} }
} }
...@@ -1517,9 +1122,9 @@ nice_agent_poll_read ( ...@@ -1517,9 +1122,9 @@ nice_agent_poll_read (
ret = g_slist_append (ret, GUINT_TO_POINTER (j)); ret = g_slist_append (ret, GUINT_TO_POINTER (j));
else else
{ {
NiceCandidate *candidate = NULL; NiceUDPSocket *socket;
Stream *stream; Stream *stream = NULL;
gchar buf[1024]; gchar buf[MAX_STUN_DATAGRAM_PAYLOAD];
guint len; guint len;
for (i = agent->streams; i; i = i->next) for (i = agent->streams; i; i = i->next)
...@@ -1527,26 +1132,24 @@ nice_agent_poll_read ( ...@@ -1527,26 +1132,24 @@ nice_agent_poll_read (
Stream *s = i->data; Stream *s = i->data;
Component *c = s->component; Component *c = s->component;
candidate = find_candidate_by_fd (c, j); socket = component_find_udp_socket_by_fd (c, j);
if (candidate != NULL) if (socket != NULL) {
stream = s;
break; break;
}
} }
if (candidate == NULL) if (socket == NULL || stream == NULL)
break;
stream = find_stream (agent, candidate->stream_id);
if (stream == NULL)
break; break;
/* XXX: not multi-component ready */
len = _nice_agent_recv (agent, stream, stream->component, len = _nice_agent_recv (agent, stream, stream->component,
candidate, 1024, buf); socket, MAX_STUN_DATAGRAM_PAYLOAD, buf);
if (len && func != NULL) if (len && func != NULL)
func (agent, stream->id, candidate->component_id, len, buf, func (agent, stream->id, stream->component->id, len, buf,
data); data);
} }
} }
...@@ -1565,21 +1168,26 @@ nice_agent_send ( ...@@ -1565,21 +1168,26 @@ nice_agent_send (
Stream *stream; Stream *stream;
Component *component; Component *component;
stream = find_stream (agent, stream_id); /* XXX: dear compiler, these are for you: */
(void)component_id;
stream = agent_find_stream (agent, stream_id);
/* XXX: not multi-component ready */
component = stream->component; component = stream->component;
if (component->active_candidate != NULL) if (component->selected_pair.local != NULL)
{ {
NiceUDPSocket *sock; NiceUDPSocket *sock;
NiceAddress *addr; NiceAddress *addr;
#if 0 #if 1
g_debug ("s%d:%d: sending %d bytes to %08x:%d", stream_id, component_id, g_debug ("s%d:%d: sending %d bytes to %08x:%d", stream_id, component_id,
len, component->peer_addr->addr_ipv4, component->peer_addr->port); len, component->selected_pair.remote->addr.addr_ipv4, component->selected_pair.remote->addr.port);
#endif #endif
sock = &component->active_candidate->sock; sock = component->selected_pair.local->sockptr;
addr = &component->peer_addr; addr = &component->selected_pair.remote->addr;
nice_udp_socket_send (sock, addr, len, buf); nice_udp_socket_send (sock, addr, len, buf);
} }
} }
...@@ -1590,7 +1198,8 @@ nice_agent_send ( ...@@ -1590,7 +1198,8 @@ nice_agent_send (
* @agent: A NiceAgent * @agent: A NiceAgent
* *
* The caller owns the returned GSList but not the candidates contained within * The caller owns the returned GSList but not the candidates contained within
* it. * 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 belonging to @agent
**/ **/
...@@ -1602,7 +1211,7 @@ nice_agent_get_local_candidates ( ...@@ -1602,7 +1211,7 @@ nice_agent_get_local_candidates (
{ {
Component *component; Component *component;
if (!find_component (agent, stream_id, component_id, NULL, &component)) if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
return NULL; return NULL;
return g_slist_copy (component->local_candidates); return g_slist_copy (component->local_candidates);
...@@ -1626,7 +1235,7 @@ nice_agent_get_remote_candidates ( ...@@ -1626,7 +1235,7 @@ nice_agent_get_remote_candidates (
{ {
Component *component; Component *component;
if (!find_component (agent, stream_id, component_id, NULL, &component)) if (!agent_find_component (agent, stream_id, component_id, NULL, &component))
return NULL; return NULL;
/* XXX: should we expose NiceCandidate to the client, or should /* XXX: should we expose NiceCandidate to the client, or should
...@@ -1642,11 +1251,13 @@ nice_agent_dispose (GObject *object) ...@@ -1642,11 +1251,13 @@ nice_agent_dispose (GObject *object)
GSList *i; GSList *i;
NiceAgent *agent = NICE_AGENT (object); NiceAgent *agent = NICE_AGENT (object);
if (agent->discovery_list) { /* step: free resources for the binding discovery timers */
g_slist_foreach (agent->discovery_list, nice_agent_free_discovery_candidate_udp, NULL); discovery_free (agent);
g_slist_free (agent->discovery_list), g_assert (agent->discovery_list == NULL);
agent->discovery_list = NULL;
} /* step: free resources for the connectivity check timers */
conn_check_free (agent);
g_assert (agent->conncheck_list == NULL);
for (i = agent->local_addresses; i; i = i->next) for (i = agent->local_addresses; i; i = i->next)
{ {
...@@ -1670,6 +1281,8 @@ nice_agent_dispose (GObject *object) ...@@ -1670,6 +1281,8 @@ nice_agent_dispose (GObject *object)
g_free (agent->stun_server_ip); g_free (agent->stun_server_ip);
agent->stun_server_ip = NULL; agent->stun_server_ip = NULL;
g_free (agent->turn_server_ip);
agent->turn_server_ip = NULL;
nice_rng_free (agent->rng); nice_rng_free (agent->rng);
agent->rng = NULL; agent->rng = NULL;
...@@ -1686,7 +1299,7 @@ struct _IOCtx ...@@ -1686,7 +1299,7 @@ struct _IOCtx
NiceAgent *agent; NiceAgent *agent;
Stream *stream; Stream *stream;
Component *component; Component *component;
NiceCandidate *candidate; NiceUDPSocket *socket;
}; };
...@@ -1695,15 +1308,17 @@ io_ctx_new ( ...@@ -1695,15 +1308,17 @@ io_ctx_new (
NiceAgent *agent, NiceAgent *agent,
Stream *stream, Stream *stream,
Component *component, Component *component,
NiceCandidate *candidate) NiceUDPSocket *socket)
{ {
IOCtx *ctx; IOCtx *ctx;
ctx = g_slice_new0 (IOCtx); ctx = g_slice_new0 (IOCtx);
ctx->agent = agent; if (ctx) {
ctx->stream = stream; ctx->agent = agent;
ctx->component = component; ctx->stream = stream;
ctx->candidate = candidate; ctx->component = component;
ctx->socket = socket;
}
return ctx; return ctx;
} }
...@@ -1714,7 +1329,6 @@ io_ctx_free (IOCtx *ctx) ...@@ -1714,7 +1329,6 @@ io_ctx_free (IOCtx *ctx)
g_slice_free (IOCtx, ctx); g_slice_free (IOCtx, ctx);
} }
static gboolean static gboolean
nice_agent_g_source_cb ( nice_agent_g_source_cb (
GIOChannel *source, GIOChannel *source,
...@@ -1728,20 +1342,78 @@ nice_agent_g_source_cb ( ...@@ -1728,20 +1342,78 @@ nice_agent_g_source_cb (
NiceAgent *agent = ctx->agent; NiceAgent *agent = ctx->agent;
Stream *stream = ctx->stream; Stream *stream = ctx->stream;
Component *component = ctx->component; Component *component = ctx->component;
NiceCandidate *candidate = ctx->candidate; gchar buf[MAX_STUN_DATAGRAM_PAYLOAD];
gchar buf[1024];
guint len; guint len;
len = _nice_agent_recv (agent, stream, component, candidate, 1024, /* XXX: dear compiler, these are for you: */
buf); (void)source;
len = _nice_agent_recv (agent, stream, component, ctx->socket,
MAX_STUN_DATAGRAM_PAYLOAD, buf);
if (len > 0) if (len > 0)
agent->read_func (agent, candidate->stream_id, candidate->component_id, agent->read_func (agent, stream->id, component->id,
len, buf, agent->read_func_data); len, buf, agent->read_func_data);
return TRUE; return TRUE;
} }
/**
* Attaches socket handles of 'stream' to the main eventloop
* context.
*
* @pre agent->main_context_set == TRUE
*/
static gboolean priv_attach_new_stream (NiceAgent *agent, Stream *stream)
{
GSList *j;
/* XXX: not multi-component ready */
Component *component = stream->component;
for (j = component->sockets; j; j = j->next) {
NiceUDPSocket *udp_socket = j->data;
GIOChannel *io;
GSource *source;
IOCtx *ctx;
io = g_io_channel_unix_new (udp_socket->fileno);
source = g_io_create_watch (io, G_IO_IN);
ctx = io_ctx_new (agent, stream, component, udp_socket);
g_source_set_callback (source, (GSourceFunc) nice_agent_g_source_cb,
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) {
g_source_destroy (source);
return FALSE;
}
}
return TRUE;
}
/**
* Detaches socket handles of 'stream' from the main eventloop
* context.
*
* @pre agent->main_context_set == TRUE
*/
static void priv_deattach_stream (Stream *stream)
{
GSList *j;
/* XXX: not multi-component ready */
Component *component = stream->component;
for (j = component->gsources; j; j = j->next) {
GSource *source = j->data;
g_debug ("Detach source %p (stream %u).", source, stream->id);
g_source_destroy (source);
}
g_slist_free (component->gsources),
component->gsources = NULL;
}
gboolean gboolean
nice_agent_main_context_attach ( nice_agent_main_context_attach (
...@@ -1755,34 +1427,22 @@ nice_agent_main_context_attach ( ...@@ -1755,34 +1427,22 @@ nice_agent_main_context_attach (
if (agent->main_context_set) if (agent->main_context_set)
return FALSE; return FALSE;
/* XXX: when sockets are not yet created, or new streams are added,
* the mainloop integration won't then work anymore! */
/* attach candidates */ /* attach candidates */
for (i = agent->streams; i; i = i->next) for (i = agent->streams; i; i = i->next) {
{ Stream *stream = i->data;
GSList *j; gboolean res = priv_attach_new_stream (agent, stream);
Stream *stream = i->data; if (!res)
Component *component = stream->component; return FALSE;
}
for (j = component->local_candidates; j; j = j->next)
{
NiceCandidate *candidate = j->data;
GIOChannel *io;
GSource *source;
IOCtx *ctx;
io = g_io_channel_unix_new (candidate->sock.fileno);
source = g_io_create_watch (io, G_IO_IN);
ctx = io_ctx_new (agent, stream, component, candidate);
g_source_set_callback (source, (GSourceFunc) nice_agent_g_source_cb,
ctx, (GDestroyNotify) io_ctx_free);
g_source_attach (source, NULL);
candidate->source = source;
}
}
agent->main_context = ctx; agent->main_context = ctx;
agent->main_context_set = TRUE; agent->main_context_set = TRUE;
agent->read_func = func; agent->read_func = func;
agent->read_func_data = data; agent->read_func_data = data;
return TRUE; return TRUE;
} }
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -77,8 +78,9 @@ typedef enum ...@@ -77,8 +78,9 @@ typedef enum
NICE_COMPONENT_STATE_CONNECTED, /* at least one working candidate pair */ NICE_COMPONENT_STATE_CONNECTED, /* at least one working candidate pair */
NICE_COMPONENT_STATE_READY, /* ICE concluded, candidate pair NICE_COMPONENT_STATE_READY, /* ICE concluded, candidate pair
selection is now final */ selection is now final */
NICE_COMPONENT_STATE_FAILED /* connectivity checks have been completed, NICE_COMPONENT_STATE_FAILED, /* connectivity checks have been completed,
but connectivity was not established */ but connectivity was not established */
NICE_COMPONENT_STATE_LAST
} NiceComponentState; } NiceComponentState;
typedef enum typedef enum
...@@ -105,29 +107,6 @@ typedef void (*NiceAgentRecvFunc) ( ...@@ -105,29 +107,6 @@ typedef void (*NiceAgentRecvFunc) (
NiceAgent *agent, guint stream_id, guint component_id, guint len, NiceAgent *agent, guint stream_id, guint component_id, guint len,
gchar *buf, gpointer user_data); gchar *buf, gpointer user_data);
struct _NiceAgent
{
GObject parent;
guint next_candidate_id;
guint next_stream_id;
gboolean full_mode;
NiceUDPSocketFactory *socket_factory;
GSList *local_addresses;
GSList *streams;
gboolean main_context_set;
GMainContext *main_context;
NiceAgentRecvFunc read_func;
gpointer read_func_data;
GSList *discovery_list;
guint discovery_unsched_items;
GSList *conncheck_list;
gchar *stun_server_ip;
guint stun_server_port;
gchar *turn_server_ip;
guint turn_server_port;
GTimeVal next_check_tv;
NiceRNG *rng;
};
typedef struct _NiceAgentClass NiceAgentClass; typedef struct _NiceAgentClass NiceAgentClass;
...@@ -154,6 +133,18 @@ nice_agent_remove_stream ( ...@@ -154,6 +133,18 @@ nice_agent_remove_stream (
NiceAgent *agent, NiceAgent *agent,
guint stream_id); guint stream_id);
gboolean
nice_agent_set_remote_credentials (
NiceAgent *agent,
guint stream_id,
const gchar *ufrag, const gchar *pwd);
gboolean
nice_agent_get_local_credentials (
NiceAgent *agent,
guint stream_id,
const gchar **ufrag, const gchar **pwd);
void void
nice_agent_add_remote_candidate ( nice_agent_add_remote_candidate (
NiceAgent *agent, NiceAgent *agent,
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -36,7 +37,7 @@ ...@@ -36,7 +37,7 @@
*/ */
#include "agent.h" #include "agent.h"
#include "component.h"
/* (ICE-13 §4.1.1) Every candidate is a transport address. It also has a type and /* (ICE-13 §4.1.1) Every candidate is a transport address. It also has a type and
* a base. Three types are defined and gathered by this specification - host * a base. Three types are defined and gathered by this specification - host
...@@ -59,12 +60,15 @@ nice_candidate_free (NiceCandidate *candidate) ...@@ -59,12 +60,15 @@ nice_candidate_free (NiceCandidate *candidate)
{ {
/* better way of checking if socket is allocated? */ /* better way of checking if socket is allocated? */
if (candidate->sock.addr.addr_ipv4 != 0)
nice_udp_socket_close (&(candidate->sock));
if (candidate->source) if (candidate->source)
g_source_destroy (candidate->source); g_source_destroy (candidate->source);
if (candidate->username)
g_free (candidate->username);
if (candidate->password)
g_free (candidate->password);
if (candidate->foundation) if (candidate->foundation)
g_free (candidate->foundation); g_free (candidate->foundation);
...@@ -88,10 +92,10 @@ nice_candidate_jingle_priority (NiceCandidate *candidate) ...@@ -88,10 +92,10 @@ nice_candidate_jingle_priority (NiceCandidate *candidate)
} }
/* ICE-13 §4.1.2; returns number between 1 and 0x7effffff */ /* ICE-15 §4.1.2.1; returns number between 1 and 0x7effffff */
G_GNUC_CONST G_GNUC_CONST
static guint32 guint32
_candidate_ice_priority ( nice_candidate_ice_priority_full (
// must be ∈ (0, 126) (max 2^7 - 2) // must be ∈ (0, 126) (max 2^7 - 2)
guint type_preference, guint type_preference,
// must be ∈ (0, 65535) (max 2^16 - 1) // must be ∈ (0, 65535) (max 2^16 - 1)
...@@ -114,12 +118,28 @@ nice_candidate_ice_priority (const NiceCandidate *candidate) ...@@ -114,12 +118,28 @@ nice_candidate_ice_priority (const NiceCandidate *candidate)
switch (candidate->type) switch (candidate->type)
{ {
case NICE_CANDIDATE_TYPE_HOST: type_preference = 120; break; case NICE_CANDIDATE_TYPE_HOST:
case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE: type_preference = 110; break; type_preference = NICE_CANDIDATE_TYPE_PREF_HOST; break;
case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: type_preference = 100; break; case NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
case NICE_CANDIDATE_TYPE_RELAYED: type_preference = 60; break; type_preference = NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE; break;
case NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
type_preference = NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE; break;
case NICE_CANDIDATE_TYPE_RELAYED:
type_preference = NICE_CANDIDATE_TYPE_PREF_RELAYED; break;
} }
return _candidate_ice_priority (type_preference, 1, candidate->component_id); /* return _candidate_ice_priority (type_preference, 1, candidate->component_id); */
return nice_candidate_ice_priority_full (type_preference, 1, candidate->component_id);
} }
/**
* Calculates the pair priority as specified in ICE -15 spec 5.7.2.
*/
guint64
nice_candidate_pair_priority (guint32 o_prio, guint32 a_prio)
{
guint32 max = o_prio > a_prio ? o_prio : a_prio;
guint32 min = o_prio < a_prio ? o_prio : a_prio;
return ((guint64)1 << 32) * min + 2 * max + (o_prio > a_prio ? 1 : 0);
}
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -42,6 +43,11 @@ ...@@ -42,6 +43,11 @@
G_BEGIN_DECLS G_BEGIN_DECLS
#define NICE_CANDIDATE_TYPE_PREF_HOST 120
#define NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE 110
#define NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE 100
#define NICE_CANDIDATE_TYPE_PREF_RELAYED 60
typedef enum typedef enum
{ {
NICE_CANDIDATE_TYPE_HOST, NICE_CANDIDATE_TYPE_HOST,
...@@ -61,17 +67,15 @@ struct _NiceCandidate ...@@ -61,17 +67,15 @@ struct _NiceCandidate
{ {
NiceCandidateType type; NiceCandidateType type;
NiceCandidateTransport transport; NiceCandidateTransport transport;
guint id;
NiceAddress addr; NiceAddress addr;
NiceAddress base_addr; NiceAddress base_addr;
guint32 priority; guint32 priority;
guint stream_id; guint stream_id;
guint component_id; guint component_id;
// guint generation; gchar *foundation;
gchar *foundation; /* note: if NULL, derive foundation from 'id' */ NiceUDPSocket *sockptr; /* XXX: to replace 'sock', see comment above */
NiceUDPSocket sock; gchar *username; /* pointer to a NULL-terminated username string */
gchar username[128]; /* XXX: why 128 chars? */ gchar *password; /* pointer to a NULL-terminated password string */
gchar password[128]; /* XXX: why 128 chars? */
GSource *source; GSource *source;
}; };
...@@ -85,9 +89,15 @@ nice_candidate_free (NiceCandidate *candidate); ...@@ -85,9 +89,15 @@ nice_candidate_free (NiceCandidate *candidate);
gfloat gfloat
nice_candidate_jingle_priority (NiceCandidate *candidate); nice_candidate_jingle_priority (NiceCandidate *candidate);
guint32
nice_candidate_ice_priority_full (guint type_pref, guint local_pref, guint component_id);
guint32 guint32
nice_candidate_ice_priority (const NiceCandidate *candidate); nice_candidate_ice_priority (const NiceCandidate *candidate);
guint64
nice_candidate_pair_priority (guint32 o_prio, guint32 a_prio);
G_END_DECLS G_END_DECLS
#endif /* _CANDIDATE_H */ #endif /* _CANDIDATE_H */
......
...@@ -55,22 +55,50 @@ component_free (Component *cmp) ...@@ -55,22 +55,50 @@ component_free (Component *cmp)
{ {
GSList *i; GSList *i;
for (i = cmp->local_candidates; i; i = i->next) for (i = cmp->local_candidates; i; i = i->next) {
{ NiceCandidate *candidate = i->data;
NiceCandidate *candidate = i->data; nice_candidate_free (candidate);
}
nice_candidate_free (candidate); for (i = cmp->remote_candidates; i; i = i->next) {
} NiceCandidate *candidate = i->data;
nice_candidate_free (candidate);
}
for (i = cmp->remote_candidates; i; i = i->next) for (i = cmp->sockets; i; i = i->next) {
{ NiceUDPSocket *udpsocket = i->data;
NiceCandidate *candidate = i->data; nice_udp_socket_close (udpsocket);
}
nice_candidate_free (candidate); for (i = cmp->gsources; i; i = i->next) {
} GSource *source = i->data;
g_source_destroy (source);
}
g_slist_free (cmp->gsources),
cmp->gsources = NULL;
g_slist_free (cmp->local_candidates); g_slist_free (cmp->local_candidates);
g_slist_free (cmp->remote_candidates); g_slist_free (cmp->remote_candidates);
g_slist_free (cmp->sockets);
g_slice_free (Component, cmp); g_slice_free (Component, cmp);
} }
NiceUDPSocket *
component_find_udp_socket_by_fd (Component *component, guint fd)
{
GSList *i;
/* XXX: this won't work anymore, a single fd may be used
* by multiple candidates */
for (i = component->sockets; i; i = i->next)
{
NiceUDPSocket *sockptr = i->data;
if (sockptr->fileno == fd)
return sockptr;
}
return NULL;
}
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -59,19 +60,35 @@ typedef enum ...@@ -59,19 +60,35 @@ typedef enum
typedef struct _Component Component; typedef struct _Component Component;
typedef struct _CandidatePair CandidatePair;
struct _CandidatePair
{
NiceCandidate *local;
NiceCandidate *remote;
guint64 priority; /**< candidate pair priority */
};
struct _Component struct _Component
{ {
ComponentType type; ComponentType type;
/* the local candidate that last received a valid connectivity check */
NiceCandidate *active_candidate;
/* the remote address that the last connectivity check came from */
NiceAddress peer_addr;
guint id; guint id;
NiceComponentState state; NiceComponentState state;
GSList *local_candidates; GSList *local_candidates; /**< list of Candidate objs */
GSList *remote_candidates; GSList *remote_candidates; /**< list of Candidate objs */
GSList *sockets; /**< list of NiceUDPSocket objs */
GSList *gsources; /**< list of GSource objs */
CandidatePair selected_pair; /**< independent from checklists,
see ICE 11.1.1 (ID-15) */
/* XXX: **to be removed**
* --cut--
GSList *checks; GSList *checks;
* the local candidate that last received a valid connectivity */
NiceCandidate *active_candidate;
/* the remote address that the last connectivity check came from */
NiceAddress peer_addr;
/* --cut-- */
}; };
Component * Component *
...@@ -82,6 +99,9 @@ component_new ( ...@@ -82,6 +99,9 @@ component_new (
void void
component_free (Component *cmp); component_free (Component *cmp);
NiceUDPSocket *
component_find_udp_socket_by_fd (Component *component, guint fd);
G_END_DECLS G_END_DECLS
#endif /* _NICE_COMPONENT_H */ #endif /* _NICE_COMPONENT_H */
......
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2006, 2007 Collabora Ltd.
* Contact: Dafydd Harries
* (C) 2006, 2007 Nokia Corporation. All rights reserved.
* Contact: Kai Vehmanen
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Kai Vehmanen, Nokia
* Dafydd Harries, Collabora Ltd.
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#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"
static void priv_update_check_list_state (NiceAgent *agent, Stream *stream);
/**
* Finds the next connectivity check in WAITING state.
*/
static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check_list)
{
GSList *i;
/* XXX: should return the highest priority check on the
* waiting list! */
for (i = conn_check_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->state == NICE_CHECK_WAITING)
return p;
}
return NULL;
}
/**
* Initiates a new connectivity check for a ICE candidate pair.
*
* @return TRUE on success, FALSE on error
*/
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 */
pair->state = NICE_CHECK_IN_PROGRESS;
conn_check_send (agent, pair);
return TRUE;
}
/**
* Unfreezes the next connectivity check in the list. Follows the
* algorithm defined in 5.7.4 of the ICE spec (-15).
*
* @return TRUE on success, and FALSE if no frozen candidates were found.
*/
static gboolean priv_conn_check_unfreeze_next (GSList *conncheck_list)
{
CandidateCheckPair *pair = NULL;
guint64 max_priority = 0;
GSList *i;
int c;
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;
if (p->state == NICE_CHECK_FROZEN &&
p->priority > max_priority) {
max_priority = p->priority;
pair = p;
}
}
}
if (pair) {
g_debug ("Pair %p (%s) unfrozen.", pair, pair->foundation);
pair->state = NICE_CHECK_WAITING;
return TRUE;
}
return FALSE;
}
/**
* 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_check_tick (gpointer pointer)
{
CandidateCheckPair *pair = NULL;
NiceAgent *agent = pointer;
GSList *i;
gboolean keep_timer_going = FALSE;
pair = priv_conn_check_find_next_waiting (agent->conncheck_list);
#ifdef DEBUG
{
static int tick_counter = 0;
if (++tick_counter % 20 == 0)
g_debug ("conncheck tick #%d with list %p (1)", tick_counter, pair);
}
#endif
if (!pair) {
gboolean c = priv_conn_check_unfreeze_next (agent->conncheck_list);
if (c == TRUE) {
pair = priv_conn_check_find_next_waiting (agent->conncheck_list);
}
}
if (pair) {
priv_conn_check_initiate (agent, pair);
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) {
CandidateCheckPair *p = i->data;
if (p->state == NICE_CHECK_IN_PROGRESS) {
/* note: macro from sys/time.h but compatible with GTimeVal */
if (p->stun_ctx == NULL) {
g_debug ("STUN connectivity check was cancelled, marking as done.");
p->state = NICE_CHECK_FAILED;
}
else if (timercmp(&p->next_tick, &now, <=)) {
int res = stun_bind_elapse (p->stun_ctx);
if (res == EAGAIN) {
/* case: not ready complete, so schedule next timeout */
unsigned int timeout = stun_bind_timeout (p->stun_ctx);
/* note: convert from milli to microseconds for g_time_val_add() */
g_get_current_time (&p->next_tick);
g_time_val_add (&p->next_tick, timeout * 10);
keep_timer_going = TRUE;
}
else {
/* case: error, abort processing */
g_debug ("Retransmissions failed, giving up on connectivity check %p", p);
p->state = NICE_CHECK_FAILED;
}
}
}
if (p->state == NICE_CHECK_FROZEN)
++frozen;
if (p->state == NICE_CHECK_IN_PROGRESS)
++inprogress;
else if (p->state == NICE_CHECK_WAITING)
++waiting;
}
#ifdef DEBUG
{
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);
}
#endif
/* note: keep the timer going as long as there is work to be done */
if (frozen || inprogress || waiting)
keep_timer_going = TRUE;
}
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);
}
return keep_timer_going;
}
/**
* Initiates the next pending connectivity check.
*/
void conn_check_schedule_next (NiceAgent *agent)
{
gboolean c = priv_conn_check_unfreeze_next (agent->conncheck_list);
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);
}
}
/**
* Forms new candidate pairs by matching the new remote candidate
* 'remote_cand' with all existing local candidates of 'component'.
* Implements the logic described in sect 5.7.1 of ICE -15 spec.
*
* @param agent context
* @param component pointer to the component
* @param remote remote candidate to match with
*
* @return non-zero on error
*/
int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote)
{
GSList *i;
int res = 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;
}
pair->priority = nice_candidate_pair_priority (local->priority, remote->priority);
pair->state = NICE_CHECK_FROZEN;
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;
g_debug ("added a new conncheck item with foundation of '%s'.", pair->foundation);
}
else {
res = -1;
break;
}
}
return res;
}
/**
* Frees the CandidateCheckPair structure pointer to
* by 'user data'. Compatible with g_slist_foreach().
*/
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;
g_slice_free (CandidateCheckPair, pair);
}
/**
* Frees all resources of agent's connectiviy checks.
*/
void conn_check_free (NiceAgent *agent)
{
if (agent->conncheck_list) {
g_slist_foreach (agent->conncheck_list, conn_check_free_item, NULL);
g_slist_free (agent->conncheck_list),
agent->conncheck_list = NULL;
if (agent->conncheck_timer_id) {
g_source_remove (agent->conncheck_timer_id),
agent->conncheck_timer_id = 0;
}
agent->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
}
}
/**
* Prunes the list of connectivity checks for items related
* to stream 'stream_id'.
*
* @return TRUE on success, FALSE on a fatal error
*/
gboolean conn_check_prune_stream (NiceAgent *agent, guint stream_id)
{
CandidateCheckPair *pair;
GSList *i;
g_debug ("pruning stream %u conn checks.", stream_id);
for (i = agent->conncheck_list; i ; ) {
pair = i->data;
if (pair->stream_id == stream_id) {
GSList *next = i->next;
g_debug ("conncheck, pruning item %p.", i);
agent->conncheck_list =
g_slist_remove (agent->conncheck_list, pair);
conn_check_free_item (pair, NULL);
i = next;
if (!agent->conncheck_list)
break;
}
else
i = i->next;
}
if (!agent->conncheck_list)
agent->conncheck_state = NICE_CHECKLIST_NOT_STARTED;
/* return FALSE if there was a memory allocation failure */
if (agent->conncheck_list == NULL && i != NULL)
return FALSE;
return TRUE;
}
/**
* Returns a username string for use in an outbound connectivity
* check. The caller is responsible for freeing the returned
* string.
*/
static gchar *priv_create_check_username (NiceAgent *agent, CandidateCheckPair *pair)
{
Stream *stream;
if (pair &&
pair->remote && pair->remote->username &&
pair->local && pair->local->username)
return g_strconcat (pair->remote->username, ":", pair->local->username, NULL);
stream = agent_find_stream (agent, pair->stream_id);
if (stream)
return g_strconcat (stream->remote_ufrag, ":", stream->local_ufrag, NULL);
return NULL;
}
/**
* Returns a password string for use in an outbound connectivity
* check.
*/
static const gchar *priv_create_check_password (NiceAgent *agent, CandidateCheckPair *pair)
{
Stream *stream;
if (pair &&
pair->remote && pair->remote->password)
return pair->remote->password;
stream = agent_find_stream (agent, pair->stream_id);
if (stream)
return stream->remote_password;
return NULL;
}
/**
* Sends a connectivity check over candidate pair 'pair'.
*/
int conn_check_send (NiceAgent *agent, CandidateCheckPair *pair)
{
/* note: following information is supplied:
* - username (for USERNAME attribute)
* - password (for MESSAGE-INTEGRITY)
* - priority (for PRIORITY)
* - ICE-CONTROLLED/ICE-CONTROLLING (for role conflicts)
* - USE-CANDIDATE (if sent by the controlling agent)
*/
guint32 priority =
nice_candidate_ice_priority_full (
NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE,
1,
pair->local->component_id);
/* XXX: memory allocs: */
gchar *username = priv_create_check_username (agent, pair);
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.",
ntohs(((struct sockaddr_in*)(&sockaddr))->sin_port),
pair->local->sockptr->fileno,
(unsigned long long)tie,
username, password, priority);
if (cand_use)
pair->nominated = TRUE;
stun_conncheck_start (&pair->stun_ctx, pair->local->sockptr->fileno,
&sockaddr, sizeof (sockaddr),
username, password,
cand_use, controlling, priority,
tie);
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);
return 0;
}
/**
* Updates the check list state.
*
* Implements parts of the algorithm described in ICE ID-15 8.2
* that apply to the whole check list.
*/
static void priv_update_check_list_state (NiceAgent *agent, Stream *stream)
{
GSList *i;
guint c, completed = 0;
/* note: iterate the conncheck list for each component separately */
for (c = 0; c < stream->n_components; c++) {
guint not_failed = 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)) {
if (p->state != NICE_CHECK_FAILED)
++not_failed;
if (p->state == NICE_CHECK_SUCCEEDED &&
p->nominated == TRUE)
break;
}
}
/* note: all checks have failed */
if (!not_failed)
agent_signal_component_state_change (agent,
stream->id,
(c + 1), /* component-id */
NICE_COMPONENT_STATE_FAILED);
/* note: no pair was ready&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)");
}
}
/**
* Updated the check list state for a stream component.
*
* Implements the algorithm described in ICE ID-15 8.2 as
* it applies to checks of a certain component.
*/
static void priv_update_check_list_state_for_component (NiceAgent *agent, Stream *stream, Component *component)
{
GSList *i;
unsigned int succeeded = 0, nominated = 0;
g_assert (component);
/* step: search for at least one nominated pair */
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) {
++succeeded;
if (p->nominated == TRUE) {
++nominated;
agent_signal_component_state_change (agent,
p->stream_id,
p->component_id,
NICE_COMPONENT_STATE_READY);
}
}
}
}
g_debug ("conn.check list status: %u nominated, %u succeeded, c-id %u.", nominated, succeeded, component->id);
if (nominated) {
/* step: cancel all FROZEN and WAITING pairs for the component */
for (i = agent->conncheck_list; i; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->state == NICE_CHECK_FROZEN ||
p->state == NICE_CHECK_WAITING)
p->state = NICE_CHECK_CANCELLED;
/* note: a SHOULD level req. in ICE ID-15: */
if (p->state == NICE_CHECK_IN_PROGRESS) {
if (p->stun_ctx)
stun_bind_cancel (p->stun_ctx),
p->stun_ctx = NULL;
p->state = NICE_CHECK_CANCELLED;
}
}
}
priv_update_check_list_state (agent, stream);
}
/**
* Changes the selected pair for the component if 'pair' is nominated
* and has higher priority than the currently selected pair. See
* ICE sect 11.1.1 (ID-15).
*/
static gboolean priv_update_selected_pair (NiceAgent *agent, Component *component, CandidateCheckPair *pair)
{
g_assert (component);
g_assert (pair);
if (pair->priority > component->selected_pair.priority) {
g_debug ("changing SELECTED PAIR for component %u: %s:%s (prio:%lu).",
component->id, pair->local->foundation, pair->remote->foundation, (long unsigned)pair->priority);
component->selected_pair.local = pair->local;
component->selected_pair.remote = pair->remote;
component->selected_pair.priority = pair->priority;
agent_signal_new_selected_pair (agent, pair->stream_id, component->id, pair->local->foundation, pair->remote->foundation);
}
return TRUE;
}
/**
* The remote party has signalled that the candidate pair
* described by 'component' and 'remotecand' is nominated
* for use.
*/
static void priv_mark_pair_nominated (NiceAgent *agent, Component *component, NiceCandidate *remotecand)
{
GSList *i;
g_assert (component);
/* 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
* 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)
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)
{
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;
}
/* 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);
if (use_candidate)
priv_mark_pair_nominated (agent, component, cand);
/* XXX: perform a triggered check */
break;
}
}
/* XXX: add support for discovering peer-reflexive candidates */
if (i == NULL)
g_debug ("No matching remote candidate for incoming STUN conncheck.");
}
else if (res == EINVAL) {
/* case 2: not a valid, new request -- continue processing */
GSList *i;
int res;
socklen_t socklen = sizeof (sockaddr);
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);
if (res == 0) {
/* case: succesful connectivity check */
g_debug ("conncheck %p SUCCEED.", p);
p->state = NICE_CHECK_SUCCEEDED;
p->stun_ctx = NULL;
/* note: CONNECTED but not yet READY, see docs */
agent_signal_component_state_change (agent,
stream->id,
component->id,
NICE_COMPONENT_STATE_CONNECTED);
priv_update_check_list_state_for_component (agent, stream, component);
if (p->nominated == TRUE) {
priv_update_selected_pair (agent, component, p);
}
trans_found = TRUE;
}
else if (res != EAGAIN) {
/* case: STUN error, the check STUN context was freed */
g_debug ("conncheck %p FAILED.", p);
p->stun_ctx = NULL;
trans_found = TRUE;
}
else
/* case: invalid/incomplete message */
g_assert (res == EAGAIN);
}
}
/* step: let's try to match the response to an existing discovery */
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);
g_debug ("stun_bind_process/disc for %p res %d.", d, res);
if (res == 0) {
/* case: succesful binding discovery, create a new local candidate */
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.port = ntohs(mapped->sin_port);
discovery_add_server_reflexive_candidate (
d->agent,
d->stream->id,
d->component->id,
&niceaddr,
d->nicesock);
d->stun_ctx = NULL;
d->done = TRUE;
trans_found = TRUE;
}
else if (res != EAGAIN) {
/* case: STUN error, the check STUN context was freed */
d->stun_ctx = NULL;
trans_found = TRUE;
}
}
}
else {
g_debug ("Invalid STUN connectivity check request. Ignoring... %s", strerror(errno));
}
return TRUE;
}
/* -----------------------------------------------------------------
* Code using the old STUN API
* ----------------------------------------------------------------- */
static void
_handle_stun_binding_request (
NiceAgent *agent,
Stream *stream,
Component *component,
NiceCandidate *local,
NiceAddress from,
StunMessage *msg)
{
GSList *i;
StunAttribute *attr;
gchar *username = NULL;
NiceCandidate *remote = NULL;
/* 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
*/
attr = stun_message_find_attribute (msg, STUN_ATTRIBUTE_USERNAME);
if (attr == NULL)
/* no username attribute found */
goto ERROR;
username = attr->username;
/* 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.
*/
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);
}
#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).
*/
/* XXX-old_stun_code: test case where @from != @remote->addr. */
component->active_candidate = local;
component->peer_addr = from;
/* send STUN response */
{
StunMessage *response;
guint len;
gchar *packed;
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);
g_free (packed);
stun_message_free (response);
}
/* 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);
username = g_strconcat (remote->username, local->username, NULL);
extra->attributes[0] = stun_attribute_username_new (username);
g_free (username);
nice_rng_generate_bytes (agent->rng, 16, extra->transaction_id);
len = stun_message_pack (extra, &packed);
nice_udp_socket_send (local->sockptr, &from, len, packed);
g_free (packed);
stun_message_free (extra);
}
/* 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;
ERROR:
#ifdef DEBUG
{
gchar ip[NICE_ADDRESS_STRING_LEN];
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);
}
#endif
/* XXX-old_stun_code: add ERROR-CODE parameter */
{
StunMessage *response;
guint len;
gchar *packed;
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_free (packed);
stun_message_free (response);
}
/* 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
*/
}
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;
}
}
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2006, 2007 Collabora Ltd.
* Contact: Dafydd Harries
* (C) 2006, 2007 Nokia Corporation. All rights reserved.
* Contact: Kai Vehmanen
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifndef _NICE_CONNCHECK_H
#define _NICE_CONNCHECK_H
/* note: this is a private header to libnice */
#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 */
typedef enum
{
NICE_CHECK_WAITING = 1, /**< waiting to be scheduled */
NICE_CHECK_IN_PROGRESS, /**< conn. checks started */
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 */
} NiceCheckState;
typedef enum
{
NICE_CHECKLIST_NOT_STARTED = 1,
NICE_CHECKLIST_RUNNING,
NICE_CHECKLIST_COMPLETED,
NICE_CHECKLIST_FAILED
} NiceCheckListState;
typedef struct _CandidateCheckPair CandidateCheckPair;
struct _CandidateCheckPair
{
NiceAgent *agent; /* back pointer to owner */
guint stream_id;
guint component_id;
NiceCandidate *local;
NiceCandidate *remote;
gchar *foundation;
NiceCheckState state;
gboolean nominated;
guint64 priority;
GTimeVal next_tick; /* next tick timestamp */
stun_bind_t *stun_ctx;
};
int conn_check_add_for_candidate (NiceAgent *agent, guint stream_id, Component *component, NiceCandidate *remote);
void conn_check_free_item (gpointer data, gpointer user_data);
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);
#endif /*_NICE_CONNCHECK_H */
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2007 Nokia Corporation. All rights reserved.
* Contact: Kai Vehmanen
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Kai Vehmanen, Nokia
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#include <string.h>
#include <errno.h>
#ifndef _BSD_SOURCE
#error "timercmp() macros needed"
#endif
#include <sys/time.h> /* timercmp() macro, BSD */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <glib.h>
#include "agent.h"
#include "agent-priv.h"
#include "agent-signals-marshal.h"
#include "component.h"
#include "discovery.h"
/**
* Frees the CandidateDiscovery structure pointed to
* by 'user data'. Compatible with g_slist_foreach().
*/
void discovery_free_item (gpointer data, gpointer user_data)
{
CandidateDiscovery *cand = data;
g_assert (user_data == NULL);
cand->server_addr = NULL;
g_slice_free (CandidateDiscovery, cand);
}
/**
* Frees all discovery related resources for the agent.
*/
void discovery_free (NiceAgent *agent)
{
if (agent->discovery_list) {
g_slist_foreach (agent->discovery_list, discovery_free_item, NULL);
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;
}
}
/**
* Prunes the list of discovery processes for items related
* to stream 'stream_id'.
*
* @return TRUE on success, FALSE on a fatal error
*/
gboolean discovery_prune_stream (NiceAgent *agent, guint stream_id)
{
CandidateDiscovery *cand;
GSList *i;
g_debug ("pruning stream %u discovery items.", stream_id);
for (i = agent->discovery_list; i ; ) {
cand = i->data;
if (cand->stream->id == stream_id) {
GSList *next = i->next;
g_debug ("discovery, pruning item %p.", i);
agent->discovery_list =
g_slist_remove (agent->discovery_list, cand);
discovery_free_item (cand, NULL);
i = next;
if (!agent->discovery_list)
break;
}
else
i = i->next;
}
/* return FALSE if there was a memory allocation failure */
if (agent->conncheck_list == NULL && i != NULL)
return FALSE;
return TRUE;
}
/**
* Creates a local host candidate for 'component_id' of stream
* 'stream_id'.
*
* @return pointer to the created candidate, or NULL on error
*/
NiceCandidate *discovery_add_local_host_candidate (
NiceAgent *agent,
guint stream_id,
guint component_id,
NiceAddress *address)
{
NiceCandidate *candidate;
Component *component;
Stream *stream;
NiceUDPSocket *udp_socket = NULL;
if (!agent_find_component (agent, stream_id, component_id, &stream, &component))
return NULL;
candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
if (candidate) {
NiceUDPSocket *udp_socket = g_slice_new0 (NiceUDPSocket);
if (udp_socket) {
candidate->foundation = g_strdup_printf ("%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 */
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) {
/* success: store a pointer to the sockaddr */
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 */
}
}
else { /* error: memory alloc / list */
nice_candidate_free (candidate), candidate = NULL;
}
}
else { /* error: socket factory make */
nice_candidate_free (candidate), candidate = NULL;
}
}
else /* error: udp socket memory allocation */
nice_candidate_free (candidate), candidate = NULL;
}
if (!candidate) {
/* clean up after errors */
if (udp_socket)
g_slice_free (NiceUDPSocket, udp_socket);
}
return candidate;
}
/**
* Creates a server reflexive candidate for 'component_id' of stream
* 'stream_id'.
*
* @return pointer to the created candidate, or NULL on error
*/
NiceCandidate*
discovery_add_server_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_SERVER_REFLEXIVE);
if (candidate) {
candidate->foundation = g_strdup_printf ("%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;
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 */
g_assert (candidate->username == NULL);
g_assert (candidate->password == NULL);
}
else /* error: memory allocation - 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
* existing discovery processes.
*
* This function is designed for the g_timeout_add() interface.
*
* @return will return FALSE when no more pending timers.
*/
static gboolean priv_discovery_tick (gpointer pointer)
{
CandidateDiscovery *cand;
NiceAgent *agent = pointer;
GSList *i;
int not_done = 0; /* note: track whether to continue timer */
#ifdef DEBUG
{
static int tick_counter = 0;
if (++tick_counter % 20 == 0)
g_debug ("discovery tick #%d with list %p (1)", tick_counter, agent->discovery_list);
}
#endif
for (i = agent->discovery_list; i ; i = i->next) {
cand = i->data;
if (cand->pending != TRUE) {
cand->pending = TRUE;
if (agent->discovery_unsched_items)
--agent->discovery_unsched_items;
g_debug ("discovery - scheduling cand type %u addr %s and socket %d.\n", cand->type, cand->server_addr, cand->socket);
if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE &&
cand->server_addr) {
struct sockaddr_in stun_server;
int res;
memset (&stun_server, 0, sizeof(stun_server));
stun_server.sin_addr.s_addr = inet_addr(cand->server_addr);
stun_server.sin_port = htons(IPPORT_STUN);
res = stun_bind_start (&cand->stun_ctx, cand->socket,
(struct sockaddr*)&stun_server, sizeof(stun_server));
if (res == 0) {
/* 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 */
cand->done = TRUE;
cand->stun_ctx = NULL;
continue;
}
}
else
/* allocate relayed candidates */
g_assert_not_reached ();
++not_done; /* note: new discovery scheduled */
}
if (cand->done != TRUE) {
GTimeVal now;
g_get_current_time (&now);
if (cand->stun_ctx == NULL) {
g_debug ("STUN discovery was cancelled, marking discovery done.");
cand->done = TRUE;
}
/* note: macro from sys/time.h but compatible with GTimeVal */
else if (timercmp(&cand->next_tick, &now, <=)) {
int res = stun_bind_elapse (cand->stun_ctx);
if (res == EAGAIN) {
/* case: not ready complete, so schedule next timeout */
unsigned int timeout = stun_bind_timeout (cand->stun_ctx);
/* note: convert from milli to microseconds for g_time_val_add() */
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 {
/* case: error, abort processing */
cand->done = TRUE;
cand->stun_ctx = NULL;
g_debug ("Error with stun_bind_elapse(), aborting discovery item.");
}
}
else
++not_done; /* note: discovery not expired yet */
}
}
if (not_done == 0) {
g_debug ("Candidate gathering FINISHED, stopping discovery timer.");
agent_signal_gathering_done (agent);
discovery_free (agent);
/* note: no pending timers, return FALSE to stop timer */
return FALSE;
}
return TRUE;
}
/**
* Initiates the active candidate discovery process.
*
* @pre agent->discovery_list != NULL // unsched discovery items available
*/
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 */
agent->discovery_timer_id =
g_timeout_add (next, priv_discovery_tick, agent);
}
}
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2007 Nokia Corporation. All rights reserved.
* Contact: Kai Vehmanen
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Kai Vehmanen, Nokia
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifndef _NICE_DISCOVERY_H
#define _NICE_DISCOVERY_H
/* note: this is a private header to libnice */
#include "stun/bind.h"
#include "agent.h"
typedef struct _CandidateDiscovery CandidateDiscovery;
struct _CandidateDiscovery
{
NiceAgent *agent; /**< back pointer to owner */
NiceCandidateType type; /**< candidate type STUN or TURN */
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 */
NiceAddress *interface; /**< Address of local interface */
stun_bind_t *stun_ctx;
GTimeVal next_tick; /**< next tick timestamp */
gboolean pending; /**< is discovery in progress? */
gboolean done; /**< is discovery complete? */
Stream *stream;
Component *component;
};
void discovery_free_item (gpointer data, gpointer user_data);
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 (
NiceAgent *agent,
guint stream_id,
guint component_id,
NiceAddress *address);
NiceCandidate*
discovery_add_server_reflexive_candidate (
NiceAgent *agent,
guint stream_id,
guint component_id,
NiceAddress *address,
NiceUDPSocket *base_socket);
#endif /*_NICE_CONNCHECK_H */
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
* file under either the MPL or the LGPL. * file under either the MPL or the LGPL.
*/ */
#include <string.h>
#include "stream.h" #include "stream.h"
Stream * Stream *
...@@ -44,6 +46,9 @@ stream_new (void) ...@@ -44,6 +46,9 @@ stream_new (void)
stream = g_slice_new0 (Stream); stream = g_slice_new0 (Stream);
stream->component = component_new (COMPONENT_TYPE_RTP); stream->component = component_new (COMPONENT_TYPE_RTP);
stream->n_components = 1;
stream->initial_binding_request_received = FALSE;
return stream; return stream;
} }
......
...@@ -44,13 +44,24 @@ ...@@ -44,13 +44,24 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/* Following do not including the terminating NULL */
#define NICE_STREAM_MAX_UFRAG_LEN 4
#define NICE_STREAM_MAX_PWD_LEN 22
typedef struct _Stream Stream; typedef struct _Stream Stream;
struct _Stream struct _Stream
{ {
guint id; guint id;
guint n_components;
gboolean initial_binding_request_received;
/* XXX: streams can have multiple components */ /* XXX: streams can have multiple components */
Component *component; 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];
}; };
Stream * Stream *
......
...@@ -36,15 +36,18 @@ ...@@ -36,15 +36,18 @@
*/ */
#include "agent.h" #include "agent.h"
#include "agent-priv.h"
#include "udp-fake.h" #include "udp-fake.h"
#include <string.h>
int int
main (void) main (void)
{ {
NiceAgent *agent; NiceAgent *agent;
NiceAddress addr = {0,}; NiceAddress addr;
NiceUDPSocketFactory factory; NiceUDPSocketFactory factory;
memset (&addr, 0, sizeof (addr));
g_type_init (); g_type_init ();
nice_udp_fake_socket_factory_init (&factory); nice_udp_fake_socket_factory_init (&factory);
......
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2007 Nokia Corporation. All rights reserved.
* Contact: Kai Vehmanen
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Kai Vehmanen, Nokia
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#include <stdlib.h>
#include <string.h>
#include "agent.h"
#include "udp-bsd.h"
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_ibr_received = FALSE;
static gboolean global_ragent_ibr_received = FALSE;
static int global_lagent_cands = 0;
static int global_ragent_cands = 0;
static gboolean timer_cb (gpointer pointer)
{
g_debug ("%s: %p", G_STRFUNC, pointer);
/* signal status via a global variable */
g_debug ("\tgathering_done=%d", global_candidate_gathering_done);
g_debug ("\tlstate=%d", global_lagent_state);
g_debug ("\trstate=%d", global_ragent_state);
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...");
exit (-1);
}
static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data)
{
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;
}
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;
else if ((int)data == 2)
global_ragent_state = NICE_COMPONENT_STATE_GATHERING;
/* XXX: dear compiler, these are for you: */
(void)agent;
}
static void cb_component_state_changed(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
{
g_debug ("%s: %p", __func__, data);
g_assert (test_component_id == component_id);
if ((int)data == 1)
global_lagent_state = state;
else if ((int)data == 2)
global_ragent_state = state;
/* XXX: dear compiler, these are for you: */
(void)agent; (void)stream_id; (void)data;
}
static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id,
gchar *lfoundation, gchar* rfoundation, gpointer data)
{
g_debug ("%s: %p", __func__, data);
g_assert (test_component_id == component_id);
if ((int)data == 1)
++global_lagent_cands;
else if ((int)data == 2)
++global_ragent_cands;
/* XXX: dear compiler, these are for you: */
(void)agent; (void)stream_id;
}
static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id,
gchar *foundation, gpointer data)
{
g_debug ("%s: %p", __func__, data);
g_assert (test_component_id == component_id);
/* XXX: dear compiler, these are for you: */
(void)agent; (void)stream_id; (void)data;
}
static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data)
{
g_debug ("%s: %p", __func__, data);
if ((int)data == 1)
global_lagent_ibr_received = TRUE;
else if ((int)data == 2)
global_ragent_ibr_received = TRUE;
/* XXX: dear compiler, these are for you: */
(void)agent; (void)stream_id; (void)data;
}
static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr)
{
NiceAddress laddr, raddr;
NiceCandidateDesc cdes = { /* candidate description (no ports) */
"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;
/* 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) */
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);
/* 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 STUN binding requests were sent */
g_assert (global_lagent_ibr_received == TRUE);
g_assert (global_ragent_ibr_received == TRUE);
/* 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 */
nice_agent_remove_stream (lagent, ls_id);
nice_agent_remove_stream (ragent, rs_id);
return 0;
}
int main (void)
{
NiceAgent *lagent, *ragent; /* agent's L and R */
NiceUDPSocketFactory udpfactory;
NiceAddress baseaddr;
int result;
guint timer_id;
g_type_init ();
global_mainloop = g_main_loop_new (NULL, FALSE);
/* Note: impl limits ...
* - no multi-stream support
* - no IPv6 support
*/
nice_udp_bsd_socket_factory_init (&udpfactory);
/* step: create the agents L and R */
lagent = nice_agent_new (&udpfactory);
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);
/* step: add a timer to catch state changes triggered by signals */
timer_id = g_timeout_add (2000, timer_cb, NULL);
/* step: specify which local interface to use */
if (!nice_address_set_ipv4_from_string (&baseaddr, "127.0.0.1"))
g_assert_not_reached ();
nice_agent_add_local_address (lagent, &baseaddr);
nice_agent_add_local_address (ragent, &baseaddr);
g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done",
G_CALLBACK (cb_candidate_gathering_done), (gpointer)1);
g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done",
G_CALLBACK (cb_candidate_gathering_done), (gpointer)2);
g_signal_connect (G_OBJECT (lagent), "component-state-changed",
G_CALLBACK (cb_component_state_changed), (gpointer)1);
g_signal_connect (G_OBJECT (ragent), "component-state-changed",
G_CALLBACK (cb_component_state_changed), (gpointer)2);
g_signal_connect (G_OBJECT (lagent), "new-selected-pair",
G_CALLBACK (cb_new_selected_pair), (gpointer)1);
g_signal_connect (G_OBJECT (ragent), "new-selected-pair",
G_CALLBACK (cb_new_selected_pair), (gpointer)2);
g_signal_connect (G_OBJECT (lagent), "new-candidate",
G_CALLBACK (cb_new_candidate), (gpointer)1);
g_signal_connect (G_OBJECT (ragent), "new-candidate",
G_CALLBACK (cb_new_candidate), (gpointer)2);
g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received",
G_CALLBACK (cb_initial_binding_request_received), (gpointer)1);
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);
/* step: check results of first run */
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 */
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;
g_debug ("test-fullmode: running test the 2nd time");
result = run_full_test (lagent, ragent, &baseaddr);
g_assert (global_lagent_state == NICE_COMPONENT_STATE_READY);
g_assert (global_ragent_state == NICE_COMPONENT_STATE_READY);
g_object_unref (lagent);
g_object_unref (ragent);
nice_udp_socket_factory_close (&udpfactory);
g_main_loop_unref (global_mainloop),
global_mainloop = NULL;
g_source_remove (timer_id);
return result;
}
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -63,9 +64,10 @@ int ...@@ -63,9 +64,10 @@ int
main (void) main (void)
{ {
NiceAgent *agent; NiceAgent *agent;
NiceAddress addr = {0,}; NiceAddress addr;
NiceUDPSocketFactory factory; NiceUDPSocketFactory factory;
memset (&addr, 0, sizeof (addr));
g_type_init (); g_type_init ();
nice_udp_fake_socket_factory_init (&factory); nice_udp_fake_socket_factory_init (&factory);
...@@ -83,7 +85,7 @@ main (void) ...@@ -83,7 +85,7 @@ main (void)
candidates = nice_agent_get_local_candidates (agent, 1, 1); candidates = nice_agent_get_local_candidates (agent, 1, 1);
candidate = candidates->data; candidate = candidates->data;
sock = &candidate->sock; sock = candidate->sockptr;
g_slist_free (candidates); g_slist_free (candidates);
nice_udp_fake_socket_push_recv (sock, &addr, 6, "\x80hello"); nice_udp_fake_socket_push_recv (sock, &addr, 6, "\x80hello");
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -66,13 +67,14 @@ int ...@@ -66,13 +67,14 @@ int
main (void) main (void)
{ {
NiceAgent *agent; NiceAgent *agent;
NiceAddress addr = {0,}; NiceAddress addr;
NiceUDPSocketFactory factory; NiceUDPSocketFactory factory;
NiceUDPSocket *sock; NiceUDPSocket *sock;
gint pipe_fds[2]; gint pipe_fds[2];
GSList *fds = NULL; GSList *fds = NULL;
GSList *readable; GSList *readable;
memset (&addr, 0, sizeof (addr));
g_type_init (); g_type_init ();
/* set up agent */ /* set up agent */
...@@ -91,7 +93,7 @@ main (void) ...@@ -91,7 +93,7 @@ main (void)
candidates = nice_agent_get_local_candidates (agent, 1, 1); candidates = nice_agent_get_local_candidates (agent, 1, 1);
candidate = candidates->data; candidate = candidates->data;
sock = &candidate->sock; sock = candidate->sockptr;
g_slist_free (candidates); g_slist_free (candidates);
} }
......
...@@ -42,11 +42,22 @@ main (void) ...@@ -42,11 +42,22 @@ main (void)
{ {
NiceCandidate *candidate; NiceCandidate *candidate;
/* test 1 */
candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST); candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_HOST);
g_assert (nice_candidate_ice_priority (candidate) == 0x78000200); g_assert (nice_candidate_ice_priority (candidate) == 0x78000200);
g_assert (nice_candidate_jingle_priority (candidate) == 1.0); g_assert (nice_candidate_jingle_priority (candidate) == 1.0);
nice_candidate_free (candidate); nice_candidate_free (candidate);
/* test 2 */
/* 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);
/* 2^32*1 + 2*5000 + 1 = 4294977297 */
g_assert (nice_candidate_pair_priority (5000, 1) == 4294977297);
return 0; return 0;
} }
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -44,9 +45,10 @@ int ...@@ -44,9 +45,10 @@ int
main (void) main (void)
{ {
NiceAgent *agent; NiceAgent *agent;
NiceAddress addr = {0,}; NiceAddress addr;
NiceUDPSocketFactory factory; NiceUDPSocketFactory factory;
memset (&addr, 0, sizeof (addr));
g_type_init (); g_type_init ();
nice_udp_fake_socket_factory_init (&factory); nice_udp_fake_socket_factory_init (&factory);
...@@ -69,7 +71,7 @@ main (void) ...@@ -69,7 +71,7 @@ main (void)
candidates = nice_agent_get_local_candidates (agent, 1, 1); candidates = nice_agent_get_local_candidates (agent, 1, 1);
candidate = candidates->data; candidate = candidates->data;
g_slist_free (candidates); g_slist_free (candidates);
sock = &(candidate->sock); sock = candidate->sockptr;
nice_udp_fake_socket_push_recv (sock, &addr, 7, "\x80lalala"); nice_udp_fake_socket_push_recv (sock, &addr, 7, "\x80lalala");
len = nice_agent_recv (agent, candidate->stream_id, len = nice_agent_recv (agent, candidate->stream_id,
candidate->component_id, 1024, buf); candidate->component_id, 1024, buf);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -66,7 +67,7 @@ static gboolean ...@@ -66,7 +67,7 @@ static gboolean
fd_is_readable (guint fd) fd_is_readable (guint fd)
{ {
fd_set fds; fd_set fds;
struct timeval timeout = {0,}; struct timeval timeout = { 0, 0 };
FD_ZERO (&fds); FD_ZERO (&fds);
FD_SET (fd, &fds); FD_SET (fd, &fds);
...@@ -101,7 +102,7 @@ send_connectivity_check ( ...@@ -101,7 +102,7 @@ send_connectivity_check (
candidates = nice_agent_get_local_candidates (agent, 1, 1); candidates = nice_agent_get_local_candidates (agent, 1, 1);
g_assert (g_slist_length (candidates) > 0); g_assert (g_slist_length (candidates) > 0);
local = candidates->data; local = candidates->data;
g_assert (local->id == 1); g_assert (strncmp (local->foundation, "1", 1) == 0);
g_slist_free (candidates); g_slist_free (candidates);
} }
...@@ -114,7 +115,7 @@ send_connectivity_check ( ...@@ -114,7 +115,7 @@ send_connectivity_check (
g_slist_free (candidates); g_slist_free (candidates);
} }
sock = &local->sock; sock = local->sockptr;
username = g_strconcat (local->username, remote->username, NULL); username = g_strconcat (local->username, remote->username, NULL);
...@@ -135,11 +136,12 @@ send_connectivity_check ( ...@@ -135,11 +136,12 @@ send_connectivity_check (
{ {
StunMessage *msg; StunMessage *msg;
NiceAddress addr = {0,}; NiceAddress addr;
gchar packed[1024]; gchar packed[1024];
gchar *dump; gchar *dump;
guint len; guint len;
memset (&addr, 0, sizeof (addr));
len = nice_udp_fake_socket_pop_send (sock, &addr, 1024, packed); len = nice_udp_fake_socket_pop_send (sock, &addr, 1024, packed);
g_assert (nice_address_equal (&addr, remote_addr)); g_assert (nice_address_equal (&addr, remote_addr));
msg = stun_message_unpack (len, packed); msg = stun_message_unpack (len, packed);
...@@ -154,11 +156,12 @@ send_connectivity_check ( ...@@ -154,11 +156,12 @@ send_connectivity_check (
{ {
StunMessage *msg; StunMessage *msg;
NiceAddress addr = {0,}; NiceAddress addr;
gchar packed[1024]; gchar packed[1024];
gchar *dump; gchar *dump;
guint len; guint len;
memset (&addr, 0, sizeof (addr));
len = nice_udp_fake_socket_pop_send (sock, &addr, 1024, packed); len = nice_udp_fake_socket_pop_send (sock, &addr, 1024, packed);
g_assert (nice_address_equal (&addr, remote_addr)); g_assert (nice_address_equal (&addr, remote_addr));
msg = stun_message_unpack (len, packed); msg = stun_message_unpack (len, packed);
...@@ -179,9 +182,11 @@ main (void) ...@@ -179,9 +182,11 @@ main (void)
{ {
NiceUDPSocketFactory factory; NiceUDPSocketFactory factory;
NiceAgent *agent; NiceAgent *agent;
NiceAddress local_addr = {0,}; NiceAddress local_addr;
NiceAddress remote_addr = {0,}; NiceAddress remote_addr;
memset (&local_addr, 0, sizeof (local_addr));
memset (&remote_addr, 0, sizeof (remote_addr));
g_type_init (); g_type_init ();
/* set up */ /* set up */
...@@ -221,7 +226,7 @@ main (void) ...@@ -221,7 +226,7 @@ main (void)
candidates = nice_agent_get_local_candidates (agent, 1, 1); candidates = nice_agent_get_local_candidates (agent, 1, 1);
candidate = candidates->data; candidate = candidates->data;
sock = &candidate->sock; sock = candidate->sockptr;
g_slist_free (candidates); g_slist_free (candidates);
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* *
* Contributors: * Contributors:
* Dafydd Harries, Collabora Ltd. * Dafydd Harries, Collabora Ltd.
* Kai Vehmanen, Nokia
* *
* Alternatively, the contents of this file may be used under the terms of the * Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
...@@ -47,12 +48,13 @@ test_stun_no_password ( ...@@ -47,12 +48,13 @@ test_stun_no_password (
NiceAddress from, NiceAddress from,
NiceUDPSocket *sock) NiceUDPSocket *sock)
{ {
NiceAddress to = {0,}; NiceAddress to;
guint len; guint len;
gchar buf[1024]; gchar buf[1024];
guint packed_len; guint packed_len;
gchar *packed; gchar *packed;
memset (&to, 0, sizeof (to));
memset (buf, '\0', 1024); memset (buf, '\0', 1024);
{ {
...@@ -97,12 +99,13 @@ test_stun_invalid_password ( ...@@ -97,12 +99,13 @@ test_stun_invalid_password (
NiceAddress from, NiceAddress from,
NiceUDPSocket *sock) NiceUDPSocket *sock)
{ {
NiceAddress to = {0,}; NiceAddress to;
guint len; guint len;
gchar buf[1024]; gchar buf[1024];
guint packed_len; guint packed_len;
gchar *packed; gchar *packed;
memset (&to, 0, sizeof (to));
memset (buf, '\0', 1024); memset (buf, '\0', 1024);
{ {
...@@ -149,13 +152,14 @@ test_stun_valid_password ( ...@@ -149,13 +152,14 @@ test_stun_valid_password (
NiceCandidate *candidate, NiceCandidate *candidate,
NiceUDPSocket *sock) NiceUDPSocket *sock)
{ {
NiceAddress to = {0,}; NiceAddress to;
guint len; guint len;
guint packed_len; guint packed_len;
gchar buf[1024]; gchar buf[1024];
gchar *packed; gchar *packed;
gchar *username; gchar *username;
memset (&to, 0, sizeof (to));
memset (buf, '\0', 1024); memset (buf, '\0', 1024);
username = g_strconcat (candidate->username, "username", NULL); username = g_strconcat (candidate->username, "username", NULL);
...@@ -207,13 +211,15 @@ int ...@@ -207,13 +211,15 @@ int
main (void) main (void)
{ {
NiceAgent *agent; NiceAgent *agent;
NiceAddress local_addr = {0,}; NiceAddress local_addr;
NiceAddress remote_addr = {0,}; NiceAddress remote_addr;
NiceCandidate *candidate; NiceCandidate *candidate;
NiceUDPSocketFactory factory; NiceUDPSocketFactory factory;
NiceUDPSocket *sock; NiceUDPSocket *sock;
GSList *candidates; GSList *candidates;
memset (&local_addr, 0, sizeof (local_addr));
memset (&remote_addr, 0, sizeof (remote_addr));
g_type_init (); g_type_init ();
nice_udp_fake_socket_factory_init (&factory); nice_udp_fake_socket_factory_init (&factory);
...@@ -231,7 +237,7 @@ main (void) ...@@ -231,7 +237,7 @@ main (void)
candidates = nice_agent_get_local_candidates (agent, 1, 1); candidates = nice_agent_get_local_candidates (agent, 1, 1);
candidate = candidates->data; candidate = candidates->data;
sock = &(candidate->sock); sock = candidate->sockptr;
g_slist_free (candidates); g_slist_free (candidates);
/* run tests */ /* run tests */
......
...@@ -39,16 +39,19 @@ ...@@ -39,16 +39,19 @@
#include "udp-fake.h" #include "udp-fake.h"
#include "agent.h" #include "agent.h"
#include "agent-priv.h"
gint gint
main (void) main (void)
{ {
NiceAgent *agent; NiceAgent *agent;
NiceAddress addr_local = {0,}, addr_remote = {0,}; NiceAddress addr_local, addr_remote;
NiceCandidate *candidate; NiceCandidate *candidate;
NiceUDPSocketFactory factory; NiceUDPSocketFactory factory;
GSList *candidates; GSList *candidates;
memset (&addr_local, 0, sizeof (addr_local));
memset (&addr_remote, 0, sizeof (addr_remote));
g_type_init (); g_type_init ();
nice_udp_fake_socket_factory_init (&factory); nice_udp_fake_socket_factory_init (&factory);
...@@ -78,7 +81,7 @@ main (void) ...@@ -78,7 +81,7 @@ main (void)
/* fake socket manager uses incremental port numbers starting at 1 */ /* fake socket manager uses incremental port numbers starting at 1 */
addr_local.port = 1; addr_local.port = 1;
g_assert (nice_address_equal (&(candidate->addr), &addr_local)); g_assert (nice_address_equal (&(candidate->addr), &addr_local));
g_assert (candidate->id == 1); g_assert (strncmp (candidate->foundation, "1", 1) == 0);
g_slist_free (candidates); g_slist_free (candidates);
/* add remote candidate */ /* add remote candidate */
......
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