Skip to content
Commits on Source (39)
1.2.7
=====
- Fix build with -Wformat-security=error
- Bump required GLib version to 2.66
- Fix some introspection annotations
- Add missing varargs functions to vapi
- Revert fix from 1.2.5 which causes managed control points to
live too long
All contributors to this release:
- Jens Georg <mail@jensge.org>
- Bastien Nocera <hadess@hadess.net>
- Andreas Müller <schnitzeltony@gmail.com>
1.2.6
=====
- Fix wrong dependency on GSSDP 1.2.4
Bugs fixed in this release:
- https://gitlab.gnome.org/GNOME/gupnp/issues/48
All contributors to this release:
- Jens Georg <mail@jensge.org>
1.2.5
=====
- Fix introspection annotation for send_action_list
- Fix potential fd leak in linux CM
- Fix potential NULL pointer dereference when evaluating
unset ServiceProxyActions
- Fix leaking the message string if an action is never
sent
- Fix leaking the ServiceProxyAction if sending fails
in call_action
- Fix introspection annotation for send_action and
call_action_finish to prevent a double-free
- Make ServiceIntrospection usable from
gobject-introspection
- Add Python examle
- Add C example
- Fix JavaScript example
- Fix potential use-after-free if service proxy is
destroxed before libsoup request finishes in control
point
- Fix potential data leak due to being vulnerable to DNS
rebind attacs
Bugs fixed in this release:
- https://gitlab.gnome.org/GNOME/gupnp/issues/47
- https://gitlab.gnome.org/GNOME/gupnp/issues/46
- https://gitlab.gnome.org/GNOME/gupnp/issues/23
- https://gitlab.gnome.org/GNOME/gupnp/issues/24
All contributors to this release:
- Jens Georg <mail@jensge.org>
- Doug Nazar <nazard@nazar.ca>
- Andre Klapper <a9016009@gmx.de>
1.2.4
=====
- Fix subscription check on V6 link-local addresses
......
......@@ -5,10 +5,9 @@ version_xml = configure_file(input: 'version.xml.in',
entities)
if get_option('gtk_doc')
gnome.gtkdoc('gupnp',
gnome.gtkdoc('gupnp',
main_xml : 'gupnp-docs.xml',
src_dir : [join_paths(meson.source_root(), 'libgupnp'),
join_paths(meson.build_root(), 'libgupnp')],
src_dir : ['libgupnp'],
dependencies : libgupnp,
scan_args : ['--ignore-decorators', 'G_DEPRECATED|G_GNUC_DEPRECATED,G_DEPRECATED_FOR'],
ignore_headers : [
......
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
#include <glib.h>
#include <libgupnp/gupnp.h>
char CONTENT_DIR[] = "urn:schemas-upnp-org:service:RenderingControl:1";
gboolean on_timeout(gpointer user_data)
{
g_main_loop_quit ((GMainLoop*)(user_data));
return FALSE;
}
void
gvalue_free (gpointer value)
{
g_value_unset ((GValue *) value);
g_free (value);
}
void
on_introspection (GObject *object, GAsyncResult *res, gpointer user_data)
{
GError *error = NULL;
GUPnPServiceIntrospection *i = gupnp_service_info_introspect_finish (
GUPNP_SERVICE_INFO (object),
res,
&error);
if (error != NULL) {
g_critical ("%s", error->message);
g_clear_error (&error);
}
const GUPnPServiceActionInfo *info =
gupnp_service_introspection_get_action (i, "GetVolume");
const char *state_variable_name =
((GUPnPServiceActionArgInfo *) info->arguments->next->data)
->related_state_variable;
const char *channel = gupnp_service_introspection_get_state_variable (
i,
state_variable_name)
->allowed_values->data;
g_print ("Calling GetVolume for channel %s", channel);
GList *in_names = NULL;
in_names = g_list_prepend (in_names, g_strdup ("Channel"));
in_names = g_list_prepend (in_names, g_strdup ("InstanceID"));
GList *in_values = NULL;
GValue instance = G_VALUE_INIT;
g_value_init (&instance, G_TYPE_INT);
g_value_set_int (&instance, 0);
in_values = g_list_prepend (in_values, &instance);
GValue channel_v = G_VALUE_INIT;
g_value_init (&channel_v, G_TYPE_STRING);
g_value_set_string (&channel_v, channel);
GUPnPServiceProxyAction *a =
gupnp_service_proxy_action_new_from_list ("GetVolume",
in_names,
in_values);
g_list_free_full (in_names, g_free);
g_list_free (in_values);
g_value_unset (&channel_v);
g_boxed_copy (gupnp_service_proxy_action_get_type (), a);
gupnp_service_proxy_call_action (GUPNP_SERVICE_PROXY (object),
a,
NULL,
&error);
g_boxed_free (gupnp_service_proxy_action_get_type (), a);
GList *out_names = NULL;
out_names = g_list_prepend (out_names, "CurrentVolume");
GList *out_types = NULL;
out_types =
g_list_prepend (out_types, GSIZE_TO_POINTER (G_TYPE_STRING));
GList *out_values = NULL;
gupnp_service_proxy_action_get_result_list (a,
out_names,
out_types,
&out_values,
&error);
g_list_free (out_types);
g_list_free (out_names);
if (error != NULL) {
g_critical ("%s", error->message);
g_clear_error (&error);
} else {
g_print ("Current volume: %s\n",
g_value_get_string (out_values->data));
}
g_list_free_full (out_values, gvalue_free);
gupnp_service_proxy_action_unref (a);
g_object_unref (i);
}
void on_proxy_available (GUPnPControlPoint *cp, GUPnPServiceProxy *proxy, gpointer user_data)
{
g_autofree char *id =
gupnp_service_info_get_id (GUPNP_SERVICE_INFO (proxy));
g_print ("Got ServiceProxy %s at %s\n",
id,
gupnp_service_info_get_location (GUPNP_SERVICE_INFO (proxy)));
g_print ("Introspecting service ...\n");
gupnp_service_info_introspect_async (GUPNP_SERVICE_INFO (proxy),
NULL,
on_introspection,
NULL);
}
int main(int argc, char *argv[])
{
GError *error = NULL;
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
GUPnPContext *context = gupnp_context_new ("wlp3s0", 0, &error);
if (error != NULL) {
g_error ("%s", error->message);
}
GUPnPControlPoint *cp = gupnp_control_point_new (context, CONTENT_DIR);
g_signal_connect (cp, "service-proxy-available", G_CALLBACK (on_proxy_available), NULL);
gssdp_resource_browser_set_active (GSSDP_RESOURCE_BROWSER (cp), TRUE);
g_timeout_add_seconds (10, on_timeout, loop);
g_main_loop_run (loop);
g_object_unref (cp);
g_object_unref (context);
g_main_loop_unref (loop);
return 0;
}
#!/usr/bin/gjs
//
// Copyright (c) 2016, Jens Georg <mail@jensge.org>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
......@@ -29,27 +24,40 @@
imports.gi.versions.GUPnP = "1.2"
const Mainloop = imports.mainloop;
const GObject = imports.gi.GObject;
const GUPnP = imports.gi.GUPnP;
const GLib = imports.gi.GLib;
const CONTENT_DIR = "urn:schemas-upnp-org:service:RenderingControl:1";
function on_action(proxy, res) {
let action = proxy.call_action_finish (res);
let [success, volume] = action.get_result_list(["CurrentVolume"], [GObject.TYPE_FLOAT]);
print(volume);
}
function on_service_introspection(proxy, res) {
var result = proxy.introspect_finish(res);
var action_info = result.get_action("GetVolume")
var state_variable_name = action_info.arguments[1].related_state_variable;
const CONTENT_DIR = "urn:schemas-upnp-org:service:RenderingControl:2";
var channel = result.get_state_variable (state_variable_name);
function _on_ready () {
print ("Calling GetVolume for channel ", channel.allowed_values[0]);
var action = GUPnP.ServiceProxyAction.new_from_list("GetVolume", ["InstanceID", "Channel"], [0, channel.allowed_values[0]]);
proxy.call_action_async (action, null, on_action);
}
function _on_sp_available (cp, proxy) {
print ("Got Proxy");
for (var i = 0; i < 1000; ++i) {
proxy.send_action_list ("GetVolume",
["InstanceId", "Channel"],
[0, 0],
[],
[]);
}
print ("Got ServiceProxy ", proxy.get_id(), proxy.get_location());
print ("Introspecting service...");
proxy.introspect_async(null, on_service_introspection)
}
var context = new GUPnP.Context ( {'interface': "lo"});
var context = new GUPnP.Context ( {'interface': "wlp3s0"});
context.init(null);
var cp = new GUPnP.ControlPoint ( {'client': context, 'target' : CONTENT_DIR});
cp.connect ("service-proxy-available", _on_sp_available);
cp.active = true;
Mainloop.run ("");
GLib.timeout_add_seconds (GLib.PRIORITY_LOW, 10, () => { Mainloop.quit(); return false; })
Mainloop.run ();
#!/usr/bin/env python3
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
import gi
gi.require_version('GUPnP', '1.2')
from gi.repository import GUPnP
from gi.repository import GLib
from gi.repository import GObject
RENDERING_CONTROL = "urn:schemas-upnp-org:service:RenderingControl:1"
def do_quit():
global l
l.quit()
return False
def on_service_introspection(proxy, res):
result = proxy.introspect_finish(res)
action_info = result.get_action('GetVolume')
state_variable_name = action_info.arguments[1].related_state_variable
channel = result.get_state_variable(state_variable_name)
print (f'Calling GetVolume for channel {channel.allowed_values[0]}')
action = GUPnP.ServiceProxyAction.new_from_list('GetVolume', ["InstanceID", "Channel"], [0, channel.allowed_values[0]])
proxy.call_action (action, None)
success, [volume] = action.get_result_list (["CurrentVolume"], [GObject.TYPE_FLOAT])
print(volume)
def on_sp_available(cp, proxy):
print(f'Got ServiceProxy {proxy.get_id()} at {proxy.get_location()}')
print('Introspecting service...')
proxy.introspect_async(None, on_service_introspection)
if __name__ == '__main__':
global l
print("Looking for renderers and querying volumes fo 10s")
l = GLib.MainLoop()
context = GUPnP.Context.new ('wlp3s0', 0)
cp = GUPnP.ControlPoint.new (context, RENDERING_CONTROL)
cp.set_active (True)
cp.connect ('service-proxy-available', on_sp_available)
GLib.timeout_add_seconds (10, do_quit)
l.run()
......@@ -10,3 +10,9 @@ executable(
'light-client.c',
dependencies : gupnp
)
executable(
'get-volume',
'get-volume.c',
dependencies: gupnp
)
......@@ -28,13 +28,6 @@ level libraries utilizing the GUPnP framework.</description>
<bug-database
rdf:resource="https://gitlab.gnome.org/GNOME/gupnp/issues/" />
<maintainer>
<foaf:Person>
<foaf:name>Ross Burton</foaf:name>
<foaf:mbox rdf:resource="mailto:ross.burton@intel.com" />
<gnome:userid>rburton</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Jens Georg</foaf:name>
......
......@@ -30,6 +30,8 @@
* Since: 0.20.11
*/
#include <config.h>
#include "gupnp-acl.h"
#include "gupnp-acl-private.h"
#include "gupnp-device.h"
......
......@@ -29,6 +29,8 @@
*
*/
#include <config.h>
#include "gupnp-connman-manager.h"
#include "gupnp-context.h"
......
......@@ -43,6 +43,14 @@ gupnp_context_rewrite_uri_to_uri (GUPnPContext *context,
G_GNUC_INTERNAL gboolean
gupnp_context_ip_is_ours (GUPnPContext *context, const char *address);
G_GNUC_INTERNAL gboolean
gupnp_context_validate_host_header (GUPnPContext *context, const char *host);
gboolean
validate_host_header (const char *host_header,
const char *host_ip,
guint context_port);
G_END_DECLS
#endif /* GUPNP_CONTEXT_PRIVATE_H */
......@@ -331,10 +331,7 @@ gupnp_context_dispose (GObject *object)
context = GUPNP_CONTEXT (object);
priv = gupnp_context_get_instance_private (context);
if (priv->session) {
g_object_unref (priv->session);
priv->session = NULL;
}
g_clear_object (&priv->session);
while (priv->host_path_datas) {
HostPathData *data;
......@@ -344,10 +341,7 @@ gupnp_context_dispose (GObject *object)
gupnp_context_unhost_path (context, data->server_path);
}
if (priv->server) {
g_object_unref (priv->server);
priv->server = NULL;
}
g_clear_object (&priv->server);
/* Call super */
object_class = G_OBJECT_CLASS (gupnp_context_parent_class);
......@@ -1715,3 +1709,71 @@ out:
return retval;
}
gboolean
validate_host_header (const char *host_header,
const char *host_ip,
guint context_port)
{
gboolean retval = FALSE;
// Be lazy and let GUri do the heavy lifting here, such as stripping the
// [] from v6 addresses, splitting of the port etc.
char *uri_from_host = g_strconcat ("http://", host_header, NULL);
char *host = NULL;
int port = 0;
GError *error = NULL;
g_uri_split_network (uri_from_host,
G_URI_FLAGS_NONE,
NULL,
&host,
&port,
&error);
if (error != NULL) {
g_debug ("Failed to parse HOST header from request: %s",
error->message);
goto out;
}
// -1 means there was no :port; according to UDA this is allowed and
// defaults to 80, the HTTP port then
if (port == -1) {
port = 80;
}
if (!g_str_equal (host, host_ip)) {
g_debug ("Mismatch between host header and host IP (%s, "
"expected: %s)",
host,
host_ip);
}
if (port != context_port) {
g_debug ("Mismatch between host header and host port (%d, "
"expected %d)",
port,
context_port);
}
retval = g_str_equal (host, host_ip) && port == context_port;
out:
g_clear_error (&error);
g_free (host);
g_free (uri_from_host);
return retval;
}
gboolean
gupnp_context_validate_host_header (GUPnPContext *context,
const char *host_header)
{
return validate_host_header (
host_header,
gssdp_client_get_host_ip (GSSDP_CLIENT (context)),
gupnp_context_get_port (context));
}
......@@ -31,6 +31,7 @@
* the specified discovery target changes.
*/
#include <config.h>
#include <string.h>
#include "gupnp-control-point.h"
......
......@@ -27,6 +27,7 @@
* device information.
*/
#include <config.h>
#include <string.h>
#include "gupnp-device-info.h"
......@@ -1421,7 +1422,7 @@ gupnp_device_info_list_service_types (GUPnPDeviceInfo *info)
* this function a new object is created. The application must cache any used
* services if it wishes to keep them around and re-use them.
*
* Returns: (transfer full): A #GUPnPServiceInfo.
* Returns: (transfer full)(allow-none): A #GUPnPServiceInfo.
**/
GUPnPServiceInfo *
gupnp_device_info_get_service (GUPnPDeviceInfo *info,
......
......@@ -27,6 +27,7 @@
* and services. #GUPnPDeviceProxy implements the #GUPnPDeviceInfo interface.
*/
#include <config.h>
#include <string.h>
#include "gupnp-device-proxy.h"
......
......@@ -28,6 +28,7 @@
* interface.
*/
#include <config.h>
#include <string.h>
#include "gupnp-device.h"
......
......@@ -19,6 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#include <config.h>
#include "gupnp-error.h"
#include "gupnp-error-private.h"
......
......@@ -943,8 +943,6 @@ create_ioctl_socket (GUPnPLinuxContextManager *self, GError **error)
priv->fd = socket (AF_INET, SOCK_DGRAM, 0);
if (priv->fd < 0) {
priv->fd = 0;
g_set_error_literal (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
......@@ -1045,6 +1043,7 @@ gupnp_linux_context_manager_init (GUPnPLinuxContextManager *self)
GUPnPLinuxContextManagerPrivate *priv;
priv = gupnp_linux_context_manager_get_instance_private (self);
priv->fd = -1;
priv->nl_seq = 0;
......@@ -1087,8 +1086,10 @@ gupnp_linux_context_manager_constructed (GObject *object)
NULL);
cleanup:
if (error) {
if (priv->fd > 0)
if (priv->fd >= 0) {
close (priv->fd);
priv->fd = -1;
}
g_warning ("Failed to setup Linux context manager: %s",
error->message);
......@@ -1129,9 +1130,9 @@ gupnp_linux_context_manager_dispose (GObject *object)
priv->netlink_socket = NULL;
}
if (priv->fd != 0) {
if (priv->fd >= 0) {
close (priv->fd);
priv->fd = 0;
priv->fd = -1;
}
if (priv->interfaces) {
......
......@@ -34,6 +34,7 @@
* a device proxy type needs to be derived from #GUPnPDeviceProxy).
*/
#include <config.h>
#include <string.h>
#include "gupnp-resource-factory-private.h"
......
......@@ -26,6 +26,7 @@
* #GUPnPRootDevice allows for implementing root devices.
*/
#include <config.h>
#include <string.h>
#include <libgssdp/gssdp-resource-group.h>
......
......@@ -29,6 +29,7 @@
#define G_LOG_DOMAIN "GUPnPServiceInfo"
#include <config.h>
#include <libsoup/soup.h>
#include <string.h>
......