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) { ...@@ -2292,6 +2292,7 @@ _transport_to_string (NiceCandidateTransport type) {
void agent_gathering_done (NiceAgent *agent) void agent_gathering_done (NiceAgent *agent)
{ {
gboolean upnp_running = FALSE; gboolean upnp_running = FALSE;
gboolean dns_resolution_ongoing = FALSE;
GSList *i, *j, *k, *l, *m; GSList *i, *j, *k, *l, *m;
if (agent->stun_resolving_list) { if (agent->stun_resolving_list) {
...@@ -2319,6 +2320,11 @@ void agent_gathering_done (NiceAgent *agent) ...@@ -2319,6 +2320,11 @@ void agent_gathering_done (NiceAgent *agent)
for (j = stream->components; j; j = j->next) { for (j = stream->components; j; j = j->next) {
NiceComponent *component = j->data; NiceComponent *component = j->data;
if (nice_component_resolving_turn (component)) {
dns_resolution_ongoing = TRUE;
continue;
}
for (k = component->local_candidates; k;) { for (k = component->local_candidates; k;) {
NiceCandidate *local_candidate = k->data; NiceCandidate *local_candidate = k->data;
GSList *next = k->next; GSList *next = k->next;
...@@ -2387,7 +2393,8 @@ next_cand: ...@@ -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); agent_signal_gathering_done (agent);
} }
...@@ -3016,6 +3023,167 @@ nice_agent_add_stream ( ...@@ -3016,6 +3023,167 @@ nice_agent_add_stream (
return ret; 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 NICEAPI_EXPORT gboolean
nice_agent_set_relay_info(NiceAgent *agent, nice_agent_set_relay_info(NiceAgent *agent,
...@@ -3050,7 +3218,7 @@ 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); length = g_list_length (component->turn_servers);
if (length == NICE_CANDIDATE_MAX_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); agent, length);
ret = FALSE; ret = FALSE;
goto done; goto done;
...@@ -3058,11 +3226,6 @@ nice_agent_set_relay_info(NiceAgent *agent, ...@@ -3058,11 +3226,6 @@ nice_agent_set_relay_info(NiceAgent *agent,
turn = turn_server_new (server_ip, server_port, username, password, type); 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 " 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, "with user/pass : %s -- %s", agent, server_ip, server_port, type,
stream_id, component_id, username, stream_id, component_id, username,
...@@ -3075,26 +3238,44 @@ nice_agent_set_relay_info(NiceAgent *agent, ...@@ -3075,26 +3238,44 @@ nice_agent_set_relay_info(NiceAgent *agent,
turn->preference = length; turn->preference = length;
component->turn_servers = g_list_append (component->turn_servers, turn); 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) { if (stream->gathering_started) {
GSList *i; GSList *i;
stream->gathering = TRUE; stream->gathering = TRUE;
if (nice_address_is_valid (&turn->server)) {
for (i = component->local_candidates; i; i = i->next) { for (i = component->local_candidates; i; i = i->next) {
NiceCandidateImpl *c = i->data; NiceCandidateImpl *c = i->data;
if (c->c.type == NICE_CANDIDATE_TYPE_HOST && if (c->c.type == NICE_CANDIDATE_TYPE_HOST &&
c->c.transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE && c->c.transport != NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE &&
nice_address_ip_version (&c->c.addr) == 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, priv_add_new_candidate_discovery_turn (agent,
c->sockptr, turn, stream, component_id, c->sockptr, turn, stream, component_id,
c->c.transport != NICE_CANDIDATE_TRANSPORT_UDP); c->c.transport != NICE_CANDIDATE_TRANSPORT_UDP);
} }
}
if (agent->discovery_unsched_items) if (agent->discovery_unsched_items)
discovery_schedule (agent); discovery_schedule (agent);
} }
}
done: done:
...@@ -3480,6 +3661,7 @@ nice_agent_gather_candidates ( ...@@ -3480,6 +3661,7 @@ nice_agent_gather_candidates (
GSList *local_addresses = NULL; GSList *local_addresses = NULL;
gboolean ret = TRUE; gboolean ret = TRUE;
guint length; guint length;
gboolean resolving_turn = FALSE;
g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE); g_return_val_if_fail (NICE_IS_AGENT (agent), FALSE);
g_return_val_if_fail (stream_id >= 1, FALSE); g_return_val_if_fail (stream_id >= 1, FALSE);
...@@ -3658,6 +3840,12 @@ nice_agent_gather_candidates ( ...@@ -3658,6 +3840,12 @@ nice_agent_gather_candidates (
for (item = component->turn_servers; item; item = item->next) { for (item = component->turn_servers; item; item = item->next) {
TurnServer *turn = item->data; 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)) { if (host_ip_version != nice_address_ip_version (&turn->server)) {
continue; continue;
} }
...@@ -3706,6 +3894,7 @@ nice_agent_gather_candidates ( ...@@ -3706,6 +3894,7 @@ nice_agent_gather_candidates (
/* note: no async discoveries pending, signal that we are ready */ /* note: no async discoveries pending, signal that we are ready */
if (agent->discovery_unsched_items == 0 && if (agent->discovery_unsched_items == 0 &&
agent->stun_resolving_list == NULL && agent->stun_resolving_list == NULL &&
resolving_turn == FALSE &&
#ifdef HAVE_GUPNP #ifdef HAVE_GUPNP
stream->upnp_mapping == NULL) { stream->upnp_mapping == NULL) {
#else #else
......
...@@ -582,7 +582,7 @@ nice_agent_set_port_range ( ...@@ -582,7 +582,7 @@ nice_agent_set_port_range (
* @agent: The #NiceAgent Object * @agent: The #NiceAgent Object
* @stream_id: The ID of the stream * @stream_id: The ID of the stream
* @component_id: The ID of the component * @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 * @server_port: The port of the TURN server
* @username: The TURN username to use for the allocate * @username: The TURN username to use for the allocate
* @password: The TURN password to use for the allocate * @password: The TURN password to use for the allocate
......
...@@ -71,6 +71,7 @@ typedef struct _TurnServer TurnServer; ...@@ -71,6 +71,7 @@ typedef struct _TurnServer TurnServer;
* TurnServer: * TurnServer:
* @ref_count: Reference count for the structure. * @ref_count: Reference count for the structure.
* @server: The #NiceAddress of the TURN server * @server: The #NiceAddress of the TURN server
* @server_address: The unresolved server address
* @username: The TURN username * @username: The TURN username
* @password: The TURN password * @password: The TURN password
* @decoded_username: The base64 decoded TURN username * @decoded_username: The base64 decoded TURN username
...@@ -87,6 +88,8 @@ struct _TurnServer ...@@ -87,6 +88,8 @@ struct _TurnServer
gint ref_count; gint ref_count;
NiceAddress server; NiceAddress server;
gchar *server_address;
guint server_port;
gchar *username; gchar *username;
gchar *password; gchar *password;
guint8 *decoded_username; guint8 *decoded_username;
...@@ -95,6 +98,8 @@ struct _TurnServer ...@@ -95,6 +98,8 @@ struct _TurnServer
gsize decoded_password_len; gsize decoded_password_len;
NiceRelayType type; NiceRelayType type;
guint preference; guint preference;
gboolean resolution_failed;
}; };
......
...@@ -1164,6 +1164,8 @@ nice_component_init (NiceComponent *component) ...@@ -1164,6 +1164,8 @@ nice_component_init (NiceComponent *component)
component->rfc4571_buffer_size = sizeof (guint16) + G_MAXUINT16; component->rfc4571_buffer_size = sizeof (guint16) + G_MAXUINT16;
component->rfc4571_buffer = g_malloc (component->rfc4571_buffer_size); component->rfc4571_buffer = g_malloc (component->rfc4571_buffer_size);
component->turn_resolving_cancellable = g_cancellable_new ();
} }
static void static void
...@@ -1272,6 +1274,9 @@ nice_component_finalize (GObject *obj) ...@@ -1272,6 +1274,9 @@ nice_component_finalize (GObject *obj)
g_list_free_full (cmp->valid_candidates, g_list_free_full (cmp->valid_candidates,
(GDestroyNotify) nice_candidate_free); (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->tcp);
g_clear_object (&cmp->stop_cancellable); g_clear_object (&cmp->stop_cancellable);
g_clear_object (&cmp->iostream); g_clear_object (&cmp->iostream);
...@@ -1557,17 +1562,16 @@ TurnServer * ...@@ -1557,17 +1562,16 @@ TurnServer *
turn_server_new (const gchar *server_ip, guint server_port, turn_server_new (const gchar *server_ip, guint server_port,
const gchar *username, const gchar *password, NiceRelayType type) 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); nice_address_init (&turn->server);
turn->ref_count = 1; 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); nice_address_set_port (&turn->server, server_port);
} else { else
g_slice_free (TurnServer, turn); turn->server_address = g_strdup (server_ip);
return NULL;
}
turn->username = g_strdup (username); turn->username = g_strdup (username);
turn->password = g_strdup (password); turn->password = g_strdup (password);
turn->decoded_username = turn->decoded_username =
...@@ -1593,6 +1597,7 @@ turn_server_unref (TurnServer *turn) ...@@ -1593,6 +1597,7 @@ turn_server_unref (TurnServer *turn)
turn->ref_count--; turn->ref_count--;
if (turn->ref_count == 0) { if (turn->ref_count == 0) {
g_free (turn->server_address);
g_free (turn->username); g_free (turn->username);
g_free (turn->password); g_free (turn->password);
g_free (turn->decoded_username); g_free (turn->decoded_username);
...@@ -1601,6 +1606,28 @@ turn_server_unref (TurnServer *turn) ...@@ -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 void
nice_component_add_valid_candidate (NiceAgent *agent, NiceComponent *component, nice_component_add_valid_candidate (NiceAgent *agent, NiceComponent *component,
const NiceCandidate *candidate) const NiceCandidate *candidate)
...@@ -1706,3 +1733,21 @@ nice_component_compute_rfc4571_headroom (NiceComponent *component) ...@@ -1706,3 +1733,21 @@ nice_component_compute_rfc4571_headroom (NiceComponent *component)
{ {
return component->rfc4571_buffer_offset - component->rfc4571_frame_offset; 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 { ...@@ -251,6 +251,9 @@ struct _NiceComponent {
guint rfc4571_consumed_size; guint rfc4571_consumed_size;
NiceAddress rfc4571_remote_addr; NiceAddress rfc4571_remote_addr;
gboolean rfc4571_wakeup_needed; gboolean rfc4571_wakeup_needed;
/* TURN resolution */
GCancellable *turn_resolving_cancellable;
}; };
typedef struct { typedef struct {
...@@ -328,6 +331,9 @@ TurnServer * ...@@ -328,6 +331,9 @@ TurnServer *
turn_server_new (const gchar *server_ip, guint server_port, turn_server_new (const gchar *server_ip, guint server_port,
const gchar *username, const gchar *password, NiceRelayType type); const gchar *username, const gchar *password, NiceRelayType type);
TurnServer *
turn_server_copy (TurnServer *turn);
TurnServer * TurnServer *
turn_server_ref (TurnServer *turn); turn_server_ref (TurnServer *turn);
...@@ -348,6 +354,10 @@ nice_component_get_sockets (NiceComponent *component); ...@@ -348,6 +354,10 @@ nice_component_get_sockets (NiceComponent *component);
guint guint
nice_component_compute_rfc4571_headroom (NiceComponent *component); nice_component_compute_rfc4571_headroom (NiceComponent *component);
gboolean
nice_component_resolving_turn (NiceComponent *component);
G_END_DECLS G_END_DECLS
#endif /* _NICE_COMPONENT_H */ #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