mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
5da9f62313
With the 2.72 release, glib-networking developers have decided that TLS certificate validation cannot be implemented correctly by them, so they've deprecated it. In a nutshell: a cert can have several validation errors, but there are no guarantees that the TLS backend will return all those errors, and things are made even more complicated by the fact that the list of errors might refer to certs that are added for backwards-compat and won't actually be used by the TLS library. Our best option is to ignore the deprecation and pass the warning onto users so they can make an appropriate security decision regarding this. We can't deprecate the tls-validation-flags property because it is very useful when connecting to RTSP cameras that will never get updates to fix certificate errors. Relevant upstream merge requests / issues: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2214 https://gitlab.gnome.org/GNOME/glib-networking/-/issues/179 https://gitlab.gnome.org/GNOME/glib-networking/-/merge_requests/193 Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2494>
299 lines
10 KiB
C
299 lines
10 KiB
C
/* GStreamer
|
|
* Copyright (C) 2017 Make.TV, Inc. <info@make.tv>
|
|
* Contact: Jan Alexander Steffens (heftig) <jsteffens@make.tv>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "gstrtmp2locationhandler.h"
|
|
#include "rtmp/rtmputils.h"
|
|
#include "rtmp/rtmpclient.h"
|
|
#include <string.h>
|
|
|
|
#define DEFAULT_SCHEME GST_RTMP_SCHEME_RTMP
|
|
#define DEFAULT_HOST "localhost"
|
|
#define DEFAULT_APPLICATION "live"
|
|
#define DEFAULT_STREAM "myStream"
|
|
#define DEFAULT_LOCATION "rtmp://" DEFAULT_HOST "/" DEFAULT_APPLICATION "/" DEFAULT_STREAM
|
|
#define DEFAULT_SECURE_TOKEN NULL
|
|
#define DEFAULT_USERNAME NULL
|
|
#define DEFAULT_PASSWORD NULL
|
|
#define DEFAULT_AUTHMOD GST_RTMP_AUTHMOD_AUTO
|
|
#define DEFAULT_TIMEOUT 5
|
|
#define DEFAULT_FLASH_VERSION "LNX 10,0,32,18"
|
|
|
|
G_DEFINE_INTERFACE (GstRtmpLocationHandler, gst_rtmp_location_handler, 0);
|
|
|
|
#define GST_CAT_DEFAULT gst_rtmp_location_handler_debug_category
|
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
|
|
|
static void
|
|
gst_rtmp_location_handler_default_init (GstRtmpLocationHandlerInterface * iface)
|
|
{
|
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtmp2locationhandler", 0,
|
|
"RTMP2 Location Handling");
|
|
|
|
g_object_interface_install_property (iface, g_param_spec_string ("location",
|
|
"Location", "Location of RTMP stream to access", DEFAULT_LOCATION,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface, g_param_spec_enum ("scheme",
|
|
"Scheme", "RTMP connection scheme",
|
|
GST_TYPE_RTMP_SCHEME, DEFAULT_SCHEME,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface, g_param_spec_string ("host",
|
|
"Host", "RTMP server host name", DEFAULT_HOST,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface, g_param_spec_int ("port", "Port",
|
|
"RTMP server port", 1, 65535,
|
|
gst_rtmp_scheme_get_default_port (DEFAULT_SCHEME),
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface,
|
|
g_param_spec_string ("application", "Application",
|
|
"RTMP application path", DEFAULT_APPLICATION,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface, g_param_spec_string ("stream",
|
|
"Stream", "RTMP stream path", DEFAULT_STREAM,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface, g_param_spec_string ("username",
|
|
"User name", "RTMP authorization user name", DEFAULT_USERNAME,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface, g_param_spec_string ("password",
|
|
"Password", "RTMP authorization password", DEFAULT_PASSWORD,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface,
|
|
g_param_spec_string ("secure-token", "Secure token",
|
|
"RTMP authorization token", DEFAULT_SECURE_TOKEN,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface, g_param_spec_enum ("authmod",
|
|
"Authorization mode", "RTMP authorization mode",
|
|
GST_TYPE_RTMP_AUTHMOD, DEFAULT_AUTHMOD,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface, g_param_spec_uint ("timeout",
|
|
"Timeout", "RTMP timeout in seconds", 0, G_MAXUINT, DEFAULT_TIMEOUT,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
/**
|
|
* GstRtmpLocationHandler::tls-validation-flags:
|
|
*
|
|
* TLS certificate validation flags used to validate server
|
|
* certificate.
|
|
*
|
|
* GLib guarantees that if certificate verification fails, at least one
|
|
* error will be set, but it does not guarantee that all possible errors
|
|
* will be set. Accordingly, you may not safely decide to ignore any
|
|
* particular type of error.
|
|
*
|
|
* For example, it would be incorrect to mask %G_TLS_CERTIFICATE_EXPIRED if
|
|
* you want to allow expired certificates, because this could potentially be
|
|
* the only error flag set even if other problems exist with the
|
|
* certificate.
|
|
*/
|
|
g_object_interface_install_property (iface,
|
|
g_param_spec_flags ("tls-validation-flags", "TLS validation flags",
|
|
"TLS validation flags to use", G_TYPE_TLS_CERTIFICATE_FLAGS,
|
|
G_TLS_CERTIFICATE_VALIDATE_ALL,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_interface_install_property (iface,
|
|
g_param_spec_string ("flash-version", "Flash version",
|
|
"Flash version reported to the server", DEFAULT_FLASH_VERSION,
|
|
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
}
|
|
|
|
static GstURIType
|
|
uri_handler_get_type_sink (GType type)
|
|
{
|
|
return GST_URI_SINK;
|
|
}
|
|
|
|
static GstURIType
|
|
uri_handler_get_type_src (GType type)
|
|
{
|
|
return GST_URI_SRC;
|
|
}
|
|
|
|
static const gchar *const *
|
|
uri_handler_get_protocols (GType type)
|
|
{
|
|
return gst_rtmp_scheme_get_strings ();
|
|
}
|
|
|
|
static gchar *
|
|
uri_handler_get_uri (GstURIHandler * handler)
|
|
{
|
|
GstRtmpLocation location = { 0, };
|
|
gchar *string;
|
|
|
|
g_object_get (handler, "scheme", &location.scheme, "host", &location.host,
|
|
"port", &location.port, "application", &location.application,
|
|
"stream", &location.stream, NULL);
|
|
|
|
string = gst_rtmp_location_get_string (&location, TRUE);
|
|
gst_rtmp_location_clear (&location);
|
|
return string;
|
|
}
|
|
|
|
static gboolean
|
|
uri_handler_set_uri (GstURIHandler * handler, const gchar * string,
|
|
GError ** error)
|
|
{
|
|
GstRtmpLocationHandler *self = GST_RTMP_LOCATION_HANDLER (handler);
|
|
GstUri *uri;
|
|
const gchar *scheme_sep, *path_sep, *stream_sep, *host, *userinfo;
|
|
GstRtmpScheme scheme;
|
|
guint port;
|
|
gboolean ret = FALSE;
|
|
|
|
GST_DEBUG_OBJECT (self, "setting URI from %s", GST_STR_NULL (string));
|
|
g_return_val_if_fail (string, FALSE);
|
|
|
|
scheme_sep = strstr (string, "://");
|
|
if (!scheme_sep) {
|
|
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
|
|
"URI lacks scheme: %s", string);
|
|
return FALSE;
|
|
}
|
|
|
|
path_sep = strchr (scheme_sep + 3, '/');
|
|
if (!path_sep) {
|
|
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
|
|
"URI lacks path: %s", string);
|
|
return FALSE;
|
|
}
|
|
|
|
stream_sep = strrchr (path_sep + 1, '/');
|
|
if (!stream_sep) {
|
|
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
|
|
"URI lacks stream: %s", string);
|
|
return FALSE;
|
|
}
|
|
|
|
{
|
|
gchar *string_without_path = g_strndup (string, path_sep - string);
|
|
uri = gst_uri_from_string_escaped (string_without_path);
|
|
g_free (string_without_path);
|
|
}
|
|
|
|
if (!uri) {
|
|
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
|
|
"URI failed to parse: %s", string);
|
|
return FALSE;
|
|
}
|
|
|
|
gst_uri_normalize (uri);
|
|
|
|
scheme = gst_rtmp_scheme_from_uri (uri);
|
|
if (scheme < 0) {
|
|
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
|
|
"URI has bad scheme: %s", string);
|
|
goto out;
|
|
}
|
|
|
|
host = gst_uri_get_host (uri);
|
|
if (!host) {
|
|
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
|
|
"URI lacks hostname: %s", string);
|
|
goto out;
|
|
}
|
|
|
|
port = gst_uri_get_port (uri);
|
|
if (port == GST_URI_NO_PORT) {
|
|
port = gst_rtmp_scheme_get_default_port (scheme);
|
|
}
|
|
|
|
{
|
|
const gchar *path = path_sep + 1, *stream = stream_sep + 1;
|
|
gchar *application = g_strndup (path, stream_sep - path);
|
|
|
|
GST_DEBUG_OBJECT (self, "setting location to %s://%s:%u/%s stream %s",
|
|
gst_rtmp_scheme_to_string (scheme), host, port, application, stream);
|
|
|
|
g_object_set (self, "scheme", scheme, "host", host, "port", port,
|
|
"application", application, "stream", stream, "username", NULL,
|
|
"password", NULL, NULL);
|
|
|
|
g_free (application);
|
|
}
|
|
|
|
userinfo = gst_uri_get_userinfo (uri);
|
|
if (userinfo) {
|
|
gchar *user, *pass;
|
|
gchar **split = g_strsplit (userinfo, ":", 2);
|
|
|
|
if (!split || !split[0] || !split[1]) {
|
|
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
|
|
"Failed to parse username:password data");
|
|
g_strfreev (split);
|
|
goto out;
|
|
}
|
|
|
|
if (g_strrstr (split[1], ":") != NULL)
|
|
GST_WARNING_OBJECT (self, "userinfo %s contains more than one ':', will "
|
|
"assume that the first ':' delineates user:pass. You should escape "
|
|
"the user and pass before adding to the URI.", userinfo);
|
|
|
|
user = g_uri_unescape_string (split[0], NULL);
|
|
pass = g_uri_unescape_string (split[1], NULL);
|
|
g_strfreev (split);
|
|
|
|
g_object_set (self, "username", user, "password", pass, NULL);
|
|
g_free (user);
|
|
g_free (pass);
|
|
}
|
|
|
|
ret = TRUE;
|
|
|
|
out:
|
|
gst_uri_unref (uri);
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
gst_rtmp_location_handler_implement_uri_handler (GstURIHandlerInterface * iface,
|
|
GstURIType type)
|
|
{
|
|
switch (type) {
|
|
case GST_URI_SINK:
|
|
iface->get_type = uri_handler_get_type_sink;
|
|
break;
|
|
case GST_URI_SRC:
|
|
iface->get_type = uri_handler_get_type_src;
|
|
break;
|
|
default:
|
|
g_return_if_reached ();
|
|
}
|
|
iface->get_protocols = uri_handler_get_protocols;
|
|
iface->get_uri = uri_handler_get_uri;
|
|
iface->set_uri = uri_handler_set_uri;
|
|
}
|
|
|
|
gboolean
|
|
gst_rtmp_location_handler_set_uri (GstRtmpLocationHandler * handler,
|
|
const gchar * uri)
|
|
{
|
|
GError *error = NULL;
|
|
gboolean ret;
|
|
|
|
g_return_val_if_fail (GST_IS_RTMP_LOCATION_HANDLER (handler), FALSE);
|
|
|
|
ret = gst_uri_handler_set_uri (GST_URI_HANDLER (handler), uri, &error);
|
|
if (!ret) {
|
|
GST_ERROR_OBJECT (handler, "Failed to set URI: %s", error->message);
|
|
g_object_set (handler, "scheme", DEFAULT_SCHEME, "host", NULL,
|
|
"port", gst_rtmp_scheme_get_default_port (DEFAULT_SCHEME),
|
|
"application", NULL, "stream", NULL, NULL);
|
|
g_error_free (error);
|
|
}
|
|
return ret;
|
|
}
|