Logo Search packages:      
Sourcecode: gnome-vfs2 version File versions  Download package

http-proxy.c

/* ************************************************************************* */
/* FIXME: Move this to gnome-vfs-proxy () so we can reuse that in ftp too   */

/* Proxy Stuff .. not cleaned yet, completely taken from the old http module  
   Ettore Perazzoli <ettore@gnu.org> (core HTTP)
   Ian McKellar <yakk@yakk.net> (WebDAV/PUT)
   Michael Fleming <mfleming@eazel.com> (Caching, Cleanup)
*/

#include <config.h>

/* Keep <sys/types.h> above any network includes for FreeBSD. */
#include <sys/types.h>
/* Keep <netinet/in.h> above <arpa/inet.h> for FreeBSD. */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <gconf/gconf-client.h>
#include <libgnomevfs/gnome-vfs-private-utils.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h> /* for atoi */
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>

#include "http-proxy.h"

/* #define DEBUG_HTTP(x) (g_print x) */
#define DEBUG_HTTP(x)

/* Standard HTTP proxy port */
#define DEFAULT_HTTP_PROXY_PORT 8080

/* GConf paths and keys */
#define PATH_GCONF_GNOME_VFS "/system/http_proxy"
#define ITEM_GCONF_HTTP_PROXY_PORT "port"
#define ITEM_GCONF_HTTP_PROXY_HOST "host"
#define KEY_GCONF_HTTP_PROXY_PORT (PATH_GCONF_GNOME_VFS "/" ITEM_GCONF_HTTP_PROXY_PORT)
#define KEY_GCONF_HTTP_PROXY_HOST (PATH_GCONF_GNOME_VFS "/" ITEM_GCONF_HTTP_PROXY_HOST)

#define ITEM_GCONF_USE_HTTP_PROXY "use_http_proxy"
#define KEY_GCONF_USE_HTTP_PROXY (PATH_GCONF_GNOME_VFS "/" ITEM_GCONF_USE_HTTP_PROXY)

#define KEY_GCONF_HTTP_AUTH_USER (PATH_GCONF_GNOME_VFS "/" "authentication_user")
#define KEY_GCONF_HTTP_AUTH_PW (PATH_GCONF_GNOME_VFS "/" "authentication_password")
#define KEY_GCONF_HTTP_USE_AUTH (PATH_GCONF_GNOME_VFS "/" "use_authentication")

#define KEY_GCONF_HTTP_PROXY_IGNORE_HOSTS (PATH_GCONF_GNOME_VFS "/" "ignore_hosts")


/* Global variables used by the HTTP proxy config */
static GConfClient *gl_client = NULL;
static GMutex *gl_mutex = NULL;     /* This mutex protects preference values
                         * and ensures serialization of authentication
                         * hook callbacks       
                         */
static gchar *gl_http_proxy = NULL;
static GSList *gl_ignore_hosts = NULL;    /* Elements are strings. */
static GSList *gl_ignore_addrs = NULL;    /* Elements are ProxyHostAddrs */

static gchar *proxy_username = NULL;
static gchar *proxy_password = NULL;

/* Store IP addresses that may represent network or host addresses and may be
 * IPv4 or IPv6. */
typedef enum {
    PROXY_IPv4 = 4,
    PROXY_IPv6 = 6
} ProxyAddrType;

typedef struct {
    ProxyAddrType type;
    struct in_addr addr;
    struct in_addr mask;
#ifdef ENABLE_IPV6
    struct in6_addr addr6;
    struct in6_addr mask6;
#endif
} ProxyHostAddr;

#ifdef ENABLE_IPV6
static void ipv6_network_addr(const struct in6_addr *addr,
                        const struct in6_addr *mask,
                        struct in6_addr *res);

/*Check whether the node is IPv6 enabled.*/
static gboolean have_ipv6(void)
{
    int s;

    s = socket(AF_INET6, SOCK_STREAM, 0);
    if (s != -1) {
      close(s);
      return TRUE;
    }

    return FALSE;
}
#     endif

/* FIXME: should be done using AC_REPLACE_FUNCS */
#ifndef HAVE_INET_PTON
static int inet_pton(int af, const char *hostname, void *pton)
{
    struct in_addr in;
    if (!inet_aton(hostname, &in))
      return 0;


    memcpy(pton, &in, sizeof(in));
    return 1;
}
#endif

#ifdef ENABLE_IPV6
static void
ipv6_network_addr(const struct in6_addr *addr, const struct in6_addr *mask,
              struct in6_addr *res)
{
    gint i;

    for (i = 0; i < 16; ++i) {
      res->s6_addr[i] = addr->s6_addr[i] & mask->s6_addr[i];
    }
}
#endif

/**
 * host_port_from_string
 * splits a <host>:<port> formatted string into its separate components
 */
static gboolean
host_port_from_string(const char *http_proxy,
                  char **p_proxy_host, guint * p_proxy_port)
{
    char *port_part;

    port_part = strchr(http_proxy, ':');

    if (port_part && '\0' != ++port_part && p_proxy_port) {
      *p_proxy_port = (guint) strtoul(port_part, NULL, 10);
    } else if (p_proxy_port) {
      *p_proxy_port = DEFAULT_HTTP_PROXY_PORT;
    }

    if (p_proxy_host) {
      if (port_part != http_proxy) {
          *p_proxy_host =
            g_strndup(http_proxy, port_part - http_proxy - 1);
      } else {
          return FALSE;
      }
    }

    return TRUE;
}

static gboolean proxy_should_for_hostname(const char *hostname)
{
#ifdef ENABLE_IPV6
    struct in6_addr in6, net6;
#endif
    struct in_addr in;
    GSList *elt;
    ProxyHostAddr *addr;


    /* IPv4 address */
    if (inet_pton(AF_INET, hostname, &in) > 0) {
      for (elt = gl_ignore_addrs; elt; elt = g_slist_next(elt)) {
          addr = (ProxyHostAddr *) (elt->data);
          if (addr->type == PROXY_IPv4
            && (in.s_addr & addr->mask.s_addr) == addr->addr.s_addr) {
            DEBUG_HTTP(("Host %s using direct connection.", hostname));
            return FALSE;
          }
      }
    }
#ifdef ENABLE_IPV6
    else if (have_ipv6() && inet_pton(AF_INET6, hostname, &in6)) {
      for (elt = gl_ignore_addrs; elt; elt = g_slist_next(elt)) {
          addr = (ProxyHostAddr *) (elt->data);
          ipv6_network_addr(&in6, &addr->mask6, &net6);
          if (addr->type == PROXY_IPv6
            && IN6_ARE_ADDR_EQUAL(&net6, &addr->addr6)) {
            DEBUG_HTTP(("Host %s using direct connection.", hostname));
            return FALSE;
          }
          /* Handle IPv6-wrapped IPv4 addresses. */
          else if (addr->type == PROXY_IPv4
                 && IN6_IS_ADDR_V4MAPPED(&net6)) {
            guint32 v4addr;

            v4addr = net6.s6_addr[12] << 24 | net6.
                s6_addr[13] << 16 | net6.
                s6_addr[14] << 8 | net6.s6_addr[15];
            if ((v4addr & addr->mask.s_addr) != addr->addr.s_addr) {
                DEBUG_HTTP(("Host %s using direct connection.",
                        hostname));
                return FALSE;
            }
          }
      }
    }
#endif
    /* All hostnames (foo.bar.com) -- independent of IPv4 or IPv6 */

    /* If there are IPv6 addresses in the ignore_hosts list but we do not
     * have IPv6 available at runtime, then those addresses will also fall
     * through to here (and harmlessly fail to match). */
    else {
      gchar *hn = g_ascii_strdown(hostname, -1);

      for (elt = gl_ignore_hosts; elt; elt = g_slist_next(elt)) {
          if (*(gchar *) (elt->data) == '*') {
            if (g_str_has_suffix(hn, (gchar *) (elt->data) + 1)) {
                DEBUG_HTTP(("Host %s using direct connection.", hn));
                g_free(hn);
                return FALSE;
            }
          } else if (strcmp(hn, elt->data) == 0) {
            DEBUG_HTTP(("Host %s using direct connection.", hn));
            g_free(hn);
            return FALSE;
          }
      }
      g_free(hn);
    }

    return TRUE;
}

/**
 * proxy_for_uri
 * Retrives an appropriate HTTP proxy for a given toplevel uri
 * Currently, only a single HTTP proxy is implemented (there's no way for
 * specifying non-proxy domain names's).  Returns FALSE if the connect should
 * take place directly
 */

gboolean proxy_for_uri (GnomeVFSToplevelURI *toplevel_uri, 
                  HttpProxyInfo *proxy_info)
{                       
    gboolean ret;

    ret = proxy_should_for_hostname (toplevel_uri->host_name);

    g_mutex_lock (gl_mutex);

    if (ret && gl_http_proxy != NULL) {
      ret = host_port_from_string(gl_http_proxy, &(proxy_info->host),
                            &(proxy_info->port));
         
      if (ret) {
            proxy_info->username = proxy_username;
            proxy_info->password = proxy_password;
      }
          
    } else {
          
      ret = FALSE;
    }

    g_mutex_unlock(gl_mutex);

    return ret;
}


/*
 * Here's how the gconf gnome-vfs HTTP proxy variables
 * are intended to be used
 *
 * /system/http_proxy/use_http_proxy      
 *    Type: boolean
 *    If set to TRUE, the client should use an HTTP proxy to connect to all
 *    servers (except those specified in the ignore_hosts key -- see below).
 *    The proxy is specified in other gconf variables below.
 *
 * /system/http_proxy/host
 *    Type: string
 *    The hostname of the HTTP proxy this client should use.  If
 *    use-http-proxy is TRUE, this should be set.  If it is not set, the
 *    application should behave as if use-http-proxy is was set to FALSE.
 *
 * /system/http_proxy/port
 *    Type: int
 *    The port number on the HTTP proxy host that the client should connect to
 *    If use_http_proxy and host are set but this is not set, the application
 *    should use a default port value of 8080
 *
 * /system/http_proxy/authentication-user
 *    Type: string
 *    Username to pass to an authenticating HTTP proxy.
 *
 * /system/http_proxy/authentication_password
 *    Type: string
 *    Password to pass to an authenticating HTTP proxy.
 *  
 * /system/http_proxy/use-authentication
 *    Type: boolean
 *    TRUE if the client should pass http-proxy-authorization-user and
 *    http-proxy-authorization-password an HTTP proxy
 *
 * /system/http_proxy/ignore_hosts
 *    Type: list of strings
 *    A list of hosts (hostnames, wildcard domains, IP addresses, and CIDR
 *    network addresses) that should be accessed directly.
 */

static void parse_ignore_host(gpointer data, gpointer user_data)
{
    gchar *hostname, *input, *netmask;
    gboolean ip_addr = FALSE, has_error = FALSE;
    struct in_addr host;
#ifdef ENABLE_IPV6
    struct in6_addr host6, mask6;
    gint i;
#endif
    ProxyHostAddr *elt;

    input = (gchar *) data;
    elt = g_new0(ProxyHostAddr, 1);
    if ((netmask = strchr(input, '/')) != NULL) {
      hostname = g_strndup(input, netmask - input);
      ++netmask;
    } else {
      hostname = g_ascii_strdown(input, -1);
    }
    if (inet_pton(AF_INET, hostname, &host) > 0) {
      ip_addr = TRUE;
      elt->type = PROXY_IPv4;
      elt->addr.s_addr = host.s_addr;
      if (netmask) {
          gchar *endptr;
          gint width = strtol(netmask, &endptr, 10);

          if (*endptr != '\0' || width < 0 || width > 32) {
            has_error = TRUE;
          }
          elt->mask.s_addr = htonl(~0 << width);
          elt->addr.s_addr &= elt->mask.s_addr;
      } else {
          elt->mask.s_addr = 0xffffffff;
      }
    }
#ifdef ENABLE_IPV6
    else if (have_ipv6() && inet_pton(AF_INET6, hostname, &host6) > 0) {
      ip_addr = TRUE;
      elt->type = PROXY_IPv6;
      for (i = 0; i < 16; ++i) {
          elt->addr6.s6_addr[i] = host6.s6_addr[i];
      }
      if (netmask) {
          gchar *endptr;
          gint width = strtol(netmask, &endptr, 10);

          if (*endptr != '\0' || width < 0 || width > 128) {
            has_error = TRUE;
          }
          for (i = 0; i < 16; ++i) {
            elt->mask6.s6_addr[i] = 0;
          }
          for (i = 0; i < width / 8; i++) {
            elt->mask6.s6_addr[i] = 0xff;
          }
          elt->mask6.s6_addr[i] = (0xff << (8 - width % 8)) & 0xff;
          ipv6_network_addr(&elt->addr6, &mask6, &elt->addr6);
      } else {
          for (i = 0; i < 16; ++i) {
            elt->mask6.s6_addr[i] = 0xff;
          }
      }
    }
#endif

    if (ip_addr) {
      if (!has_error) {
          gchar *dst = g_new0(gchar, INET_ADDRSTRLEN);

          gl_ignore_addrs = g_slist_append(gl_ignore_addrs, elt);
          DEBUG_HTTP(("Host %s/%s does not go through proxy.",
                  hostname, inet_ntop(AF_INET, &elt->mask,
                                  dst, INET_ADDRSTRLEN)));
          g_free(dst);
      }
      g_free(hostname);
    } else {
      /* It is a hostname. */
      gl_ignore_hosts = g_slist_append(gl_ignore_hosts, hostname);
      DEBUG_HTTP(("Host %s does not go through proxy.", hostname));
      g_free(elt);
    }
}

static void construct_gl_http_proxy(gboolean use_proxy)
{
    g_free(gl_http_proxy);
    gl_http_proxy = NULL;

    g_slist_foreach(gl_ignore_hosts, (GFunc) g_free, NULL);
    g_slist_free(gl_ignore_hosts);
    gl_ignore_hosts = NULL;
    g_slist_foreach(gl_ignore_addrs, (GFunc) g_free, NULL);
    g_slist_free(gl_ignore_addrs);
    gl_ignore_addrs = NULL;

    if (use_proxy) {
      char *proxy_host;
      int proxy_port;
      GSList *ignore;

      proxy_host =
          gconf_client_get_string(gl_client,
                            KEY_GCONF_HTTP_PROXY_HOST, NULL);
      proxy_port =
          gconf_client_get_int(gl_client,
                         KEY_GCONF_HTTP_PROXY_PORT, NULL);

      if (proxy_host && proxy_host[0] != '\0') {
          if (0 != proxy_port && 0xffff >= (unsigned) proxy_port) {
            gl_http_proxy =
                g_strdup_printf("%s:%u", proxy_host, (unsigned)
                            proxy_port);
          } else {
            gl_http_proxy =
                g_strdup_printf("%s:%u", proxy_host, (unsigned)
                            DEFAULT_HTTP_PROXY_PORT);
          }
          DEBUG_HTTP(("New HTTP proxy: '%s'", gl_http_proxy));
      } else {
          DEBUG_HTTP(("HTTP proxy unset"));
      }

      g_free(proxy_host);
      proxy_host = NULL;

      ignore = gconf_client_get_list(gl_client,
                               KEY_GCONF_HTTP_PROXY_IGNORE_HOSTS,
                               GCONF_VALUE_STRING, NULL);
      g_slist_foreach(ignore, (GFunc) parse_ignore_host, NULL);
      g_slist_foreach(ignore, (GFunc) g_free, NULL);
      g_slist_free(ignore);
      ignore = NULL;
    }
}

static void set_proxy_auth(gboolean use_proxy_auth)
{
    char *auth_user;
    char *auth_pw;

    auth_user =
      gconf_client_get_string(gl_client, KEY_GCONF_HTTP_AUTH_USER, NULL);
    auth_pw =
      gconf_client_get_string(gl_client, KEY_GCONF_HTTP_AUTH_PW, NULL);

    if (use_proxy_auth) {
      /* Christian Kellner: Here are the only changes I made to the proxy sub-
       * system 
       */
      proxy_username = (auth_user != NULL ? g_strdup(auth_user) : NULL);
      proxy_password = (auth_pw != NULL ? g_strdup(auth_pw) : NULL);
      DEBUG_HTTP(("New HTTP proxy auth user: '%s'", auth_user));
    } else {
      if (proxy_username != NULL)
          g_free(proxy_username);

      if (proxy_password != NULL)
          g_free(proxy_password);

      proxy_username = proxy_password = NULL;

      DEBUG_HTTP(("HTTP proxy auth unset"));
    }

    g_free(auth_user);
    g_free(auth_pw);
}

/**
 * sig_gconf_value_changed 
 * GGconf notify function for when HTTP proxy GConf key has changed.
 */
static void
notify_gconf_value_changed(GConfClient * client,
                     guint cnxn_id, GConfEntry * entry,
                     gpointer data)
{
    const char *key;

    key = gconf_entry_get_key(entry);

    if (strcmp(key, KEY_GCONF_USE_HTTP_PROXY) == 0
      || strcmp(key, KEY_GCONF_HTTP_PROXY_IGNORE_HOSTS) == 0
      || strcmp(key, KEY_GCONF_HTTP_PROXY_HOST) == 0
      || strcmp(key, KEY_GCONF_HTTP_PROXY_PORT) == 0) {
      gboolean use_proxy_value;

      g_mutex_lock(gl_mutex);

      /* Check and see if we are using the proxy */
      use_proxy_value =
          gconf_client_get_bool(gl_client,
                          KEY_GCONF_USE_HTTP_PROXY, NULL);
      construct_gl_http_proxy(use_proxy_value);

      g_mutex_unlock(gl_mutex);
    } else if (strcmp(key, KEY_GCONF_HTTP_AUTH_USER) == 0
             || strcmp(key, KEY_GCONF_HTTP_AUTH_PW) == 0
             || strcmp(key, KEY_GCONF_HTTP_USE_AUTH) == 0) {
      gboolean use_proxy_auth;

      g_mutex_lock(gl_mutex);

      use_proxy_auth =
          gconf_client_get_bool(gl_client,
                          KEY_GCONF_HTTP_USE_AUTH, NULL);
      set_proxy_auth(use_proxy_auth);

      g_mutex_unlock(gl_mutex);
    }
}

void proxy_init(void)
{
    GError *gconf_error = NULL;
    gboolean use_proxy;
    gboolean use_proxy_auth;

    gl_client = gconf_client_get_default();
    gl_mutex = g_mutex_new();

    gconf_client_add_dir(gl_client, PATH_GCONF_GNOME_VFS,
                   GCONF_CLIENT_PRELOAD_ONELEVEL, &gconf_error);
    if (gconf_error) {
      DEBUG_HTTP(("GConf error during client_add_dir '%s'",
                gconf_error->message));
      g_error_free(gconf_error);
      gconf_error = NULL;
    }

    gconf_client_notify_add(gl_client, PATH_GCONF_GNOME_VFS,
                      notify_gconf_value_changed, NULL, NULL,
                      &gconf_error);
    if (gconf_error) {
      DEBUG_HTTP(("GConf error during notify_error '%s'",
                gconf_error->message));
      g_error_free(gconf_error);
      gconf_error = NULL;
    }

    /* Load the http proxy setting */
    use_proxy =
      gconf_client_get_bool(gl_client, KEY_GCONF_USE_HTTP_PROXY,
                        &gconf_error);

    if (gconf_error != NULL) {
      DEBUG_HTTP(("GConf error during client_get_bool '%s'",
                gconf_error->message));
      g_error_free(gconf_error);
      gconf_error = NULL;
    } else {
      construct_gl_http_proxy(use_proxy);
    }

    use_proxy_auth =
      gconf_client_get_bool(gl_client, KEY_GCONF_HTTP_USE_AUTH,
                        &gconf_error);

    if (gconf_error != NULL) {
      DEBUG_HTTP(("GConf error during client_get_bool '%s'",
                gconf_error->message));
      g_error_free(gconf_error);
      gconf_error = NULL;
    } else {
      set_proxy_auth(use_proxy_auth);
    }
}


void proxy_shutdown(void)
{
    g_object_unref(G_OBJECT(gl_client));
}

/* End of the proxy stuff */
/* ************************************************************************** */

Generated by  Doxygen 1.6.0   Back to index