Commit b9da8180 authored by Olivier Crête's avatar Olivier Crête

agent: Resolve TURN server IP addresses

This should enable having servers that do both IPv4 and IPv6. And
ideally should make life easier for app developers.
parent 24eecfd7
......@@ -2292,6 +2292,7 @@ _transport_to_string (NiceCandidateTransport type) {
void agent_gathering_done (NiceAgent *agent)
{
gboolean upnp_running = FALSE;
gboolean dns_resolution_ongoing = FALSE;
GSList *i, *j, *k, *l, *m;
if (agent->stun_resolving_list) {
......@@ -2319,6 +2320,11 @@ void agent_gathering_done (NiceAgent *agent)
for (j = stream->components; j; j = j->next) {
NiceComponent *component = j->data;
if (nice_component_resolving_turn (component)) {
dns_resolution_ongoing = TRUE;
continue;
}
for (k = component->local_candidates; k;) {
NiceCandidate *local_candidate = k->data;
GSList *next = k->next;
......@@ -2387,7 +2393,8 @@ next_cand:
}
}
if (agent->discovery_timer_source == NULL && !upnp_running)
if (agent->discovery_timer_source == NULL && !upnp_running &&
!dns_resolution_ongoing)
agent_signal_gathering_done (agent);
}
......@@ -3016,6 +3023,167 @@ nice_agent_add_stream (
return ret;
}
struct TurnResolverData {
GWeakRef component_ref;
TurnServer *turn;
};
static void
turn_server_resolved_cb (GObject *src, GAsyncResult *result,
gpointer user_data)
{
GResolver *resolver = G_RESOLVER (src);
GList *addresses = NULL, *item;
GError *error = NULL;
struct TurnResolverData *rd = user_data;
NiceAgent *agent;
NiceStream *stream;
NiceComponent *component;
TurnServer *turn = rd->turn;
gboolean first_filled = FALSE;
component = g_weak_ref_get (&rd->component_ref);
g_weak_ref_clear (&rd->component_ref);
g_slice_free (struct TurnResolverData, rd);
if (component == NULL) {
turn_server_unref (turn);
return;
}
agent = g_weak_ref_get (&component->agent_ref);
if (agent == NULL) {
g_object_unref (component);
turn_server_unref (turn);
return;
}
agent_lock (agent);
if (g_list_find (component->turn_servers, turn) == NULL) {
/* No longer relevant turn server */
goto done;
}
stream = agent_find_stream (agent, component->stream_id);
addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
if (addresses == NULL) {
g_warning ("Agent: %p: s:%d/c:%d: Can't resolve TURN server %s: %s", agent,
component->stream_id, component->id, turn->server_address,
error->message);
g_clear_error (&error);
turn->resolution_failed = TRUE;
goto done;
}
for (item = addresses; item; item = item->next) {
GInetAddress *addr = item->data;
const guint8 *addr_bytes = g_inet_address_to_bytes (addr);
GSList *citem;
if (nice_debug_is_enabled ()) {
char *resolved_addr = g_inet_address_to_string (addr);
nice_debug ("Agent %p: s:%d/c:%d: Resolved TURN server %s to %s",
agent, component->stream_id, component->id, turn->server_address,
resolved_addr);
g_free (resolved_addr);
}
/* If there is already one resolved, duplicate it */
if (first_filled) {
TurnServer *copy = turn_server_copy (turn);
turn_server_unref (turn);
turn = copy;
component->turn_servers = g_list_append (component->turn_servers,
turn_server_ref (turn));
}
switch (g_inet_address_get_family (addr)) {
case G_SOCKET_FAMILY_IPV4:
nice_address_set_ipv4 (&turn->server, ntohl (*((guint32 *) addr_bytes)));
break;
case G_SOCKET_FAMILY_IPV6:
nice_address_set_ipv6 (&turn->server, addr_bytes);
break;
default:
/* Ignore others */
continue;
}
nice_address_set_port (&turn->server, turn->server_port);
first_filled = TRUE;
if (stream->gathering_started) {
for (citem = component->local_candidates; citem; citem = citem->next) {
NiceCandidateImpl *host_candidate = citem->data;
if (host_candidate->c.type != NICE_CANDIDATE_TYPE_HOST)
continue;
if (nice_address_is_linklocal (&host_candidate->c.addr))
continue;
/* TODO: Add server-reflexive support for TCP candidates */
if (host_candidate->c.transport ==
NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE)
continue;
if (nice_address_ip_version (&host_candidate->c.addr) !=
nice_address_ip_version (&turn->server))
continue;
priv_add_new_candidate_discovery_turn (agent,
host_candidate->sockptr, turn, stream, component->id,
host_candidate->c.transport != NICE_CANDIDATE_TRANSPORT_UDP);
}
}
}
if (agent->discovery_unsched_items)
discovery_schedule (agent);
else
agent_gathering_done (agent);
done:
agent_unlock_and_emit (agent);
g_list_free_full (addresses, g_object_unref);
turn_server_unref (turn);
g_object_unref (component);
g_object_unref (agent);
}
static gboolean
resolve_turn_in_context (NiceAgent *agent, gpointer data)
{
struct TurnResolverData *rd = data;
NiceComponent *component;
GResolver *resolver;
component = g_weak_ref_get (&rd->component_ref);
if (component == NULL) {
g_weak_ref_clear (&rd->component_ref);
turn_server_unref (rd->turn);
g_slice_free (struct TurnResolverData, rd);
return G_SOURCE_REMOVE;
}
resolver = g_resolver_get_default ();
g_main_context_push_thread_default (agent->main_context);
g_resolver_lookup_by_name_async (resolver, rd->turn->server_address,
component->turn_resolving_cancellable, turn_server_resolved_cb,
rd);
g_main_context_pop_thread_default (agent->main_context);
g_object_unref (resolver);
g_object_unref (component);
return G_SOURCE_REMOVE;
}
NICEAPI_EXPORT gboolean
nice_agent_set_relay_info(NiceAgent *agent,
......@@ -3050,7 +3218,7 @@ nice_agent_set_relay_info(NiceAgent *agent,
length = g_list_length (component->turn_servers);
if (length == NICE_CANDIDATE_MAX_TURN_SERVERS) {
g_warning ("Agent %p : cannot have more than %d turn servers.",
g_warning ("Agent %p : cannot have more than %d turn servers per component.",
agent, length);
ret = FALSE;
goto done;
......@@ -3058,11 +3226,6 @@ nice_agent_set_relay_info(NiceAgent *agent,
turn = turn_server_new (server_ip, server_port, username, password, type);
if (!turn) {
ret = FALSE;
goto done;
}
nice_debug ("Agent %p: added relay server [%s]:%d of type %d to s/c %d/%d "
"with user/pass : %s -- %s", agent, server_ip, server_port, type,
stream_id, component_id, username,
......@@ -3075,26 +3238,44 @@ nice_agent_set_relay_info(NiceAgent *agent,
turn->preference = length;
component->turn_servers = g_list_append (component->turn_servers, turn);
if (!nice_address_is_valid (&turn->server)) {
GSource *source = NULL;
struct TurnResolverData *rd = g_slice_new (struct TurnResolverData);
g_weak_ref_init (&rd->component_ref, component);
rd->turn = turn_server_ref (turn);
nice_debug("Agent:%p s:%d/%d: Resolving TURN server %s",
agent, stream_id, component_id, server_ip);
agent_timeout_add_with_context (agent, &source, "TURN resolution", 0,
resolve_turn_in_context, rd);
g_source_unref (source);
}
if (stream->gathering_started) {
GSList *i;
stream->gathering = TRUE;
if (nice_address_is_valid (&turn->server)) {
for (i = component->local_candidates; i; i = i->next) {
NiceCandidateImpl *c = i->data;
if (c->c.type == NICE_CANDIDATE_TYPE_HOST &&
c->c.transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
nice_address_ip_version (&c->c.addr) ==
nice_address_ip_version (&turn->server))
nice_address_ip_version (&turn->server)) {
priv_add_new_candidate_discovery_turn (agent,
c->sockptr, turn, stream, component_id,
c->c.transport != NICE_CANDIDATE_TRANSPORT_UDP);
}
}
if (agent->discovery_unsched_items)
discovery_schedule (agent);
}
}
done:
......@@ -3480,6 +3661,7 @@ nice_agent_gather_candidates (
GSList *local_addresses = NULL;
gboolean ret = TRUE;
guint length;
gboolean resolving_turn = FALSE;
g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE);
g_return_val_if_fail (stream_id >= 1, FALSE);
......@@ -3658,6 +3840,12 @@ nice_agent_gather_candidates (
for (item = component->turn_servers; item; item = item->next) {
TurnServer *turn = item->data;
if (!nice_address_is_valid (&turn->server)) {
if (!turn->resolution_failed)
resolving_turn = TRUE;
continue;
}
if (host_ip_version != nice_address_ip_version (&turn->server)) {
continue;
}
......@@ -3706,6 +3894,7 @@ nice_agent_gather_candidates (
/* note: no async discoveries pending, signal that we are ready */
if (agent->discovery_unsched_items == 0 &&
agent->stun_resolving_list == NULL &&
resolving_turn == FALSE &&
#ifdef HAVE_GUPNP
stream->upnp_mapping == NULL) {
#else
......
......@@ -582,7 +582,7 @@ nice_agent_set_port_range (
* @agent: The #NiceAgent Object
* @stream_id: The ID of the stream
* @component_id: The ID of the component
* @server_ip: The IP address of the TURN server
* @server_ip: The address of the TURN server
* @server_port: The port of the TURN server
* @username: The TURN username to use for the allocate
* @password: The TURN password to use for the allocate
......
......@@ -71,6 +71,7 @@ typedef struct _TurnServer TurnServer;
* TurnServer:
* @ref_count: Reference count for the structure.
* @server: The #NiceAddress of the TURN server
* @server_address: The unresolved server address
* @username: The TURN username
* @password: The TURN password
* @decoded_username: The base64 decoded TURN username
......@@ -87,6 +88,8 @@ struct _TurnServer
gint ref_count;
NiceAddress server;
gchar *server_address;
guint server_port;
gchar *username;
gchar *password;
guint8 *decoded_username;
......@@ -95,6 +98,8 @@ struct _TurnServer
gsize decoded_password_len;
NiceRelayType type;
guint preference;
gboolean resolution_failed;
};
......
......@@ -1164,6 +1164,8 @@ nice_component_init (NiceComponent *component)
component->rfc4571_buffer_size = sizeof (guint16) + G_MAXUINT16;
component->rfc4571_buffer = g_malloc (component->rfc4571_buffer_size);
component->turn_resolving_cancellable = g_cancellable_new ();
}
static void
......@@ -1272,6 +1274,9 @@ nice_component_finalize (GObject *obj)
g_list_free_full (cmp->valid_candidates,
(GDestroyNotify) nice_candidate_free);
g_cancellable_cancel (cmp->turn_resolving_cancellable);
g_clear_object (&cmp->turn_resolving_cancellable);
g_clear_object (&cmp->tcp);
g_clear_object (&cmp->stop_cancellable);
g_clear_object (&cmp->iostream);
......@@ -1557,17 +1562,16 @@ TurnServer *
turn_server_new (const gchar *server_ip, guint server_port,
const gchar *username, const gchar *password, NiceRelayType type)
{
TurnServer *turn = g_slice_new (TurnServer);
TurnServer *turn = g_slice_new0 (TurnServer);
nice_address_init (&turn->server);
turn->ref_count = 1;
if (nice_address_set_from_string (&turn->server, server_ip)) {
turn->server_port = server_port;
if (nice_address_set_from_string (&turn->server, server_ip))
nice_address_set_port (&turn->server, server_port);
} else {
g_slice_free (TurnServer, turn);
return NULL;
}
else
turn->server_address = g_strdup (server_ip);
turn->username = g_strdup (username);
turn->password = g_strdup (password);
turn->decoded_username =
......@@ -1593,6 +1597,7 @@ turn_server_unref (TurnServer *turn)
turn->ref_count--;
if (turn->ref_count == 0) {
g_free (turn->server_address);
g_free (turn->username);
g_free (turn->password);
g_free (turn->decoded_username);
......@@ -1601,6 +1606,28 @@ turn_server_unref (TurnServer *turn)
}
}
TurnServer *
turn_server_copy (TurnServer *turn)
{
TurnServer *copy = g_slice_new0 (TurnServer);
copy->ref_count = 1;
copy->server = turn->server;
copy->server_address = g_strdup (turn->server_address);
copy->username = g_strdup (turn->username);
copy->password = g_strdup (turn->password);
copy->decoded_username = g_memdup (turn->decoded_username,
turn->decoded_username_len);
copy->decoded_password = g_memdup (turn->decoded_password,
turn->decoded_password_len);
copy->decoded_username_len = turn->decoded_username_len;
copy->decoded_password_len = turn->decoded_password_len;
copy->type = turn->type;
copy->preference = turn->preference;
return copy;
}
void
nice_component_add_valid_candidate (NiceAgent *agent, NiceComponent *component,
const NiceCandidate *candidate)
......@@ -1706,3 +1733,21 @@ nice_component_compute_rfc4571_headroom (NiceComponent *component)
{
return component->rfc4571_buffer_offset - component->rfc4571_frame_offset;
}
gboolean
nice_component_resolving_turn (NiceComponent *component)
{
GList *item;
for (item = component->turn_servers; item; item = item->next) {
TurnServer *turn = item->data;
if (turn->resolution_failed)
continue;
if (!nice_address_is_valid (&turn->server))
return TRUE;
}
return FALSE;
}
......@@ -251,6 +251,9 @@ struct _NiceComponent {
guint rfc4571_consumed_size;
NiceAddress rfc4571_remote_addr;
gboolean rfc4571_wakeup_needed;
/* TURN resolution */
GCancellable *turn_resolving_cancellable;
};
typedef struct {
......@@ -328,6 +331,9 @@ TurnServer *
turn_server_new (const gchar *server_ip, guint server_port,
const gchar *username, const gchar *password, NiceRelayType type);
TurnServer *
turn_server_copy (TurnServer *turn);
TurnServer *
turn_server_ref (TurnServer *turn);
......@@ -348,6 +354,10 @@ nice_component_get_sockets (NiceComponent *component);
guint
nice_component_compute_rfc4571_headroom (NiceComponent *component);
gboolean
nice_component_resolving_turn (NiceComponent *component);
G_END_DECLS
#endif /* _NICE_COMPONENT_H */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment