[Spice-devel] [PATCH spice-gtk 4/6] session: allow to connect via HTTP CONNECT proxy

Marc-André Lureau marcandre.lureau at gmail.com
Fri Aug 24 06:58:17 PDT 2012


Allow to connect to a Spice server via a HTTP proxy with CONNECT
method. spice-gtk will use the SPICE_PROXY environment variable, which
can currently only have the following syntax: [http://]hostname[:port]

This is paving the way to more proxies support (socks4/socks5).

This code is now entirely sync (it was not even completely async), the
following patch will make it all async again.

Bump glib dependency to 2.26.

Tested with Squid, locally only.
---
 configure.ac        |   2 +-
 gtk/spice-session.c | 128 ++++++++++++++++++++++++++++++----------------------
 2 files changed, 76 insertions(+), 54 deletions(-)

diff --git a/configure.ac b/configure.ac
index 38ceeab..f4cdef4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -243,7 +243,7 @@ else
         EXTERNAL_PNP_IDS="$with_pnp_ids_path"
 fi
 
-PKG_CHECK_MODULES(GLIB2, glib-2.0 >= 2.22)
+PKG_CHECK_MODULES(GLIB2, glib-2.0 >= 2.26)
 AC_SUBST(GLIB2_CFLAGS)
 AC_SUBST(GLIB2_LIBS)
 
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 5f8847e..ae70b73 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -1511,43 +1511,59 @@ gboolean spice_session_has_channel_type(SpiceSession *session, gint type)
 /* ------------------------------------------------------------------ */
 /* private functions                                                  */
 
-static GSocket *channel_connect_socket(SpiceChannel *channel,
-                                       GSocketAddress *sockaddr,
-                                       GError **error)
+static GSocketAddress* get_proxy_address(SpiceSession *session, guint port, GError **error)
 {
-    SpiceChannelPrivate *c = channel->priv;
-    GSocket *sock = g_socket_new(g_socket_address_get_family(sockaddr),
-                                 G_SOCKET_TYPE_STREAM,
-                                 G_SOCKET_PROTOCOL_DEFAULT,
-                                 error);
+    SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(session);
+    GSocketAddress *address = NULL;
+    const gchar *proxy = g_getenv("SPICE_PROXY");
+    GList *addresses = NULL, *it;
 
-    if (!sock)
+    if (proxy == NULL)
         return NULL;
 
-    g_socket_set_blocking(sock, FALSE);
-    g_socket_set_keepalive(sock, TRUE);
+    /* FIXME: use GUri when it is ready... */
+    if (g_ascii_strncasecmp("http://", proxy, 7) == 0)
+        proxy += 7;
+
+    gchar **proxyv = g_strsplit(proxy, ":", 0);
+    guint16 pport = 3128;
+    const gchar *proxy_host = NULL, *proxy_port = NULL;
+
+    proxy_host = proxyv[0];
+    if (proxy_host != NULL)
+        proxy_port = proxyv[1];
+
+    if (proxy_port != NULL) {
+        char *endptr;
+        pport = strtoul(proxy_port, &endptr, 10);
+        if (*endptr != '\0') {
+            g_set_error(error, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                        "Invalid proxy port: %s", proxy_port);
+            goto end;
+        }
+    }
 
-    if (!g_socket_connect(sock, sockaddr, NULL, error)) {
-        if (*error && (*error)->code == G_IO_ERROR_PENDING) {
-            g_clear_error(error);
-            SPICE_DEBUG("Socket pending");
-            g_coroutine_socket_wait(&c->coroutine, sock, G_IO_OUT | G_IO_ERR | G_IO_HUP);
+    addresses = g_resolver_lookup_by_name(g_resolver_get_default(),
+                                          proxy_host, NULL, error);
+    if (*error != NULL)
+        goto end;
 
-            if (!g_socket_check_connect_result(sock, error)) {
-                SPICE_DEBUG("Failed to connect %s", (*error)->message);
-                g_object_unref(sock);
-                return NULL;
-            }
-        } else {
-            SPICE_DEBUG("Socket error: %s", *error ? (*error)->message : "unknown");
-            g_object_unref(sock);
-            return NULL;
-        }
+    /* FIXME: iterate over all addresses
+     * gproxy makes it quite unconvenient to deal with hostname,
+     * it would make sense to do it once earlier?
+     */
+    for (it = addresses; it != NULL; it = it->next) {
+        address = g_proxy_address_new(G_INET_ADDRESS(it->data), pport, "http",
+                                      s->host, port, NULL, NULL);
+        if (address != NULL)
+            break;
     }
 
-    SPICE_DEBUG("Finally connected");
+end:
+    g_resolver_free_addresses(addresses);
+    g_strfreev(proxyv);
 
-    return sock;
+    return address;
 }
 
 /* coroutine context */
@@ -1556,11 +1572,12 @@ GSocket* spice_session_channel_open_host(SpiceSession *session, SpiceChannel *ch
                                          gboolean use_tls)
 {
     SpiceSessionPrivate *s = SPICE_SESSION_GET_PRIVATE(session);
-    GSocketConnectable *addr;
-    GSocketAddressEnumerator *enumerator;
-    GSocketAddress *sockaddr;
-    GError *conn_error = NULL;
-    GSocket *sock = NULL;
+    GSocketClient *client = NULL;
+    GSocketConnection *connection = NULL;
+    GSocketAddress *address = NULL;
+    GSocketConnectable *connectable = NULL;
+    GSocket *socket = NULL;
+    GError *error = NULL;
     int port;
 
     if ((use_tls && !s->tls_port) || (!use_tls && !s->port))
@@ -1568,29 +1585,34 @@ GSocket* spice_session_channel_open_host(SpiceSession *session, SpiceChannel *ch
 
     port = atoi(use_tls ? s->tls_port : s->port);
 
-    SPICE_DEBUG("Resolving host %s %d", s->host, port);
+    /* FIXME: make all of this async again */
+    client = g_socket_client_new();
+    address = get_proxy_address(session, port, &error);
+    if (error != NULL) {
+        g_critical("%s", error->message);
+        goto end;
+    }
 
-    addr = g_network_address_new(s->host, port);
+    if (address != NULL)
+        connectable = G_SOCKET_CONNECTABLE(address);
+    else
+        connectable = g_network_address_new(s->host, port);
 
-    enumerator = g_socket_connectable_enumerate (addr);
-    g_object_unref (addr);
+    connection = g_socket_client_connect(client, connectable, NULL, &error);
+    if (connection == NULL)
+        goto end;
 
-    /* Try each sockaddr until we succeed. Record the first
-     * connection error, but not any further ones (since they'll probably
-     * be basically the same as the first).
-     */
-    while (!sock &&
-           (sockaddr = g_socket_address_enumerator_next(enumerator, NULL, &conn_error))) {
-        SPICE_DEBUG("Trying one socket");
-        g_clear_error(&conn_error);
-        sock = channel_connect_socket(channel, sockaddr, &conn_error);
-        if (conn_error != NULL)
-            SPICE_DEBUG("%s", conn_error->message);
-        g_object_unref(sockaddr);
-    }
-    g_object_unref(enumerator);
-    g_clear_error(&conn_error);
-    return sock;
+    socket = g_socket_connection_get_socket(connection);
+    g_socket_set_blocking(socket, FALSE);
+    g_socket_set_keepalive(socket, TRUE);
+    g_object_ref(socket);
+
+end:
+    g_clear_object(&connection);
+    g_clear_object(&connectable);
+    g_clear_object(&client);
+
+    return socket;
 }
 
 
-- 
1.7.11.4



More information about the Spice-devel mailing list