From e708543039b25b64597e2a69479d09de69af6f31 Mon Sep 17 00:00:00 2001 From: Johan Sternerup Date: Mon, 31 Aug 2020 14:04:54 +0200 Subject: [PATCH] webrtcbin: Add settings for HTTP proxy Pass this to libnice which has a simple HTTP 1.0 proxy with basic authentication only. Part-of: --- .../docs/plugins/gst_plugins_cache.json | 12 ++ .../gst-plugins-bad/ext/webrtc/gstwebrtcbin.c | 24 ++++ .../gst-plugins-bad/gst-libs/gst/webrtc/ice.c | 41 ++++++ .../gst-plugins-bad/gst-libs/gst/webrtc/ice.h | 34 +++++ .../gst-libs/gst/webrtc/nice/nice.c | 132 ++++++++++++++++++ 5 files changed, 243 insertions(+) diff --git a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json index 243de9f44d..30a8ccfe78 100644 --- a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json @@ -236708,6 +236708,18 @@ "type": "GstWebRTCSessionDescription", "writable": false }, + "http-proxy": { + "blurb": "A HTTP proxy for use with TURN/TCP of the form http://[username:password@]hostname[:port]", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "NULL", + "mutable": "null", + "readable": true, + "type": "gchararray", + "writable": true + }, "ice-agent": { "blurb": "The WebRTC ICE agent", "conditionally-available": false, diff --git a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c index 5d59ee6959..870b0b28c0 100644 --- a/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c +++ b/subprojects/gst-plugins-bad/ext/webrtc/gstwebrtcbin.c @@ -533,6 +533,7 @@ enum PROP_ICE_AGENT, PROP_LATENCY, PROP_SCTP_TRANSPORT, + PROP_HTTP_PROXY }; static guint gst_webrtc_bin_signals[LAST_SIGNAL] = { 0 }; @@ -8058,6 +8059,10 @@ gst_webrtc_bin_set_property (GObject * object, guint prop_id, case PROP_ICE_AGENT: webrtc->priv->ice = g_value_get_object (value); break; + case PROP_HTTP_PROXY: + gst_webrtc_ice_set_http_proxy (webrtc->priv->ice, + g_value_get_string (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -8135,6 +8140,10 @@ gst_webrtc_bin_get_property (GObject * object, guint prop_id, case PROP_SCTP_TRANSPORT: g_value_set_object (value, webrtc->priv->sctp_transport); break; + case PROP_HTTP_PROXY: + g_value_take_string (value, + gst_webrtc_ice_get_http_proxy (webrtc->priv->ice)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -8419,6 +8428,21 @@ gst_webrtc_bin_class_init (GstWebRTCBinClass * klass) 0, G_MAXUINT, DEFAULT_JB_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstWebRTCBin:http-proxy: + * + * A HTTP proxy for use with TURN/TCP of the form + * http://[username:password@]hostname[:port] + * + * Since: 1.22 + */ + g_object_class_install_property (gobject_class, + PROP_HTTP_PROXY, + g_param_spec_string ("http-proxy", "HTTP Proxy", + "A HTTP proxy for use with TURN/TCP of the form " + "http://[username:password@]hostname[:port]", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** * GstWebRTCBin:sctp-transport: * diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/ice.c b/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/ice.c index f9ae663825..2328d0b82d 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/ice.c +++ b/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/ice.c @@ -452,6 +452,45 @@ gst_webrtc_ice_get_turn_server (GstWebRTCICE * ice) return GST_WEBRTC_ICE_GET_CLASS (ice)->get_turn_server (ice); } +/** + * gst_webrtc_ice_set_http_proxy: + * @ice: The #GstWebRTCICE + * @uri: (transfer none): URI of the HTTP proxy of the form + * http://[username:password@]hostname[:port] + * + * Set HTTP Proxy to be used when connecting to TURN server. + * + * Since: 1.22 + */ +void +gst_webrtc_ice_set_http_proxy (GstWebRTCICE * ice, const gchar * uri_s) +{ + g_return_if_fail (GST_IS_WEBRTC_ICE (ice)); + g_assert (GST_WEBRTC_ICE_GET_CLASS (ice)->set_http_proxy); + + GST_WEBRTC_ICE_GET_CLASS (ice)->set_http_proxy (ice, uri_s); +} + +/** + * gst_webrtc_ice_get_http_proxy: + * @ice: The #GstWebRTCICE + * + * Returns: (transfer full): URI of the HTTP proxy of the form + * http://[username:password@]hostname[:port] + * + * Get HTTP Proxy to be used when connecting to TURN server. + * + * Since: 1.22 + */ +gchar * +gst_webrtc_ice_get_http_proxy (GstWebRTCICE * ice) +{ + g_return_val_if_fail (GST_IS_WEBRTC_ICE (ice), NULL); + g_assert (GST_WEBRTC_ICE_GET_CLASS (ice)->get_http_proxy); + + return GST_WEBRTC_ICE_GET_CLASS (ice)->get_http_proxy (ice); +} + static void gst_webrtc_ice_set_property (GObject * object, guint prop_id, @@ -516,6 +555,8 @@ gst_webrtc_ice_class_init (GstWebRTCICEClass * klass) klass->get_stun_server = NULL; klass->set_turn_server = NULL; klass->get_turn_server = NULL; + klass->get_http_proxy = NULL; + klass->set_http_proxy = NULL; klass->set_tos = NULL; klass->set_on_ice_candidate = NULL; klass->get_local_candidates = NULL; diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/ice.h b/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/ice.h index 38e8bf6ab6..f67889b1f4 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/ice.h +++ b/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/ice.h @@ -106,6 +106,33 @@ struct _GstWebRTCICEClass { void (*set_turn_server) (GstWebRTCICE * ice, const gchar * uri); gchar * (*get_turn_server) (GstWebRTCICE * ice); + + /** + * GstWebRTCICEClass::set_http_proxy: + * @ice: a #GstWebRTCICE + * @uri: (transfer none): URI of the HTTP proxy of the form + * http://[username:password@]hostname[:port] + * + * Set HTTP Proxy to be used when connecting to TURN server. + * + * Since: 1.22 + */ + void (*set_http_proxy) (GstWebRTCICE * ice, + const gchar * uri); + + /** + * GstWebRTCICEClass::get_http_proxy: + * @ice: a #GstWebRTCICE + * + * Get HTTP Proxy to be used when connecting to TURN server. + * + * Returns: (transfer full): URI of the HTTP proxy of the form + * http://[username:password@]hostname[:port] + * + * Since: 1.22 + */ + gchar * (*get_http_proxy) (GstWebRTCICE * ice); + void (*set_tos) (GstWebRTCICE * ice, GstWebRTCICEStream * stream, guint tos); @@ -186,6 +213,13 @@ void gst_webrtc_ice_set_turn_server (GstWebRTCIC GST_WEBRTC_API gchar * gst_webrtc_ice_get_turn_server (GstWebRTCICE * ice); +GST_WEBRTC_API +void gst_webrtc_ice_set_http_proxy (GstWebRTCICE * ice, + const gchar * uri); + +GST_WEBRTC_API +gchar * gst_webrtc_ice_get_http_proxy (GstWebRTCICE * ice); + GST_WEBRTC_API void gst_webrtc_ice_set_on_ice_candidate (GstWebRTCICE * ice, GstWebRTCICEOnCandidateFunc func, diff --git a/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/nice/nice.c b/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/nice/nice.c index ce1e430536..defc97cf82 100644 --- a/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/nice/nice.c +++ b/subprojects/gst-plugins-bad/gst-libs/gst/webrtc/nice/nice.c @@ -34,6 +34,8 @@ NICE_VERSION_MICRO >= (micro))) #endif +#define HTTP_PROXY_PORT_DEFAULT 3128 + /* XXX: * * - are locally generated remote candidates meant to be readded to libnice? @@ -74,6 +76,8 @@ struct _GstWebRTCNicePrivate GstUri *turn_server; GHashTable *turn_servers; + + GstUri *http_proxy; }; #define gst_webrtc_nice_parent_class parent_class @@ -1390,6 +1394,106 @@ out: return NULL; } +static void +on_http_proxy_resolved (GstWebRTCICE * ice, GAsyncResult * res, + gpointer user_data) +{ + GstWebRTCNice *nice = GST_WEBRTC_NICE (ice); + GstUri *uri = user_data; + GList *addresses; + GError *error = NULL; + const gchar *userinfo; + gchar *user = NULL; + gchar *pass = NULL; + gchar *ip = NULL; + guint port = GST_URI_NO_PORT; + + if (!(addresses = resolve_host_finish (nice, res, &error))) { + GST_WARNING_OBJECT (ice, "Failed to resolve http proxy: %s", + error->message); + g_clear_error (&error); + return; + } + + /* XXX: only the first IP is used */ + ip = g_inet_address_to_string (addresses->data); + + if (!ip) { + GST_ERROR_OBJECT (ice, "failed to resolve host for proxy"); + gst_uri_unref (uri); + return; + } + + port = gst_uri_get_port (uri); + if (port == GST_URI_NO_PORT) { + port = HTTP_PROXY_PORT_DEFAULT; + GST_DEBUG_OBJECT (ice, "Proxy server has no port, assuming %u", + HTTP_PROXY_PORT_DEFAULT); + } + + userinfo = gst_uri_get_userinfo (uri); + _parse_userinfo (userinfo, &user, &pass); + + g_object_set (nice->priv->nice_agent, + "proxy-ip", ip, "proxy-port", port, "proxy-type", NICE_PROXY_TYPE_HTTP, + "proxy-username", user, "proxy-password", pass, NULL); + + g_free (ip); + g_free (user); + g_free (pass); +} + +static GstUri * +_set_http_proxy (GstWebRTCICE * ice, const gchar * s) +{ + GstWebRTCNice *nice = GST_WEBRTC_NICE (ice); + GstUri *uri = gst_uri_from_string_escaped (s); + const gchar *msg = + "must be of the form http://[username:password@][:]"; + const gchar *host = NULL; + const gchar *userinfo; + gchar *user = NULL, *pass = NULL; + + GST_DEBUG_OBJECT (ice, "setting http proxy %s", s); + + if (!uri) { + GST_ERROR_OBJECT (ice, "Couldn't parse http proxy uri '%s', %s", s, msg); + return NULL; + } + + if (g_strcmp0 (gst_uri_get_scheme (uri), "http") != 0) { + GST_ERROR_OBJECT (ice, + "Couldn't parse uri scheme for http proxy server '%s', %s", s, msg); + gst_uri_unref (uri); + return NULL; + } + + host = gst_uri_get_host (uri); + if (!host) { + GST_ERROR_OBJECT (ice, "http proxy server '%s' has no host, %s", s, msg); + gst_uri_unref (uri); + return NULL; + } + + userinfo = gst_uri_get_userinfo (uri); + _parse_userinfo (userinfo, &user, &pass); + if ((pass && pass[0] != '\0') && (!user || user[0] == '\0')) { + GST_ERROR_OBJECT (ice, + "Password specified without user for http proxy '%s', %s", s, msg); + uri = NULL; + goto out; + } + + resolve_host_async (nice, host, (GAsyncReadyCallback) on_http_proxy_resolved, + gst_uri_ref (uri), (GDestroyNotify) gst_uri_unref); + +out: + g_free (user); + g_free (pass); + + return uri; +} + static void gst_webrtc_nice_set_stun_server (GstWebRTCICE * ice, const gchar * uri_s) { @@ -1443,6 +1547,30 @@ gst_webrtc_nice_get_turn_server (GstWebRTCICE * ice) return NULL; } +static void +gst_webrtc_nice_set_http_proxy (GstWebRTCICE * ice, const gchar * http_proxy) +{ + GstWebRTCNice *nice = GST_WEBRTC_NICE (ice); + GstUri *uri = _set_http_proxy (ice, http_proxy); + + if (uri) { + if (nice->priv->http_proxy) + gst_uri_unref (nice->priv->http_proxy); + nice->priv->http_proxy = uri; + } +} + +static gchar * +gst_webrtc_nice_get_http_proxy (GstWebRTCICE * ice) +{ + GstWebRTCNice *nice = GST_WEBRTC_NICE (ice); + + if (nice->priv->http_proxy) + return gst_uri_to_string (nice->priv->http_proxy); + else + return NULL; +} + static void gst_webrtc_nice_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) @@ -1526,6 +1654,8 @@ gst_webrtc_nice_finalize (GObject * object) gst_uri_unref (ice->priv->turn_server); if (ice->priv->stun_server) gst_uri_unref (ice->priv->stun_server); + if (ice->priv->http_proxy) + gst_uri_unref (ice->priv->http_proxy); g_mutex_clear (&ice->priv->lock); g_cond_clear (&ice->priv->cond); @@ -1573,6 +1703,7 @@ gst_webrtc_nice_class_init (GstWebRTCNiceClass * klass) gst_webrtc_ice_class->get_is_controller = gst_webrtc_nice_get_is_controller; gst_webrtc_ice_class->get_stun_server = gst_webrtc_nice_get_stun_server; gst_webrtc_ice_class->get_turn_server = gst_webrtc_nice_get_turn_server; + gst_webrtc_ice_class->get_http_proxy = gst_webrtc_nice_get_http_proxy; gst_webrtc_ice_class->set_force_relay = gst_webrtc_nice_set_force_relay; gst_webrtc_ice_class->set_is_controller = gst_webrtc_nice_set_is_controller; gst_webrtc_ice_class->set_local_credentials = @@ -1582,6 +1713,7 @@ gst_webrtc_nice_class_init (GstWebRTCNiceClass * klass) gst_webrtc_ice_class->set_stun_server = gst_webrtc_nice_set_stun_server; gst_webrtc_ice_class->set_tos = gst_webrtc_nice_set_tos; gst_webrtc_ice_class->set_turn_server = gst_webrtc_nice_set_turn_server; + gst_webrtc_ice_class->set_http_proxy = gst_webrtc_nice_set_http_proxy; gst_webrtc_ice_class->set_on_ice_candidate = gst_webrtc_nice_set_on_ice_candidate; gst_webrtc_ice_class->get_local_candidates =