Commit 02ff6c5f authored by Youness Alaoui's avatar Youness Alaoui
Browse files

Add UPnP support to libnice

parent c4968e09
......@@ -12,12 +12,13 @@ AM_CFLAGS = \
-DG_LOG_DOMAIN=\"libnice\" \
$(ERROR_CFLAGS) \
$(GLIB_CFLAGS) \
$(GUPNP_CFLAGS) \
-I $(top_srcdir) \
-I $(top_srcdir)/random \
-I $(top_srcdir)/socket \
-I $(top_srcdir)/stun
COMMON_LDADD = libagent.la $(GLIB_LIBS)
COMMON_LDADD = libagent.la $(GLIB_LIBS) (GUPNP_LIBS)
dist_noinst_DATA = agent-signals-marshal.list
noinst_LTLIBRARIES = libagent.la
......
......@@ -41,6 +41,13 @@
/* note: this is a private header part of agent.h */
#ifdef HAVE_CONFIG_H
# include <config.h>
#else
#define NICEAPI_EXPORT
#endif
#include <glib.h>
#include "agent.h"
......@@ -53,6 +60,10 @@
#include "stun/usages/turn.h"
#include "stun/usages/ice.h"
#ifdef HAVE_GUPNP
#include <libgupnp-igd/gupnp-simple-igd.h>
#endif
/* XXX: starting from ICE ID-18, Ta SHOULD now be set according
* to session bandwidth -> this is not yet implemented in NICE */
......@@ -101,6 +112,13 @@ struct _NiceAgent
GStaticRecMutex mutex; /* Mutex used for thread-safe lib */
NiceCompatibility compatibility; /* property: Compatibility mode */
StunAgent stun_agent; /* STUN agent */
#ifdef HAVE_GUPNP
GUPnPSimpleIgd* upnp; /* GUPnP Single IGD agent */
gboolean upnp_enabled; /* whether UPnP discovery is enabled */
guint upnp_timeout; /* UPnP discovery timeout */
GSList *upnp_mapping; /* list of Candidates being mapped */
GSource *upnp_timer_source; /* source of upnp timeout timer */
#endif
/* XXX: add pointer to internal data struct for ABI-safe extensions */
};
......
......@@ -94,7 +94,11 @@ enum
PROP_PROXY_IP,
PROP_PROXY_PORT,
PROP_PROXY_USERNAME,
PROP_PROXY_PASSWORD
PROP_PROXY_PASSWORD,
#ifdef HAVE_GUPNP
PROP_UPNP,
PROP_UPNP_TIMEOUT
#endif
};
......@@ -116,6 +120,8 @@ static gboolean priv_attach_stream_component (NiceAgent *agent,
Component *component);
static void priv_detach_stream_component (Stream *stream, Component *component);
static void priv_free_upnp (NiceAgent *agent);
StunUsageIceCompatibility
agent_to_ice_compatibility (NiceAgent *agent)
{
......@@ -292,7 +298,7 @@ nice_agent_class_init (NiceAgentClass *klass)
"stun-pacing-timer",
"STUN pacing timer",
"Timer 'Ta' (msecs) used in the IETF ICE specification for pacing candidate gathering and sending of connectivity checks",
1, 0xffffffff,
1, 0xffffffff,
NICE_AGENT_TIMER_TA_DEFAULT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
......@@ -302,7 +308,7 @@ nice_agent_class_init (NiceAgentClass *klass)
"max-connectivity-checks",
"Maximum number of connectivity checks",
"Upper limit for the total number of connectivity checks performed",
0, 0xffffffff,
0, 0xffffffff,
0, /* default set in init */
G_PARAM_READWRITE));
......@@ -348,6 +354,27 @@ nice_agent_class_init (NiceAgentClass *klass)
NULL,
G_PARAM_READWRITE));
#ifdef HAVE_GUPNP
g_object_class_install_property (gobject_class, PROP_UPNP,
g_param_spec_boolean (
"upnp",
"Use UPnP",
"Whether the agent should use UPnP to open a port in the router and "
"get the external IP",
TRUE, /* enable UPnP by default */
G_PARAM_READWRITE| G_PARAM_CONSTRUCT));
g_object_class_install_property (gobject_class, PROP_UPNP_TIMEOUT,
g_param_spec_uint (
"upnp-timeout",
"Timeout for UPnP discovery",
"The maximum amount of time to wait for UPnP discovery to finish before "
"signaling the candidate-gathering-done signal",
100, 60000,
2000,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
#endif
/* install signals */
/**
......@@ -603,6 +630,16 @@ nice_agent_get_property (
g_value_set_string (value, agent->proxy_password);
break;
#ifdef HAVE_GUPNP
case PROP_UPNP:
g_value_set_boolean (value, agent->upnp_enabled);
break;
case PROP_UPNP_TIMEOUT:
g_value_set_uint (value, agent->upnp_timeout);
break;
#endif
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
......@@ -698,6 +735,15 @@ nice_agent_set_property (
agent->proxy_password = g_value_dup_string (value);
break;
#ifdef HAVE_GUPNP
case PROP_UPNP_TIMEOUT:
agent->upnp_timeout = g_value_get_uint (value);
break;
case PROP_UPNP:
agent->upnp_enabled = g_value_get_boolean (value);
break;
#endif
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
......@@ -744,7 +790,10 @@ void agent_gathering_done (NiceAgent *agent)
}
}
agent_signal_gathering_done (agent);
if (agent->discovery_timer_source == NULL &&
agent->upnp_timer_source == NULL) {
agent_signal_gathering_done (agent);
}
}
void agent_signal_gathering_done (NiceAgent *agent)
......@@ -932,7 +981,6 @@ priv_add_new_candidate_discovery_turn (NiceAgent *agent,
socket = nice_http_socket_new (socket, &turn->server,
agent->proxy_username, agent->proxy_password);
} else {
/* TODO add HTTP support */
nice_socket_free (socket);
socket = NULL;
}
......@@ -1077,6 +1125,136 @@ nice_agent_set_relay_info(NiceAgent *agent,
return TRUE;
}
#ifdef HAVE_GUPNP
static gboolean priv_upnp_timeout_cb (gpointer user_data)
{
NiceAgent *agent = (NiceAgent*)user_data;
GSList *i;
g_static_rec_mutex_lock (&agent->mutex);
nice_debug ("Agent %p : UPnP port mapping timed out", agent);
for (i = agent->upnp_mapping; i; i = i->next) {
NiceAddress *a = i->data;
nice_address_free (a);
}
g_slist_free (agent->upnp_mapping);
agent->upnp_mapping = NULL;
if (agent->upnp_timer_source != NULL) {
g_source_destroy (agent->upnp_timer_source);
g_source_unref (agent->upnp_timer_source);
agent->upnp_timer_source = NULL;
}
agent_gathering_done (agent);
g_static_rec_mutex_unlock (&agent->mutex);
return FALSE;
}
static void _upnp_mapped_external_port (GUPnPSimpleIgd *self, gchar *proto,
gchar *external_ip, gchar *replaces_external_ip, guint external_port,
gchar *local_ip, guint local_port, gchar *description, gpointer user_data)
{
NiceAgent *agent = (NiceAgent*)user_data;
NiceAddress localaddr;
NiceAddress externaddr;
GSList *i, *j, *k;
g_static_rec_mutex_lock (&agent->mutex);
nice_debug ("Agent %p : Sucessfully mapped %s:%d to %s:%d", agent, local_ip,
local_port, external_ip, external_port);
nice_address_set_from_string (&localaddr, local_ip);
nice_address_set_port (&localaddr, local_port);
nice_address_set_from_string (&externaddr, external_ip);
nice_address_set_port (&externaddr, external_port);
for (i = agent->upnp_mapping; i; i = i->next) {
NiceAddress *addr = i->data;
if (nice_address_equal (&localaddr, addr)) {
agent->upnp_mapping = g_slist_remove (agent->upnp_mapping, addr);
nice_address_free (addr);
break;
}
}
for (i = agent->streams; i; i = i->next) {
Stream *stream = i->data;
for (j = stream->components; j; j = j->next) {
Component *component = j->data;
for (k = component->local_candidates; k; k = k->next) {
NiceCandidate *local_candidate = k->data;
if (nice_address_equal (&localaddr, &local_candidate->base_addr)) {
discovery_add_server_reflexive_candidate (
agent,
stream->id,
component->id,
&externaddr,
local_candidate->sockptr);
goto end;
}
}
}
}
end:
if (g_slist_length (agent->upnp_mapping)) {
if (agent->upnp_timer_source != NULL) {
g_source_destroy (agent->upnp_timer_source);
g_source_unref (agent->upnp_timer_source);
agent->upnp_timer_source = NULL;
}
agent_gathering_done (agent);
}
g_static_rec_mutex_unlock (&agent->mutex);
}
static void _upnp_error_mapping_port (GUPnPSimpleIgd *self, GError *error,
gchar *proto, guint external_port, gchar *local_ip, guint local_port,
gchar *description, gpointer user_data)
{
NiceAgent *agent = (NiceAgent*)user_data;
NiceAddress localaddr;
GSList *i;
g_static_rec_mutex_lock (&agent->mutex);
nice_debug ("Agent %p : Error mapping %s:%d to %d (%d) : %s", agent, local_ip,
local_port, external_port, error->domain, error->message);
nice_address_set_from_string (&localaddr, local_ip);
nice_address_set_port (&localaddr, local_port);
for (i = agent->upnp_mapping; i; i = i->next) {
NiceAddress *addr = i->data;
if (nice_address_equal (&localaddr, addr)) {
agent->upnp_mapping = g_slist_remove (agent->upnp_mapping, addr);
nice_address_free (addr);
break;
}
}
if (g_slist_length (agent->upnp_mapping)) {
if (agent->upnp_timer_source != NULL) {
g_source_destroy (agent->upnp_timer_source);
g_source_unref (agent->upnp_timer_source);
agent->upnp_timer_source = NULL;
}
agent_gathering_done (agent);
}
g_static_rec_mutex_unlock (&agent->mutex);
}
#endif
NICEAPI_EXPORT void
nice_agent_gather_candidates (
......@@ -1097,6 +1275,31 @@ nice_agent_gather_candidates (
nice_debug ("Agent %p : In %s mode, starting candidate gathering.", agent,
agent->full_mode ? "ICE-FULL" : "ICE-LITE");
#ifdef HAVE_GUPNP
priv_free_upnp (agent);
if (agent->upnp_enabled) {
agent->upnp = gupnp_simple_igd_new (agent->main_context);
agent->upnp_timer_source = agent_timeout_add_with_context (agent,
agent->upnp_timeout, priv_upnp_timeout_cb, agent);
g_object_set (agent->upnp, "request-timeout", 1, NULL);
if (agent->upnp) {
g_signal_connect (agent->upnp, "mapped-external-port",
G_CALLBACK (_upnp_mapped_external_port), agent);
g_signal_connect (agent->upnp, "error-mapping-port",
G_CALLBACK (_upnp_error_mapping_port), agent);
} else {
nice_debug ("Agent %p : Error creating UPnP Simple IGD agent", agent);
}
} else {
nice_debug ("Agent %p : UPnP property Disabled", agent);
}
#else
nice_debug ("Agent %p : libnice compiled without UPnP support", agent);
#endif
/* if no local addresses added, generate them ourselves */
if (agent->local_addresses == NULL) {
GList *addresses = nice_interfaces_get_local_ips (FALSE);
......@@ -1120,6 +1323,11 @@ nice_agent_gather_candidates (
NiceAddress *addr = i->data;
NiceCandidate *host_candidate;
#ifdef HAVE_GUPNP
gchar local_ip[NICE_ADDRESS_STRING_LEN];
nice_address_to_string (addr, local_ip);
#endif
for (n = 0; n < stream->n_components; n++) {
Component *component = stream_find_component_by_id (stream, n + 1);
host_candidate = discovery_add_local_host_candidate (agent, stream->id,
......@@ -1130,11 +1338,23 @@ nice_agent_gather_candidates (
break;
}
#ifdef HAVE_GUPNP
if (agent->upnp_enabled) {
NiceAddress *addr = nice_address_dup (&host_candidate->base_addr);
nice_debug ("Agent %p: Adding UPnP port %s:%d", agent, local_ip,
nice_address_get_port (&host_candidate->base_addr));
gupnp_simple_igd_add_port (agent->upnp, "UDP",
nice_address_get_port (&host_candidate->base_addr),
local_ip, 0, 6000, PACKAGE_STRING);
agent->upnp_mapping = g_slist_prepend (agent->upnp_mapping, 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)) {
gboolean res;
gboolean res;
nice_address_set_port (&stun_server, agent->stun_server_port);
res =
......@@ -1191,6 +1411,31 @@ nice_agent_gather_candidates (
g_static_rec_mutex_unlock (&agent->mutex);
}
static void priv_free_upnp (NiceAgent *agent)
{
GSList *i;
#ifdef HAVE_GUPNP
if (agent->upnp) {
g_object_unref (agent->upnp);
agent->upnp = NULL;
}
for (i = agent->upnp_mapping; i; i = i->next) {
NiceAddress *a = i->data;
nice_address_free (a);
}
g_slist_free (agent->upnp_mapping);
agent->upnp_mapping = NULL;
if (agent->upnp_timer_source != NULL) {
g_source_destroy (agent->upnp_timer_source);
g_source_unref (agent->upnp_timer_source);
agent->upnp_timer_source = NULL;
}
#endif
}
static void priv_remove_keepalive_timer (NiceAgent *agent)
{
if (agent->keepalive_timer_source != NULL) {
......@@ -1660,6 +1905,7 @@ nice_agent_restart (
return res;
}
static void
nice_agent_dispose (GObject *object)
{
......@@ -1703,6 +1949,8 @@ nice_agent_dispose (GObject *object)
nice_rng_free (agent->rng);
agent->rng = NULL;
priv_free_upnp (agent);
if (G_OBJECT_CLASS (nice_agent_parent_class)->dispose)
G_OBJECT_CLASS (nice_agent_parent_class)->dispose (object);
......
......@@ -102,7 +102,7 @@ void discovery_free (NiceAgent *agent)
/*
* Prunes the list of discovery processes for items related
* to stream 'stream_id'.
* to stream 'stream_id'.
*
* @return TRUE on success, FALSE on a fatal error
*/
......@@ -316,7 +316,7 @@ static void priv_assign_foundation (NiceAgent *agent, NiceCandidate *candidate)
}
}
}
g_snprintf (candidate->foundation, NICE_CANDIDATE_MAX_FOUNDATION, "%u", agent->next_candidate_id++);
}
......@@ -436,7 +436,7 @@ NiceCandidate *discovery_add_local_host_candidate (
if (udp_socket)
nice_socket_free (udp_socket);
}
return candidate;
}
......@@ -446,7 +446,7 @@ NiceCandidate *discovery_add_local_host_candidate (
*
* @return pointer to the created candidate, or NULL on error
*/
NiceCandidate*
NiceCandidate*
discovery_add_server_reflexive_candidate (
NiceAgent *agent,
guint stream_id,
......
......@@ -66,7 +66,6 @@ AC_CHECK_LIB(rt, clock_gettime, [LIBRT="-lrt"], [LIBRT=""])
AC_CHECK_FUNCS([poll])
AC_SUBST(LIBRT)
PKG_CHECK_MODULES(GLIB, [dnl
glib-2.0 >= 2.10 dnl
gobject-2.0 >= 2.10 dnl
......@@ -102,6 +101,31 @@ AC_SUBST(gstplugindir)
AM_CONDITIONAL(WITH_GSTREAMER, test "$with_gstreamer" = yes)
AC_ARG_ENABLE([gupnp],
AC_HELP_STRING([--disable-gupnp], [Disable GUPnP IGD support]),
[case "${enableval}" in
yes) WANT_GUPNP=yes ;;
no) WANT_GUPNP=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-gupnp) ;;
esac],
WANT_GUPNP=test)
HAVE_GUPNP=no
if test "x$WANT_GUPNP" != "xno"; then
PKG_CHECK_MODULES(GUPNP, [ gupnp-igd-1.0 ],
[ HAVE_GUPNP=yes ],
[ HAVE_GUPNP=no ])
fi
if test "x$WANT_GUPNP" = "xyes" && test "x$HAVE_GUPNP" = "xno"; then
AC_ERROR([Requested GUPnP IGD, but it is not available])
fi
if test "x$HAVE_GUPNP" = "xyes"; then
AC_DEFINE(HAVE_GUPNP,,[Have the GUPnP IGD library])
fi
AC_SUBST(HAVE_GUPNP)
dnl Test coverage
AC_ARG_ENABLE([coverage],
[AS_HELP_STRING([--enable-coverage],
......
......@@ -12,6 +12,7 @@ AM_CFLAGS = \
-DG_LOG_DOMAIN=\"libnice-socket\" \
$(ERROR_CFLAGS) \
$(GLIB_CFLAGS) \
$(GUPNP_CFLAGS) \
-I $(top_srcdir)/random \
-I $(top_srcdir)/agent \
-I $(top_srcdir)/
......
......@@ -11,13 +11,14 @@ include $(top_srcdir)/common.mk
AM_CFLAGS = \
$(ERROR_CFLAGS) \
$(GLIB_CFLAGS) \
$(GUPNP_CFLAGS) \
-I $(top_srcdir) \
-I $(top_srcdir)/agent \
-I $(top_srcdir)/random \
-I $(top_srcdir)/socket \
-I $(top_srcdir)/stun
COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS)
COMMON_LDADD = $(top_builddir)/agent/libagent.la $(top_builddir)/socket/libsocket.la $(GLIB_LIBS) $(GUPNP_LIBS)
check_PROGRAMS = \
test-bsd \
......
......@@ -214,6 +214,8 @@ int main (void)
g_object_set (G_OBJECT (lagent), "controlling-mode", TRUE, NULL);
g_object_set (G_OBJECT (ragent), "controlling-mode", FALSE, NULL);
g_object_set (G_OBJECT (lagent), "upnp", FALSE, NULL);
g_object_set (G_OBJECT (ragent), "upnp", FALSE, NULL);
/* step: add a timer to catch state changes triggered by signals */
timer_id = g_timeout_add (30000, timer_cb, NULL);
......
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