diff options
author | Olivier CrĂȘte <olivier.crete@collabora.com> | 2023-01-01 18:29:50 -0500 |
---|---|---|
committer | Olivier CrĂȘte <olivier.crete@collabora.com> | 2023-01-04 14:40:31 -0500 |
commit | b9da8180372bd45aad31231faa63bea34ab67572 (patch) | |
tree | 9fb2cf8abadb712b8c7ec9f98704bcfc2e568419 | |
parent | 24eecfd7f7726f2fc93ed7ac2f03ff5ab36498d4 (diff) | |
download | libnice-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.c | 229 | ||||
-rw-r--r-- | agent/agent.h | 2 | ||||
-rw-r--r-- | agent/candidate-priv.h | 7 | ||||
-rw-r--r-- | agent/component.c | 57 | ||||
-rw-r--r-- | agent/component.h | 10 |
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 */ |