[grilo-plugins] upnp: tag sources that belong to the same user



commit 91c4ee1dfe0bd39155e8a19ca3d7c4d335bb652b
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Wed Mar 5 21:19:45 2014 +0100

    upnp: tag sources that belong to the same user
    
    If the source is found to be on the local machine, scan /proc/net/tcp
    to find the UID of the process that is listening on the socket,
    and tag the source if it's the same user as the one running the
    client.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=723780

 src/upnp/grl-upnp-utils.c |  300 ++++++++++++++++++++++++++++++++++++++++++---
 src/upnp/grl-upnp-utils.h |    4 +-
 src/upnp/grl-upnp.c       |   27 +++--
 3 files changed, 306 insertions(+), 25 deletions(-)
---
diff --git a/src/upnp/grl-upnp-utils.c b/src/upnp/grl-upnp-utils.c
index 0ad0833..6d818bc 100644
--- a/src/upnp/grl-upnp-utils.c
+++ b/src/upnp/grl-upnp-utils.c
@@ -25,6 +25,7 @@
 #define _GNU_SOURCE
 
 #include <string.h>
+#include <stdlib.h>
 #include <gio/gio.h>
 
 #ifdef G_OS_UNIX
@@ -32,6 +33,10 @@
 #include <sys/socket.h>
 #include <unistd.h>
 #include <errno.h>
+#ifdef __linux__
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#endif
 #endif
 
 #include "grl-upnp-utils.h"
@@ -92,38 +97,303 @@ is_our_ip_address (GInetAddress *address)
   return ret;
 }
 
-gboolean
-grl_upnp_util_uri_is_localhost (SoupURI *uri)
+#ifdef __linux__
+static gboolean
+is_our_user_ipv4 (struct sockaddr_in *address)
+{
+  GIOChannel *file;
+  gboolean found;
+  uid_t uid;
+  gboolean ret;
+  GIOStatus status;
+  gchar *line;
+
+  ret = FALSE;
+  file = g_io_channel_new_file ("/proc/net/tcp", "r", NULL);
+  if (file == NULL)
+    return FALSE;
+
+  found = FALSE;
+  /* skip first line, it's the header */
+  status = g_io_channel_read_line (file, &line, NULL, NULL, NULL);
+  g_free (line);
+  if (status != G_IO_STATUS_NORMAL)
+    goto out;
+
+  status = g_io_channel_read_line (file, &line, NULL, NULL, NULL);
+  while (status == G_IO_STATUS_NORMAL) {
+    int j, k, l;
+    /* 8 for IP, 4 for port, 1 for :, 1 for NUL */
+    char buffer[8 + 4 + 1 + 1];
+    guint32 ip;
+    guint16 port;
+
+    j = 0;
+
+    /* skip leading space */
+    while (line[j] == ' ')
+      j++;
+
+    /* skip the first field */
+    while (line[j] != ' ')
+      j++;
+    while (line[j] == ' ')
+      j++;
+
+    strncpy(buffer, line + j, sizeof(buffer));
+    buffer[8+4+1] = 0;
+    buffer[8] = 0;
+    j += 8+4+1;
+
+    /* the IP is in network byte order
+       (so 127.0.0.1 is 0100007F)
+    */
+    ip = strtoul(buffer, NULL, 16);
+    port = htons(strtoul(buffer+8+1, NULL, 16));
+
+    if ((ip == 0 || ip == address->sin_addr.s_addr) &&
+        port == address->sin_port) {
+      /* skip rem_address, st, tx_queue+rx_queue, tr+tm->when, retrnsmt */
+      while (line[j] == ' ')
+        j++;
+      for (k = 0; k < 5; k++) {
+        while (line[j] != ' ')
+          j++;
+        while (line[j] == ' ')
+          j++;
+      }
+
+      strncpy(buffer, line + j, sizeof(buffer));
+      buffer[sizeof(buffer)-1] = 0;
+      l = 0;
+      while (buffer[l] != ' ' && buffer[l] != 0)
+        l++;
+      buffer[l] = 0;
+
+      uid = strtoul(buffer, NULL, 0);
+
+      found = TRUE;
+      break;
+    }
+
+    g_free (line);
+    status = g_io_channel_read_line (file, &line, NULL, NULL, NULL);
+  }
+
+  if (found)
+    ret = uid == getuid();
+
+ out:
+  g_io_channel_unref (file);
+  return ret;
+}
+
+static gboolean
+is_our_user_ipv6 (struct sockaddr_in6 *address)
+{
+  GIOChannel *file;
+  gboolean found;
+  uid_t uid;
+  gboolean ret;
+  GIOStatus status;
+  gchar *line;
+
+  ret = FALSE;
+  file = g_io_channel_new_file ("/proc/net/tcp", "r", NULL);
+  if (file == NULL)
+    return FALSE;
+
+  found = FALSE;
+  /* skip first line, it's the header */
+  status = g_io_channel_read_line (file, &line, NULL, NULL, NULL);
+  g_free (line);
+  if (status != G_IO_STATUS_NORMAL)
+    goto out;
+
+  status = g_io_channel_read_line (file, &line, NULL, NULL, NULL);
+  while (status == G_IO_STATUS_NORMAL) {
+    char *line;
+    int j, k, l;
+    /* 4*8 for IP, 4 for port, 1 for :, 1 for NUL */
+    char buffer[4*8 + 4 + 1 + 1];
+    guint32 ip[4];
+    guint16 port;
+    guint32 all_ipv6[4] = { 0, 0, 0, 0 };
+
+    j = 0;
+
+    /* skip leading space */
+    while (line[j] == ' ')
+      j++;
+
+    /* skip the first field */
+    while (line[j] != ' ')
+      j++;
+    while (line[j] == ' ')
+      j++;
+
+    strncpy(buffer, line + j, sizeof(buffer));
+    buffer[4*8+4+1] = 0;
+    buffer[4*8] = 0;
+    j += 4*8+4+1;
+
+    for (k = 0; k < 4; k++) {
+      /* the IP is written as 4 uint32 units, each in network
+         byte order
+      */
+      char c;
+      c = buffer[8 * k];
+      buffer[8 * k] = 0;
+      ip[k] = strtoul(buffer, NULL, 16);
+      buffer[8 * k] = c;
+    }
+    port = htons(strtoul(buffer+4*8+1, NULL, 16));
+
+    if ((memcmp (ip, all_ipv6, sizeof(ip)) == 0 ||
+         memcmp (ip, address->sin6_addr.s6_addr, sizeof(ip)) == 0) &&
+        port == address->sin6_port) {
+      /* skip remote_address, st, tx_queue+rx_queue, tr+tm->when, retrnsmt */
+      while (line[j] == ' ')
+        j++;
+      for (k = 0; k < 5; k++) {
+        while (line[j] != ' ')
+          j++;
+        while (line[j] == ' ')
+          j++;
+      }
+
+      strncpy(buffer, line + j, sizeof(buffer));
+      buffer[sizeof(buffer)-1] = 0;
+      l = 0;
+      while (buffer[l] != ' ' && buffer[l] != 0)
+        l++;
+      buffer[l] = 0;
+
+      uid = strtoul(buffer, NULL, 0);
+
+      found = TRUE;
+      break;
+    }
+
+    g_free (line);
+    status = g_io_channel_read_line (file, &line, NULL, NULL, NULL);
+  }
+
+  if (found)
+    ret = uid == getuid();
+
+ out:
+  g_io_channel_unref (file);
+  return ret;
+}
+
+static gboolean
+is_our_user (GSocketAddress *sockaddr)
+{
+  struct sockaddr *native_sockaddr;
+  gsize native_len;
+  gboolean ret;
+
+  native_len = g_socket_address_get_native_size (sockaddr);
+  native_sockaddr = g_alloca (native_len);
+  g_socket_address_to_native (sockaddr, native_sockaddr, native_len, NULL);
+
+  if (native_sockaddr->sa_family == AF_INET) {
+    ret = is_our_user_ipv4 ((struct sockaddr_in*) native_sockaddr);
+
+    if (!ret) {
+      /* try also an ipv6 mapped ipv4 */
+      struct sockaddr_in6 ipv6;
+      ipv6.sin6_family = AF_INET6;
+      ipv6.sin6_port = ((struct sockaddr_in*) native_sockaddr)->sin_port;
+      ipv6.sin6_flowinfo = 0;
+
+      memset (ipv6.sin6_addr.s6_addr, 0, 12);
+      memcpy (ipv6.sin6_addr.s6_addr + 12, &((struct sockaddr_in*) native_sockaddr)->sin_port, 4);
+
+      return is_our_user_ipv6 (&ipv6);
+    } else
+      return ret;
+  } else
+    return is_our_user_ipv6 ((struct sockaddr_in6*) native_sockaddr);
+}
+#else
+static gboolean
+is_our_user (GSocketAddress *address)
+{
+  return FALSE;
+}
+#endif
+
+void
+grl_upnp_util_uri_is_localhost (SoupURI  *uri,
+                                gboolean *localuser,
+                                gboolean *localhost)
 {
   char hostname_buffer[HOSTNAME_LENGTH+1];
   const char *host;
   GInetAddress *ip_address;
-  gboolean ret;
 
   host = soup_uri_get_host (uri);
-  if (host == NULL)
-    return FALSE;
+  if (host == NULL) {
+    *localhost = FALSE;
+    *localuser = FALSE;
+    return;
+  }
 
   gethostname (hostname_buffer, sizeof(hostname_buffer));
-  if (strcmp (hostname_buffer, host) == 0)
-    return TRUE;
+  if (strcmp (hostname_buffer, host) == 0) {
+    GList *addresses;
+    GSocketAddress *sockaddr;
+
+    addresses = g_resolver_lookup_by_name (g_resolver_get_default (), host, NULL, NULL);
+    if (addresses == NULL) {
+      *localhost = FALSE;
+      *localuser = FALSE;
+      return;
+    }
+
+    *localhost = TRUE;
+
+    sockaddr = G_SOCKET_ADDRESS (g_inet_socket_address_new (addresses->data, uri->port));
+    *localuser = is_our_user (sockaddr);
+
+    g_object_unref (sockaddr);
+    g_list_free_full (addresses, g_object_unref);
+    return;
+  }
 
   ip_address = g_inet_address_new_from_string (host);
-  if (ip_address == NULL)
-    return FALSE;
+  if (ip_address == NULL) {
+    *localhost = FALSE;
+    *localuser = FALSE;
+    return;
+  }
 
-  ret = is_our_ip_address (ip_address);
-  g_object_unref (ip_address);
+  *localhost = is_our_ip_address (ip_address);
+  if (*localhost) {
+    GSocketAddress *sockaddr;
 
-  return ret;
+    sockaddr = G_SOCKET_ADDRESS (g_inet_socket_address_new (ip_address, uri->port));
+    *localuser = is_our_user (sockaddr);
+
+    g_object_unref (sockaddr);
+  } else {
+    *localuser = FALSE;
+  }
+
+  g_object_unref (ip_address);
 }
 
 #else
 
-gboolean
-grl_upnp_util_uri_is_localhost (SoupURI *uri)
+void
+grl_upnp_util_uri_is_localhost (SoupURI  *uri,
+                                gboolean *localhost,
+                                gboolean *localuser)
 {
-  return FALSE;
+  *localhost = FALSE;
+  *localuser = FALSE;
 }
 
 #endif
diff --git a/src/upnp/grl-upnp-utils.h b/src/upnp/grl-upnp-utils.h
index 52332b6..0f9e23e 100644
--- a/src/upnp/grl-upnp-utils.h
+++ b/src/upnp/grl-upnp-utils.h
@@ -25,7 +25,9 @@
 
 G_BEGIN_DECLS
 
-gboolean grl_upnp_util_uri_is_localhost (SoupURI  *uri);
+void grl_upnp_util_uri_is_localhost (SoupURI  *uri,
+                                     gboolean *localhost,
+                                     gboolean *localuser);
 
 G_END_DECLS
 
diff --git a/src/upnp/grl-upnp.c b/src/upnp/grl-upnp.c
index f9a7e39..4288925 100644
--- a/src/upnp/grl-upnp.c
+++ b/src/upnp/grl-upnp.c
@@ -110,7 +110,11 @@ static void setup_key_mappings (void);
 
 static gchar *build_source_id (const gchar *udn);
 
-static GrlUpnpSource *grl_upnp_source_new (const gchar *id, const gchar *name, const gchar *icon_url, 
gboolean localmachine);
+static GrlUpnpSource *grl_upnp_source_new (const gchar *id,
+                                           const gchar *name,
+                                           const gchar *icon_url,
+                                           gboolean     localhost,
+                                           gboolean     localuser);
 
 gboolean grl_upnp_plugin_init (GrlRegistry *registry,
                                GrlPlugin *plugin,
@@ -211,12 +215,14 @@ static GrlUpnpSource *
 grl_upnp_source_new (const gchar *source_id,
                      const gchar *name,
                      const gchar *icon_url,
-                     gboolean     localhost)
+                     gboolean     localhost,
+                     gboolean     localuser)
 {
   gchar *source_desc;
   GrlUpnpSource *source;
   GIcon *icon = NULL;
-  gchar *tags[2];
+  gchar *tags[3];
+  int i;
 
   GRL_DEBUG ("grl_upnp_source_new");
   source_desc = g_strdup_printf (SOURCE_DESC_TEMPLATE, name);
@@ -229,11 +235,12 @@ grl_upnp_source_new (const gchar *source_id,
     g_object_unref (file);
   }
 
-  if (localhost) {
-    tags[0] = "localhost";
-    tags[1] = NULL;
-  } else
-    tags[0] = NULL;
+  i = 0;
+  if (localhost)
+    tags[i++] = "localhost";
+  if (localuser)
+    tags[i++] = "localuser";
+  tags[i++] = NULL;
 
   source = g_object_new (GRL_UPNP_SOURCE_TYPE,
                         "source-id", source_id,
@@ -476,6 +483,7 @@ device_available_cb (GUPnPControlPoint *cp,
   GrlRegistry *registry;
   gchar *source_id;
   gchar *icon_url;
+  gboolean localhost, localuser;
 
   GRL_DEBUG ("device_available_cb");
 
@@ -508,8 +516,9 @@ device_available_cb (GUPnPControlPoint *cp,
   /* Now let's check if it supports search operations before registering */
   icon_url = get_device_icon (device);
   url_base = (SoupURI*) gupnp_device_info_get_url_base (GUPNP_DEVICE_INFO (device));
+  grl_upnp_util_uri_is_localhost (url_base, &localhost, &localuser);
   GrlUpnpSource *source = grl_upnp_source_new (source_id, name, icon_url,
-                                               grl_upnp_util_uri_is_localhost (url_base));
+                                               localhost, localuser);
   g_free (icon_url);
   source->priv->device = g_object_ref (device);
   source->priv->service = g_object_ref (service);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]