tests/test-dribble
tests/test-fallback
tests/test-fullmode
+tests/test-icetcp
tests/test-io-stream-cancelling
tests/test-io-stream-closing-read
tests/test-io-stream-closing-write
GQueue pending_signals;
guint16 rfc4571_expecting_length;
+ gboolean use_ice_udp;
+ gboolean use_ice_tcp;
/* XXX: add pointer to internal data struct for ABI-safe extensions */
};
PROP_PROXY_PASSWORD,
PROP_UPNP,
PROP_UPNP_TIMEOUT,
- PROP_RELIABLE
+ PROP_RELIABLE,
+ PROP_ICE_UDP,
+ PROP_ICE_TCP,
};
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (gobject_class, PROP_ICE_UDP,
+ g_param_spec_boolean (
+ "ice-udp",
+ "Use ICE-UDP",
+ "Use ICE-UDP specification to generate UDP candidates",
+ TRUE, /* use ice-udp by default */
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class, PROP_ICE_TCP,
+ g_param_spec_boolean (
+ "ice-tcp",
+ "Use ICE-TCP",
+ "Use ICE-TCP specification to generate TCP candidates",
+ TRUE, /* use ice-tcp by default */
+ G_PARAM_READWRITE));
/* install signals */
/**
agent->compatibility = NICE_COMPATIBILITY_RFC5245;
agent->reliable = FALSE;
+ agent->use_ice_udp = TRUE;
+ agent->use_ice_tcp = TRUE;
stun_agent_init (&agent->stun_agent, STUN_ALL_KNOWN_ATTRIBUTES,
STUN_COMPATIBILITY_RFC5389,
g_value_set_boolean (value, agent->reliable);
break;
+ case PROP_ICE_UDP:
+ g_value_set_boolean (value, agent->use_ice_udp);
+ break;
+
+ case PROP_ICE_TCP:
+ g_value_set_boolean (value, agent->use_ice_tcp);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
agent->reliable = g_value_get_boolean (value);
break;
+ /* Don't allow ice-udp and ice-tcp to be disabled at the same time */
+ case PROP_ICE_UDP:
+ if (agent->use_ice_tcp == TRUE || g_value_get_boolean (value) == TRUE)
+ agent->use_ice_udp = g_value_get_boolean (value);
+ break;
+
+ case PROP_ICE_TCP:
+ if (agent->use_ice_udp == TRUE || g_value_get_boolean (value) == TRUE)
+ agent->use_ice_tcp = g_value_get_boolean (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
cdisco->type = NICE_CANDIDATE_TYPE_RELAYED;
if (turn->type == NICE_RELAY_TYPE_TURN_UDP) {
+ if (agent->use_ice_udp == FALSE) {
+ g_slice_free (CandidateDiscovery, cdisco);
+ return;
+ }
if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
NiceAddress addr = nicesock->addr;
NiceSocket *new_socket;
agent->compatibility == NICE_COMPATIBILITY_OC2007R2)
reliable_tcp = TRUE;
+ /* Ignore tcp candidates if we disabled ice-tcp */
+ if ((agent->use_ice_udp == FALSE && reliable_tcp == FALSE) ||
+ (agent->use_ice_tcp == FALSE && reliable_tcp == TRUE)) {
+ g_slice_free (CandidateDiscovery, cdisco);
+ return;
+ }
nicesock = NULL;
+
+ /* TODO: add support for turn-tcp RFC 6062 */
if (agent->proxy_type != NICE_PROXY_TYPE_NONE &&
agent->proxy_ip != NULL &&
nice_address_set_from_string (&proxy_server, agent->proxy_ip)) {
for (cid = 1; cid <= stream->n_components; cid++) {
Component *component = stream_find_component_by_id (stream, cid);
- guint current_port;
- guint start_port;
+ enum {
+ ADD_HOST_MIN = 0,
+ ADD_HOST_UDP = ADD_HOST_MIN,
+ ADD_HOST_TCP_ACTIVE,
+ ADD_HOST_TCP_PASSIVE,
+ ADD_HOST_MAX = ADD_HOST_TCP_PASSIVE
+ } add_type;
if (component == NULL)
continue;
- start_port = component->min_port;
- if(component->min_port != 0) {
- start_port = nice_rng_generate_int(agent->rng, component->min_port, component->max_port+1);
- }
- current_port = start_port;
-
- host_candidate = NULL;
- while (host_candidate == NULL) {
- nice_debug ("Agent %p: Trying to create host candidate on port %d", agent, current_port);
- nice_address_set_port (addr, current_port);
- host_candidate = discovery_add_local_host_candidate (agent, stream->id,
- cid, addr, NICE_CANDIDATE_TRANSPORT_UDP);
- if (current_port > 0)
- current_port++;
- if (current_port > component->max_port) current_port = component->min_port;
- if (current_port == 0 || current_port == start_port)
- break;
- }
- nice_address_set_port (addr, 0);
-
- if (!host_candidate) {
- if (nice_debug_is_enabled ()) {
- gchar ip[NICE_ADDRESS_STRING_LEN];
- nice_address_to_string (addr, ip);
- nice_debug ("Agent %p: Unable to add local host candidate %s for"
- " s%d:%d. Invalid interface?", agent, ip, stream->id,
- component->id);
+ for (add_type = ADD_HOST_MIN; add_type <= ADD_HOST_MAX; add_type++) {
+ NiceCandidateTransport transport;
+ guint current_port;
+ guint start_port;
+
+ if ((agent->use_ice_udp == FALSE && add_type == ADD_HOST_UDP) ||
+ (agent->use_ice_tcp == FALSE && add_type != ADD_HOST_UDP))
+ continue;
+
+ switch (add_type) {
+ default:
+ case ADD_HOST_UDP:
+ transport = NICE_CANDIDATE_TRANSPORT_UDP;
+ break;
+ case ADD_HOST_TCP_ACTIVE:
+ transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
+ break;
+ case ADD_HOST_TCP_PASSIVE:
+ transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
+ break;
+ }
+
+ start_port = component->min_port;
+ if(component->min_port != 0) {
+ start_port = nice_rng_generate_int(agent->rng, component->min_port, component->max_port+1);
+ }
+ current_port = start_port;
+
+ host_candidate = NULL;
+ while (host_candidate == NULL) {
+ nice_debug ("Agent %p: Trying to create host candidate on port %d", agent, current_port);
+ nice_address_set_port (addr, current_port);
+ host_candidate = discovery_add_local_host_candidate (agent, stream->id,
+ cid, addr, transport);
+ if (current_port > 0)
+ current_port++;
+ if (current_port > component->max_port) current_port = component->min_port;
+ if (current_port == 0 || current_port == start_port)
+ break;
+ }
+ nice_address_set_port (addr, 0);
+
+ if (!host_candidate) {
+ if (nice_debug_is_enabled ()) {
+ gchar ip[NICE_ADDRESS_STRING_LEN];
+ nice_address_to_string (addr, ip);
+ nice_debug ("Agent %p: Unable to add local host candidate %s for"
+ " s%d:%d. Invalid interface?", agent, ip, stream->id,
+ component->id);
+ }
+ ret = FALSE;
+ goto error;
}
- ret = FALSE;
- goto error;
- }
#ifdef HAVE_GUPNP
- if (agent->upnp_enabled) {
- NiceAddress *base_addr = nice_address_dup (&host_candidate->base_addr);
- nice_debug ("Agent %p: Adding UPnP port %s:%d", agent, local_ip,
- nice_address_get_port (base_addr));
- gupnp_simple_igd_add_port (GUPNP_SIMPLE_IGD (agent->upnp), "UDP",
- 0, local_ip, nice_address_get_port (base_addr),
- 0, PACKAGE_STRING);
- agent->upnp_mapping = g_slist_prepend (agent->upnp_mapping, base_addr);
+ if (agent->upnp_enabled) {
+ NiceAddress *base_addr = nice_address_dup (&host_candidate->base_addr);
+ nice_debug ("Agent %p: Adding UPnP port %s:%d", agent, local_ip,
+ nice_address_get_port (base_addr));
+ gupnp_simple_igd_add_port (GUPNP_SIMPLE_IGD (agent->upnp),
+ transport == NICE_CANDIDATE_TRANSPORT_UDP ? "UDP" : "TCP",
+ 0, local_ip, nice_address_get_port (base_addr),
+ 0, PACKAGE_STRING);
+ agent->upnp_mapping = g_slist_prepend (agent->upnp_mapping, base_addr);
}
#endif
- if (agent->full_mode &&
- agent->stun_server_ip) {
- NiceAddress stun_server;
- if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
- nice_address_set_port (&stun_server, agent->stun_server_port);
-
- priv_add_new_candidate_discovery_stun (agent,
- host_candidate->sockptr,
- stun_server,
- stream,
- cid);
+ /* TODO: Add server-reflexive support for TCP candidates */
+ if (agent->full_mode && agent->stun_server_ip &&
+ transport == NICE_CANDIDATE_TRANSPORT_UDP) {
+ NiceAddress stun_server;
+ if (nice_address_set_from_string (&stun_server, agent->stun_server_ip)) {
+ nice_address_set_port (&stun_server, agent->stun_server_port);
+
+ priv_add_new_candidate_discovery_stun (agent,
+ host_candidate->sockptr,
+ stun_server,
+ stream,
+ cid);
+ }
}
- }
- if (agent->full_mode && component) {
- GList *item;
+ if (agent->full_mode && component) {
+ GList *item;
- for (item = component->turn_servers; item; item = item->next) {
- TurnServer *turn = item->data;
+ for (item = component->turn_servers; item; item = item->next) {
+ TurnServer *turn = item->data;
- priv_add_new_candidate_discovery_turn (agent,
- host_candidate->sockptr,
- turn,
- stream,
- cid);
+ priv_add_new_candidate_discovery_turn (agent,
+ host_candidate->sockptr,
+ turn,
+ stream,
+ cid);
+ }
}
}
}
}
g_free (local_bufs);
} else {
- /* In the case of a real ICE-TCP connection, we can use the socket as a
- * bytestream and do the read here with caching of data being read
- */
- gssize available = g_socket_get_available_bytes (nicesock->fileno);
+ if (nicesock->type == NICE_SOCKET_TYPE_TCP_PASSIVE) {
+ NiceSocket *new_socket;
+
+ /* Passive candidates when readable should accept and create a new
+ * socket. When established, the connchecks will create a peer reflexive
+ * candidate for it */
+ new_socket = nice_tcp_passive_socket_accept (nicesock);
+ if (new_socket) {
+ _priv_set_socket_tos (agent, new_socket, stream->tos);
+ component_attach_socket (component, new_socket);
+ }
+ retval = 0;
+ } else {
+ /* In the case of a real ICE-TCP connection, we can use the socket as a
+ * bytestream and do the read here with caching of data being read
+ */
+ gssize available = g_socket_get_available_bytes (nicesock->fileno);
+
+ /* TODO: Support bytestream reads */
+ message->length = 0;
+ if (available <= 0) {
+ retval = available;
+ } else if (agent->rfc4571_expecting_length == 0) {
+ if ((gsize) available >= sizeof(guint16)) {
+ guint16 rfc4571_frame;
+ GInputVector local_buf = { &rfc4571_frame, sizeof(guint16)};
+ NiceInputMessage local_message = { &local_buf, 1, message->from, 0};
- /* TODO: Support bytestream reads */
- message->length = 0;
- if (available <= 0) {
- retval = available;
- } else if (agent->rfc4571_expecting_length == 0) {
- if ((gsize) available >= sizeof(guint16)) {
- guint16 rfc4571_frame;
- GInputVector local_buf = { &rfc4571_frame, sizeof(guint16)};
- NiceInputMessage local_message = { &local_buf, 1, message->from, 0};
+ retval = nice_socket_recv_messages (nicesock, &local_message, 1);
+ if (retval == 1) {
+ agent->rfc4571_expecting_length = ntohs (rfc4571_frame);
+ available = g_socket_get_available_bytes (nicesock->fileno);
+ }
+ } else {
+ retval = 0;
+ }
+ }
+ if (agent->rfc4571_expecting_length > 0 &&
+ available >= agent->rfc4571_expecting_length) {
+ GInputVector *local_bufs;
+ NiceInputMessage local_message;
+ gsize off;
+ guint n_bufs = 0;
+ guint i;
+ /* Count the number of buffers. */
+ if (message->n_buffers == -1) {
+ for (i = 0; message->buffers[i].buffer != NULL; i++)
+ n_bufs++;
+ } else {
+ n_bufs = message->n_buffers;
+ }
+
+ local_bufs = g_malloc_n (n_bufs, sizeof (GInputVector));
+ local_message.buffers = local_bufs;
+ local_message.from = message->from;
+ local_message.length = 0;
+ local_message.n_buffers = 0;
+
+ /* Only read up to the expected number of bytes in the frame */
+ off = 0;
+ for (i = 0; i < n_bufs; i++) {
+ if (message->buffers[i].size < agent->rfc4571_expecting_length - off) {
+ local_bufs[i].buffer = message->buffers[i].buffer;
+ local_bufs[i].size = message->buffers[i].size;
+ local_message.n_buffers++;
+ off += message->buffers[i].size;
+ } else {
+ local_bufs[i].buffer = message->buffers[i].buffer;
+ local_bufs[i].size = MIN (message->buffers[i].size,
+ agent->rfc4571_expecting_length - off);
+ local_message.n_buffers++;
+ off += local_bufs[i].size;
+ }
+ }
retval = nice_socket_recv_messages (nicesock, &local_message, 1);
if (retval == 1) {
- agent->rfc4571_expecting_length = ntohs (rfc4571_frame);
- available = g_socket_get_available_bytes (nicesock->fileno);
+ message->length = local_message.length;
+ agent->rfc4571_expecting_length -= local_message.length;
}
+ g_free (local_bufs);
} else {
retval = 0;
}
}
- if (agent->rfc4571_expecting_length > 0 &&
- available >= agent->rfc4571_expecting_length) {
- GInputVector *local_bufs;
- NiceInputMessage local_message;
- gsize off;
- guint n_bufs = 0;
- guint i;
-
- /* Count the number of buffers. */
- if (message->n_buffers == -1) {
- for (i = 0; message->buffers[i].buffer != NULL; i++)
- n_bufs++;
- } else {
- n_bufs = message->n_buffers;
- }
-
- local_bufs = g_malloc_n (n_bufs, sizeof (GInputVector));
- local_message.buffers = local_bufs;
- local_message.from = message->from;
- local_message.length = 0;
- local_message.n_buffers = 0;
-
- /* Only read up to the expected number of bytes in the frame */
- off = 0;
- for (i = 0; i < n_bufs; i++) {
- if (message->buffers[i].size < agent->rfc4571_expecting_length - off) {
- local_bufs[i].buffer = message->buffers[i].buffer;
- local_bufs[i].size = message->buffers[i].size;
- local_message.n_buffers++;
- off += message->buffers[i].size;
- } else {
- local_bufs[i].buffer = message->buffers[i].buffer;
- local_bufs[i].size = MIN (message->buffers[i].size,
- agent->rfc4571_expecting_length - off);
- local_message.n_buffers++;
- off += local_bufs[i].size;
- }
- }
- retval = nice_socket_recv_messages (nicesock, &local_message, 1);
- if (retval == 1) {
- message->length = local_message.length;
- agent->rfc4571_expecting_length -= local_message.length;
- }
- g_free (local_bufs);
- } else {
- retval = 0;
- }
}
} else {
retval = nice_socket_recv_messages (nicesock, message, 1);
}
}
}
+ } else {
+ for (l = component->local_candidates; l; l = l->next) {
+ NiceCandidate *cand = l->data;
+ if (nice_address_equal (&cand->addr, &icheck->local_socket->addr)) {
+ local_candidate = cand;
+ break;
+ }
+ }
}
if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE &&
icheck->local_socket,
local_candidate, remote_candidate);
if (candidate) {
- conn_check_add_for_candidate (agent, stream->id, component, candidate);
+ if (local_candidate &&
+ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
+ priv_conn_check_add_for_candidate_pair_matched (agent,
+ stream->id, component, local_candidate, candidate);
+ else
+ conn_check_add_for_candidate (agent, stream->id, component, candidate);
if (icheck->use_candidate)
priv_mark_pair_nominated (agent, stream, component, candidate);
pair->component_id = component->id;;
pair->local = local;
pair->remote = remote;
- pair->sockptr = (NiceSocket *) local->sockptr;
+ if (remote->type == NICE_CANDIDATE_TYPE_PEER_REFLEXIVE)
+ pair->sockptr = (NiceSocket *) remote->sockptr;
+ else
+ pair->sockptr = (NiceSocket *) local->sockptr;
g_snprintf (pair->foundation, NICE_CANDIDATE_PAIR_MAX_FOUNDATION, "%s:%s", local->foundation, remote->foundation);
pair->priority = agent_candidate_pair_priority (agent, local, remote);
STUN_TIMER_DEFAULT_MAX_RETRANSMISSIONS);
}
+ /* TCP-ACTIVE candidate must create a new socket before sending
+ * by connecting to the peer. The new socket is stored in the candidate
+ * check pair, until we discover a new local peer reflexive */
+ if (pair->sockptr->fileno == NULL &&
+ pair->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
+ Stream *stream = NULL;
+ Component *component = NULL;
+ NiceSocket *new_socket;
+
+ if (agent_find_component (agent, pair->stream_id, pair->component_id,
+ &stream, &component)) {
+ new_socket = nice_tcp_active_socket_connect (pair->sockptr,
+ &pair->remote->addr);
+ if (new_socket) {
+ pair->sockptr = new_socket;
+ _priv_set_socket_tos (agent, pair->sockptr, stream->tos);
+ component_attach_socket (component, new_socket);
+ }
+ }
+ }
/* send the conncheck */
agent_socket_send (pair->sockptr, &pair->remote->addr,
buffer_len, (gchar *)pair->stun_buffer);
CandidateCheckPair *p = i->data;
if (p->component_id == component->id &&
p->remote == remote_cand &&
- p->sockptr == local_socket) {
+ ((p->local->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
+ p->sockptr == local_socket) ||
+ (p->local->transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
+ p->local->sockptr == local_socket))) {
+ /* We don't check for p->sockptr because in the case of
+ * tcp-active we don't want to retrigger a check on a pair that
+ * was FAILED when a peer-reflexive pair was created */
nice_debug ("Agent %p : Found a matching pair %p for triggered check.", agent, p);
{
CandidateCheckPair *new_pair = NULL;
NiceAddress mapped;
- GSList *j;
+ GSList *i, *j;
gboolean local_cand_matches = FALSE;
nice_address_set_from_sockaddr (&mapped, mapped_sockaddr);
for (j = component->local_candidates; j; j = j->next) {
NiceCandidate *cand = j->data;
if (nice_address_equal (&mapped, &cand->addr)) {
- local_cand_matches = TRUE;
+ local_cand_matches = TRUE;
+
+ /* We always need to select the peer-reflexive Candidate Pair in the case
+ * of a TCP-ACTIVE local candidate, so we find it even if an incoming
+ * check matched an existing pair because it could be the original
+ * ACTIVE-PASSIVE candidate pair which was retriggered */
+ for (i = stream->conncheck_list; i; i = i->next) {
+ CandidateCheckPair *pair = i->data;
+ if (pair->local == cand && remote_candidate == pair->remote) {
+ new_pair = pair;
+ break;
+ }
+ }
break;
}
}
}
for (i = component->local_candidates; i; i = i->next) {
NiceCandidate *cand = i->data;
- if (cand->sockptr == nicesock) {
+ if (nice_address_equal (&nicesock->addr, &cand->addr)) {
local_candidate = cand;
break;
}
agent, stream, component, priority, from, nicesock,
local_candidate,
remote_candidate2 ? remote_candidate2 : remote_candidate);
- if(remote_candidate)
- conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
+ if(remote_candidate) {
+ if (local_candidate &&
+ local_candidate->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
+ priv_conn_check_add_for_candidate_pair_matched (agent,
+ stream->id, component, local_candidate, remote_candidate);
+ else
+ conn_check_add_for_candidate (agent, stream->id, component, remote_candidate);
+ }
}
priv_reply_to_conn_check (agent, stream, component, remote_candidate,
level ufrag/password are used */
if (transport == NICE_CANDIDATE_TRANSPORT_UDP) {
nicesock = nice_udp_bsd_socket_new (address);
+ } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE) {
+ nicesock = nice_tcp_active_socket_new (agent->main_context, address);
+ } else if (transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE) {
+ nicesock = nice_tcp_passive_socket_new (agent->main_context, address);
} else {
- /* TODO: Add ICE-TCP */
+ /* TODO: Add TCP-SO */
}
if (!nicesock)
goto errors;
return NULL;
candidate = nice_candidate_new (NICE_CANDIDATE_TYPE_PEER_REFLEXIVE);
- candidate->transport = local->transport;
+ if (local)
+ candidate->transport = local->transport;
+ else if (remote)
+ candidate->transport = conn_check_match_transport (remote->transport);
+ else {
+ if (base_socket->type == NICE_SOCKET_TYPE_UDP_BSD ||
+ base_socket->type == NICE_SOCKET_TYPE_UDP_TURN)
+ candidate->transport = NICE_CANDIDATE_TRANSPORT_UDP;
+ else
+ candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE;
+ }
candidate->stream_id = stream_id;
candidate->component_id = component_id;
candidate->addr = *address;
+ candidate->sockptr = base_socket;
candidate->base_addr = base_socket->addr;
if (agent->compatibility == NICE_COMPATIBILITY_GOOGLE) {
candidate->password = g_strdup(local->password);
}
- /* step: link to the base candidate+socket */
- candidate->sockptr = base_socket;
- candidate->base_addr = base_socket->addr;
-
result = priv_add_local_candidate_pruned (agent, stream_id, component, candidate);
if (result != TRUE) {
/* error: memory allocation, or duplicate candidate */
else
candidate->transport = NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE;
}
+ candidate->sockptr = nicesock;
candidate->stream_id = stream->id;
candidate->component_id = component->id;
candidate->password = g_strdup(remote->password);
}
- candidate->sockptr = NULL; /* not stored for remote candidates */
/* note: candidate username and password are left NULL as stream
level ufrag/password are used */
test-thread \
test-dribble \
test-new-dribble \
- test-tcp
+ test-tcp \
+ test-icetcp
dist_check_SCRIPTS = \
check-test-fullmode-with-stun.sh \
test_tcp_LDADD = $(COMMON_LDADD)
+test_icetcp_LDADD = $(COMMON_LDADD)
+
all-local:
chmod a+x $(srcdir)/check-test-fullmode-with-stun.sh
chmod a+x $(srcdir)/test-pseudotcp-random.sh
NICE_COMPATIBILITY);
#endif
+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL);
+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL);
+
+
nice_agent_set_software (lagent, "Test-fullmode, Left Agent");
nice_agent_set_software (ragent, "Test-fullmode, Right Agent");
--- /dev/null
+/*
+ * This file is part of the Nice GLib ICE library.
+ *
+ * Unit test for ICE full-mode related features.
+ *
+ * (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.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "agent.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+static NiceComponentState global_lagent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
+static NiceComponentState global_ragent_state[2] = { NICE_COMPONENT_STATE_LAST, NICE_COMPONENT_STATE_LAST };
+static guint global_components_ready = 0;
+static guint global_components_ready_exit = 0;
+static guint global_components_failed = 0;
+static guint global_components_failed_exit = 0;
+static GMainLoop *global_mainloop = NULL;
+static gboolean global_lagent_gathering_done = FALSE;
+static gboolean global_ragent_gathering_done = FALSE;
+static gboolean global_lagent_ibr_received = FALSE;
+static gboolean global_ragent_ibr_received = FALSE;
+static int global_lagent_cands = 0;
+static int global_ragent_cands = 0;
+static gint global_ragent_read = 0;
+static guint global_exit_when_ibr_received = 0;
+
+static void priv_print_global_status (void)
+{
+ g_debug ("\tgathering_done=%d", global_lagent_gathering_done && global_ragent_gathering_done);
+ g_debug ("\tlstate[rtp]=%d [rtcp]=%d", global_lagent_state[0], global_lagent_state[1]);
+ g_debug ("\trstate[rtp]=%d [rtcp]=%d", global_ragent_state[0], global_ragent_state[1]);
+ g_debug ("\tL cands=%d R cands=%d", global_lagent_cands, global_ragent_cands);
+}
+
+static gboolean timer_cb (gpointer pointer)
+{
+ g_debug ("test-icetcp:%s: %p", G_STRFUNC, pointer);
+
+ /* signal status via a global variable */
+
+ /* note: should not be reached, abort */
+ g_error ("ERROR: test has got stuck, aborting...");
+
+ return FALSE;
+}
+
+static void cb_writable (NiceAgent*agent, guint stream_id, guint component_id)
+{
+ g_debug ("Transport is now writable, stopping mainloop");
+ g_main_loop_quit (global_mainloop);
+}
+
+static void cb_nice_recv (NiceAgent *agent, guint stream_id, guint component_id, guint len, gchar *buf, gpointer user_data)
+{
+ g_debug ("test-icetcp:%s: %p", G_STRFUNC, user_data);
+
+ /* XXX: dear compiler, these are for you: */
+ (void)agent; (void)stream_id; (void)component_id; (void)buf;
+
+ /*
+ * Lets ignore stun packets that got through
+ */
+ if (len < 8)
+ return;
+ if (strncmp ("12345678", buf, 8))
+ return;
+
+ if (GPOINTER_TO_UINT (user_data) == 2) {
+ g_debug ("right agent received %d bytes, stopping mainloop", len);
+ global_ragent_read = len;
+ g_main_loop_quit (global_mainloop);
+ }
+}
+
+static void cb_candidate_gathering_done(NiceAgent *agent, guint stream_id, gpointer data)
+{
+ g_debug ("test-icetcp:%s: %p", G_STRFUNC, data);
+
+ if (GPOINTER_TO_UINT (data) == 1)
+ global_lagent_gathering_done = TRUE;
+ else if (GPOINTER_TO_UINT (data) == 2)
+ global_ragent_gathering_done = TRUE;
+
+ if (global_lagent_gathering_done &&
+ global_ragent_gathering_done)
+ g_main_loop_quit (global_mainloop);
+
+ /* XXX: dear compiler, these are for you: */
+ (void)agent;
+}
+
+static void cb_component_state_changed (NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer data)
+{
+ g_debug ("test-icetcp:%s: %p", G_STRFUNC, data);
+
+ if (GPOINTER_TO_UINT (data) == 1)
+ global_lagent_state[component_id - 1] = state;
+ else if (GPOINTER_TO_UINT (data) == 2)
+ global_ragent_state[component_id - 1] = state;
+
+ if (state == NICE_COMPONENT_STATE_READY)
+ global_components_ready++;
+ if (state == NICE_COMPONENT_STATE_FAILED)
+ global_components_failed++;
+
+ g_debug ("test-icetcp: checks READY/EXIT-AT %u/%u.", global_components_ready, global_components_ready_exit);
+ g_debug ("test-icetcp: checks FAILED/EXIT-AT %u/%u.", global_components_failed, global_components_failed_exit);
+
+ /* signal status via a global variable */
+ if (global_components_ready == global_components_ready_exit &&
+ global_components_failed == global_components_failed_exit) {
+ g_debug ("Components ready/failed achieved. Stopping mailoop");
+ g_main_loop_quit (global_mainloop);
+ return;
+ }
+
+#if 0
+ /* signal status via a global variable */
+ if (global_components_failed == global_components_failed_exit) {
+ g_main_loop_quit (global_mainloop);
+ return;
+ }
+#endif
+
+ /* XXX: dear compiler, these are for you: */
+ (void)agent; (void)stream_id; (void)data; (void)component_id;
+}
+
+static void cb_new_selected_pair(NiceAgent *agent, guint stream_id, guint component_id,
+ gchar *lfoundation, gchar* rfoundation, gpointer data)
+{
+ g_debug ("test-icetcp:%s: %p", G_STRFUNC, data);
+
+ if (GPOINTER_TO_UINT (data) == 1)
+ ++global_lagent_cands;
+ else if (GPOINTER_TO_UINT (data) == 2)
+ ++global_ragent_cands;
+
+ /* XXX: dear compiler, these are for you: */
+ (void)agent; (void)stream_id; (void)component_id; (void)lfoundation; (void)rfoundation;
+}
+
+static void cb_new_candidate(NiceAgent *agent, guint stream_id, guint component_id,
+ gchar *foundation, gpointer data)
+{
+ g_debug ("test-icetcp:%s: %p", G_STRFUNC, data);
+
+ /* XXX: dear compiler, these are for you: */
+ (void)agent; (void)stream_id; (void)data; (void)component_id; (void)foundation;
+}
+
+static void cb_initial_binding_request_received(NiceAgent *agent, guint stream_id, gpointer data)
+{
+ g_debug ("test-icetcp:%s: %p", G_STRFUNC, data);
+
+ if (GPOINTER_TO_UINT (data) == 1)
+ global_lagent_ibr_received = TRUE;
+ else if (GPOINTER_TO_UINT (data) == 2)
+ global_ragent_ibr_received = TRUE;
+
+ if (global_exit_when_ibr_received) {
+ g_debug ("Received initial binding request. Stopping mailoop");
+ g_main_loop_quit (global_mainloop);
+ }
+
+ /* XXX: dear compiler, these are for you: */
+ (void)agent; (void)stream_id; (void)data;
+}
+
+static void set_candidates (NiceAgent *from, guint from_stream,
+ NiceAgent *to, guint to_stream, guint component)
+{
+ GSList *cands = NULL, *i;
+
+ cands = nice_agent_get_local_candidates (from, from_stream, component);
+
+ restart:
+ for (i = cands; i; i = i->next) {
+ NiceCandidate *cand = i->data;
+ if (cand->transport == NICE_CANDIDATE_TRANSPORT_UDP) {
+ cands = g_slist_remove (cands, cand);
+ nice_candidate_free (cand);
+ goto restart;
+ }
+ }
+
+
+ nice_agent_set_remote_candidates (to, to_stream, component, cands);
+
+ for (i = cands; i; i = i->next)
+ nice_candidate_free ((NiceCandidate *) i->data);
+ g_slist_free (cands);
+}
+
+static void set_credentials (NiceAgent *lagent, guint lstream,
+ NiceAgent *ragent, guint rstream)
+{
+ gchar *ufrag = NULL, *password = NULL;
+
+ nice_agent_get_local_credentials(lagent, lstream, &ufrag, &password);
+ nice_agent_set_remote_credentials (ragent, rstream, ufrag, password);
+ g_free (ufrag);
+ g_free (password);
+ nice_agent_get_local_credentials(ragent, rstream, &ufrag, &password);
+ nice_agent_set_remote_credentials (lagent, lstream, ufrag, password);
+ g_free (ufrag);
+ g_free (password);
+}
+
+static int run_full_test (NiceAgent *lagent, NiceAgent *ragent, NiceAddress *baseaddr, guint ready, guint failed)
+{
+ guint ls_id, rs_id;
+ gint ret;
+
+ /* XXX: dear compiler, this is for you */
+ (void)baseaddr;
+
+ /* step: initialize variables modified by the callbacks */
+ global_components_ready = 0;
+ global_components_ready_exit = ready;
+ global_components_failed = 0;
+ global_components_failed_exit = failed;
+ global_lagent_gathering_done = FALSE;
+ global_ragent_gathering_done = FALSE;
+ global_lagent_ibr_received =
+ global_ragent_ibr_received = FALSE;
+ global_lagent_cands =
+ global_ragent_cands = 0;
+
+ g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
+ g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
+
+ /* step: add one stream, with RTP+RTCP components, to each agent */
+ ls_id = nice_agent_add_stream (lagent, 2);
+
+ rs_id = nice_agent_add_stream (ragent, 2);
+ g_assert (ls_id > 0);
+ g_assert (rs_id > 0);
+
+ /* Gather candidates */
+ g_assert (nice_agent_gather_candidates (lagent, ls_id) == TRUE);
+ g_assert (nice_agent_gather_candidates (ragent, rs_id) == TRUE);
+
+ {
+ GSList *cands = NULL, *i;
+ NiceCandidate *cand = NULL;
+
+ cands = nice_agent_get_local_candidates (lagent, ls_id, 1);
+ g_assert (g_slist_length (cands) == 2);
+ cand = cands->data;
+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST);
+ g_assert (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE ||
+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE);
+ cand = cands->next->data;
+ g_assert (cand->type == NICE_CANDIDATE_TYPE_HOST);
+ g_assert (cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE ||
+ cand->transport == NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE);
+ for (i = cands; i; i = i->next)
+ nice_candidate_free ((NiceCandidate *) i->data);
+ g_slist_free (cands);
+ }
+
+ /* step: attach to mainloop (needed to register the fds) */
+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTP,
+ g_main_loop_get_context (global_mainloop), cb_nice_recv,
+ GUINT_TO_POINTER (1));
+ nice_agent_attach_recv (lagent, ls_id, NICE_COMPONENT_TYPE_RTCP,
+ g_main_loop_get_context (global_mainloop), cb_nice_recv,
+ GUINT_TO_POINTER (1));
+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTP,
+ g_main_loop_get_context (global_mainloop), cb_nice_recv,
+ GUINT_TO_POINTER (2));
+ nice_agent_attach_recv (ragent, rs_id, NICE_COMPONENT_TYPE_RTCP,
+ g_main_loop_get_context (global_mainloop), cb_nice_recv,
+ GUINT_TO_POINTER (2));
+
+ /* step: run mainloop until local candidates are ready
+ * (see timer_cb() above) */
+ if (global_lagent_gathering_done != TRUE ||
+ global_ragent_gathering_done != TRUE) {
+ g_debug ("test-icetcp: Added streams, running mainloop until 'candidate-gathering-done'...");
+ g_main_loop_run (global_mainloop);
+ g_assert (global_lagent_gathering_done == TRUE);
+ g_assert (global_ragent_gathering_done == TRUE);
+ }
+
+ set_credentials (lagent, ls_id, ragent, rs_id);
+
+ /* step: pass the remote candidates to agents */
+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTP);
+ set_candidates (ragent, rs_id, lagent, ls_id, NICE_COMPONENT_TYPE_RTCP);
+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTP);
+ set_candidates (lagent, ls_id, ragent, rs_id, NICE_COMPONENT_TYPE_RTCP);
+
+ g_debug ("test-icetcp: 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: test payload send and receive */
+ global_ragent_read = 0;
+ ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678");
+ if (ret == -1)
+ {
+ gboolean reliable = FALSE;
+ g_object_get (G_OBJECT (lagent), "reliable", &reliable, NULL);
+ g_debug ("Sending data returned -1 in %s mode", reliable?"Reliable":"Non-reliable");
+ if (reliable) {
+ gulong signal_handler;
+ signal_handler = g_signal_connect (G_OBJECT (lagent),
+ "reliable-transport-writable", G_CALLBACK (cb_writable), NULL);
+ g_debug ("Running mainloop until transport is writable");
+ g_main_loop_run (global_mainloop);
+ g_signal_handler_disconnect(G_OBJECT (lagent), signal_handler);
+
+ ret = nice_agent_send (lagent, ls_id, 1, 16, "1234567812345678");
+ }
+ }
+ g_debug ("Sent %d bytes", ret);
+ g_assert (ret == 16);
+ g_main_loop_run (global_mainloop);
+ g_assert (global_ragent_read == 16);
+
+ g_debug ("test-icetcp: 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 */
+ NiceAddress baseaddr;
+ int result;
+ guint timer_id;
+
+#ifdef G_OS_WIN32
+ WSADATA w;
+
+ WSAStartup(0x0202, &w);
+#endif
+ g_type_init ();
+#if !GLIB_CHECK_VERSION(2,31,8)
+ g_thread_init(NULL);
+#endif
+
+ global_mainloop = g_main_loop_new (NULL, FALSE);
+
+ /* step: create the agents L and R */
+ lagent = nice_agent_new (g_main_loop_get_context (global_mainloop),
+ NICE_COMPATIBILITY_RFC5245);
+ ragent = nice_agent_new (g_main_loop_get_context (global_mainloop),
+ NICE_COMPATIBILITY_RFC5245);
+
+ g_object_set (G_OBJECT (lagent), "ice-udp", FALSE, NULL);
+ g_object_set (G_OBJECT (ragent), "ice-udp", FALSE, NULL);
+ nice_agent_set_software (lagent, "Test-icetcp, Left Agent");
+ nice_agent_set_software (ragent, "Test-icetcp, Right Agent");
+
+ /* step: add a timer to catch state changes triggered by signals */
+ timer_id = g_timeout_add (30000, timer_cb, NULL);
+
+ /* step: specify which local interface to use */
+ if (!nice_address_set_from_string (&baseaddr, "127.0.0.1"))
+ g_assert_not_reached ();
+ nice_agent_add_local_address (lagent, &baseaddr);
+ nice_agent_add_local_address (ragent, &baseaddr);
+
+ g_signal_connect (G_OBJECT (lagent), "candidate-gathering-done",
+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER(1));
+ g_signal_connect (G_OBJECT (ragent), "candidate-gathering-done",
+ G_CALLBACK (cb_candidate_gathering_done), GUINT_TO_POINTER (2));
+ g_signal_connect (G_OBJECT (lagent), "component-state-changed",
+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (1));
+ g_signal_connect (G_OBJECT (ragent), "component-state-changed",
+ G_CALLBACK (cb_component_state_changed), GUINT_TO_POINTER (2));
+ g_signal_connect (G_OBJECT (lagent), "new-selected-pair",
+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER(1));
+ g_signal_connect (G_OBJECT (ragent), "new-selected-pair",
+ G_CALLBACK (cb_new_selected_pair), GUINT_TO_POINTER (2));
+ g_signal_connect (G_OBJECT (lagent), "new-candidate",
+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (1));
+ g_signal_connect (G_OBJECT (ragent), "new-candidate",
+ G_CALLBACK (cb_new_candidate), GUINT_TO_POINTER (2));
+ g_signal_connect (G_OBJECT (lagent), "initial-binding-request-received",
+ G_CALLBACK (cb_initial_binding_request_received),
+ GUINT_TO_POINTER (1));
+ g_signal_connect (G_OBJECT (ragent), "initial-binding-request-received",
+ G_CALLBACK (cb_initial_binding_request_received),
+ GUINT_TO_POINTER (2));
+
+ /* step: run test the first time */
+ g_debug ("test-icetcp: TEST STARTS / running test for the 1st time");
+ result = run_full_test (lagent, ragent, &baseaddr, 4 ,0);
+ priv_print_global_status ();
+ g_assert (result == 0);
+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY);
+ g_assert (global_lagent_state[1] == NICE_COMPONENT_STATE_READY);
+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY);
+ g_assert (global_ragent_state[1] == NICE_COMPONENT_STATE_READY);
+ /* note: verify that correct number of local candidates were reported */
+ g_assert (global_lagent_cands >= 2);
+ g_assert (global_ragent_cands >= 2);
+
+
+ /* step: run test again without unref'ing agents */
+ g_debug ("test-icetcp: TEST STARTS / running test for the 2nd time");
+ result = run_full_test (lagent, ragent, &baseaddr, 4, 0);
+ priv_print_global_status ();
+ g_assert (result == 0);
+ g_assert (global_lagent_state[0] == NICE_COMPONENT_STATE_READY);
+ g_assert (global_lagent_state[1] == NICE_COMPONENT_STATE_READY);
+ g_assert (global_ragent_state[0] == NICE_COMPONENT_STATE_READY);
+ g_assert (global_ragent_state[1] == NICE_COMPONENT_STATE_READY);
+ /* note: verify that correct number of local candidates were reported */
+ g_assert (global_lagent_cands >= 2);
+ g_assert (global_ragent_cands >= 2);
+
+ g_object_unref (lagent);
+ g_object_unref (ragent);
+
+ g_main_loop_unref (global_mainloop);
+ global_mainloop = NULL;
+
+ g_source_remove (timer_id);
+#ifdef G_OS_WIN32
+ WSACleanup();
+#endif
+ return result;
+}
lagent = nice_agent_new (NULL, NICE_COMPATIBILITY_RFC5245);
ragent = nice_agent_new (NULL, NICE_COMPATIBILITY_RFC5245);
+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL);
+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL);
+
g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
/* step: create the agents L and R */
lagent = nice_agent_new (g_main_loop_get_context (global_mainloop), NICE_COMPATIBILITY_RFC5245);
ragent = nice_agent_new (g_main_loop_get_context (global_mainloop), NICE_COMPATIBILITY_RFC5245);
+ g_object_set (G_OBJECT (lagent), "ice-tcp", FALSE, NULL);
+ g_object_set (G_OBJECT (ragent), "ice-tcp", FALSE, NULL);
/* step: add a timer to catch state changes triggered by signals */
nice_address_set_port (&addr_remote, 2345);
agent = nice_agent_new ( NULL, NICE_COMPATIBILITY_RFC5245);
+ g_object_set (G_OBJECT (agent), "ice-tcp", FALSE, NULL);
g_assert (agent->local_addresses == NULL);