summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier CrĂȘte <olivier.crete@collabora.com>2023-01-01 18:29:50 -0500
committerOlivier CrĂȘte <olivier.crete@collabora.com>2023-01-04 14:40:31 -0500
commitb9da8180372bd45aad31231faa63bea34ab67572 (patch)
tree9fb2cf8abadb712b8c7ec9f98704bcfc2e568419
parent24eecfd7f7726f2fc93ed7ac2f03ff5ab36498d4 (diff)
downloadlibnice-b9da8180372bd45aad31231faa63bea34ab67572.tar.gz
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.
-rw-r--r--agent/agent.c229
-rw-r--r--agent/agent.h2
-rw-r--r--agent/candidate-priv.h7
-rw-r--r--agent/component.c57
-rw-r--r--agent/component.h10
5 files changed, 277 insertions, 28 deletions
diff --git a/agent/agent.c b/agent/agent.c
index 1be18aa..8994906 100644
--- a/agent/agent.c
+++ b/agent/agent.c
@@ -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,25 +3238,43 @@ nice_agent_set_relay_info(NiceAgent *agent,
turn->preference = length;
component->turn_servers = g_list_append (component->turn_servers, turn);
- if (stream->gathering_started) {
+ 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;
- for (i = component->local_candidates; i; i = i->next) {
- NiceCandidateImpl *c = i->data;
+ 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)) {
+ priv_add_new_candidate_discovery_turn (agent,
+ c->sockptr, turn, stream, component_id,
+ c->c.transport != NICE_CANDIDATE_TRANSPORT_UDP);
+ }
+ }
- 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))
- 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);
}
-
- if (agent->discovery_unsched_items)
- discovery_schedule (agent);
}
@@ -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
diff --git a/agent/agent.h b/agent/agent.h
index 1746b62..a3bdeb3 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -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
diff --git a/agent/candidate-priv.h b/agent/candidate-priv.h
index e6e2529..96e16f1 100644
--- a/agent/candidate-priv.h
+++ b/agent/candidate-priv.h
@@ -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;
};
@@ -122,4 +127,4 @@ struct _NiceCandidateImpl
G_END_DECLS
-#endif /* __LIBNICE_CANDIDATE_H__ */ \ No newline at end of file
+#endif /* __LIBNICE_CANDIDATE_H__ */
diff --git a/agent/component.c b/agent/component.c
index b19c50f..cb7d098 100644
--- a/agent/component.c
+++ b/agent/component.c
@@ -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;
+}
diff --git a/agent/component.h b/agent/component.h
index 1dcd57b..de80ad4 100644
--- a/agent/component.h
+++ b/agent/component.h
@@ -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 {
@@ -329,6 +332,9 @@ 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);
void
@@ -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 */