changeset 1153:efb854e7b418

proxy support for HTTPS
author corvid <corvid@lavabit.com>
date Mon, 01 Jun 2009 01:29:42 +0000
parents 523f2873b999
children 97cac73b5c71
files ChangeLog dillorc dpi/https.c src/IO/Url.h src/IO/http.c src/capi.c
diffstat 6 files changed, 132 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun May 31 17:38:42 2009 -0400
+++ b/ChangeLog	Mon Jun 01 01:29:42 2009 +0000
@@ -37,6 +37,7 @@
  - Added "View Stylesheets" to the page menu.
  - Remove standard_widget_colors dillorc option.
  - Added dillo(1) man page.
+ - Proxy support for HTTPS.
    Patches: place (AKA corvid)
 +- Switched SSL-enabled to configure.in (./configure --enable-ssl).
  - Standardised the installation of dpid/dpidrc with auto* tools.
--- a/dillorc	Sun May 31 17:38:42 2009 -0400
+++ b/dillorc	Mon Jun 01 01:29:42 2009 +0000
@@ -115,9 +115,8 @@
 
 # Set the proxy information for http.
 # Note that the http_proxy environment variable overrides this setting.
-# WARNING: - HTTPS does not currently use the proxy settings.
-#          - FTP and downloads plugins use wget. To use a proxy with them,
-#            you will need to configure wget accordingly.
+# WARNING: FTP and downloads plugins use wget. To use a proxy with them,
+#          you will need to configure wget accordingly.
 # http_proxy="http://localhost:8080/"
 #(by default, no proxy is used)
 
--- a/dpi/https.c	Sun May 31 17:38:42 2009 -0400
+++ b/dpi/https.c	Mon Jun 01 01:29:42 2009 +0000
@@ -129,7 +129,8 @@
    SSL_CTX * ssl_context = NULL;
    SSL * ssl_connection = NULL;
 
-   char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL;
+   char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL,
+        *proxy_url = NULL, *proxy_connect = NULL;
    char buf[4096];
    int retval = 0;
    int network_socket = -1;
@@ -194,6 +195,9 @@
       /*Get the network address and command to be used*/
       dpip_tag = sock_handler_read(sh);
       cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
+      proxy_url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "proxy_url");
+      proxy_connect =
+                  a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "proxy_connect");
       url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
       http_query = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "query");
 
@@ -205,13 +209,66 @@
    }
 
    if (exit_error == 0){
-      network_socket = get_network_connection(url);
+      char *connect_url = proxy_url ? proxy_url : url;
+
+      network_socket = get_network_connection(connect_url);
       if (network_socket<0){
          MSG("Network socket create error\n");
          exit_error = 1;
       }
    }
 
+   if (exit_error == 0 && proxy_connect != NULL) {
+      ssize_t St;
+      const char *p = proxy_connect;
+      int writelen = strlen(proxy_connect);
+
+      while (writelen > 0) {
+         St = write(network_socket, p, writelen);
+         if (St < 0) {
+            /* Error */
+            if (errno != EINTR) {
+               MSG("Error writing to proxy.\n");
+               exit_error = 1;
+               break;
+            }
+         } else {
+            p += St;
+            writelen -= St;
+         }
+      }
+      if (exit_error == 0) {
+         const size_t buflen = 200;
+         char buf[buflen];
+         Dstr *reply = dStr_new("");
+
+         while (1) {
+            St = read(network_socket, buf, buflen);
+            if (St > 0) {
+               dStr_append_l(reply, buf, St);
+               if (strstr(reply->str, "\r\n\r\n")) {
+                  /* have whole reply header */
+                  if (reply->len >= 12 && reply->str[9] == '2') {
+                     /* e.g. "HTTP/1.1 200 Connection established[...]" */
+                     MSG("CONNECT through proxy succeeded.\n");
+                  } else {
+                     /* TODO: send reply body to dillo */
+                     exit_error = 1;
+                     MSG("CONNECT through proxy failed.\n");
+                  }
+                  break;
+               }
+            } else if (St < 0) {
+               if (errno != EINTR) {
+                  exit_error = 1;
+                  MSG("Error reading from proxy.\n");
+                  break;
+               }
+            }
+         }
+         dStr_free(reply, 1);
+      }
+   }
 
    if (exit_error == 0){
       /* Configure SSL to use network file descriptor */
@@ -263,6 +320,8 @@
    dFree(cmd);
    dFree(url);
    dFree(http_query);
+   dFree(proxy_url);
+   dFree(proxy_connect);
 
    if (network_socket != -1){
       close(network_socket);
@@ -297,6 +356,9 @@
    /*Determine how much of url we chop off as unneeded*/
    if (dStrncasecmp(url, "https://", 8) == 0){
       url_offset = 8;
+   } else if (dStrncasecmp(url, "http://", 7) == 0) {
+      url_offset = 7;
+      portnum = 80;
    }
 
    /*Find end of URL*/
--- a/src/IO/Url.h	Sun May 31 17:38:42 2009 -0400
+++ b/src/IO/Url.h	Mon Jun 01 01:29:42 2009 +0000
@@ -16,6 +16,8 @@
 int a_Http_init(void);
 int a_Http_proxy_auth(void);
 void a_Http_set_proxy_passwd(const char *str);
+char *a_Http_make_connect_str(const DilloUrl *url);
+const char *a_Http_get_proxy_urlstr();
 Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy);
 
 void a_Http_ccc (int Op, int Branch, int Dir, ChainLink *Info,
--- a/src/IO/http.c	Sun May 31 17:38:42 2009 -0400
+++ b/src/IO/http.c	Mon Jun 01 01:29:42 2009 +0000
@@ -16,6 +16,7 @@
 
 #include <config.h>
 
+#include <ctype.h>              /* isdigit */
 #include <unistd.h>
 #include <errno.h>              /* for errno */
 #include <stdlib.h>
@@ -415,6 +416,56 @@
 }
 
 /*
+ * Return a new string for the request used to tunnel HTTPS through a proxy.
+ * As of 2009, the best reference appears to be section 5 of RFC 2817.
+ */
+char *a_Http_make_connect_str(const DilloUrl *url)
+{
+   Dstr *dstr;
+   const char *auth1;
+   int auth_len;
+   char *auth2, *proxy_auth, *retstr;
+
+   dReturn_val_if_fail(Http_must_use_proxy(url), NULL);
+
+   dstr = dStr_new("");
+   auth1 = URL_AUTHORITY(url);
+   auth_len = strlen(auth1);
+   if (auth_len > 0 && !isdigit(auth1[auth_len - 1]))
+      /* if no port number, add HTTPS port */
+      auth2 = dStrconcat(auth1, ":443", NULL);
+   else
+      auth2 = dStrdup(auth1);
+   proxy_auth = HTTP_Proxy_Auth_base64 ?
+                   dStrconcat ("Proxy-Authorization: Basic ",
+                               HTTP_Proxy_Auth_base64, "\r\n", NULL) :
+                   dStrdup("");
+   dStr_sprintfa(
+      dstr,
+      "CONNECT %s HTTP/1.1\r\n"
+      "Host: %s\r\n"
+      "%s"
+      "\r\n",
+      auth2, 
+      auth2,
+      proxy_auth);
+
+   dFree(auth2);
+   dFree(proxy_auth);
+   retstr = dstr->str;
+   dStr_free(dstr, 0);
+   return retstr;
+}
+
+/*
+ * Return URL string of HTTP proxy, if any
+ */
+const char *a_Http_get_proxy_urlstr()
+{
+   return HTTP_Proxy ? URL_STR(HTTP_Proxy) : NULL;
+}
+
+/*
  * Callback function for the DNS resolver.
  * Continue connecting the socket, or abort upon error condition.
  * S->web is checked to assert the operation wasn't aborted while waiting.
--- a/src/capi.c	Sun May 31 17:38:42 2009 -0400
+++ b/src/capi.c	Mon Jun 01 01:29:42 2009 +0000
@@ -265,7 +265,6 @@
 
 /*
  * Build the dpip command tag, according to URL and server.
- * TODO: make it PROXY-aware (AFAIS, it should be easy)
  */
 static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
 {
@@ -273,10 +272,20 @@
 
    if (strcmp(server, "proto.https") == 0) {
       /* Let's be kind and make the HTTP query string for the dpi */
+      char *proxy_connect = a_Http_make_connect_str(web->url);
       Dstr *http_query = a_Http_make_query_str(web->url, FALSE);
       /* BUG: embedded NULLs in query data will truncate message */
-      cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s",
-                             "open_url", URL_STR(web->url), http_query->str);
+      if (proxy_connect) {
+         const char *proxy_urlstr = a_Http_get_proxy_urlstr();
+         cmd = a_Dpip_build_cmd("cmd=%s proxy_url=%s proxy_connect=%s "
+                                "url=%s query=%s", "open_url", proxy_urlstr,
+                                proxy_connect, URL_STR(web->url),
+                                http_query->str);
+      } else {
+         cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s",
+                                "open_url", URL_STR(web->url),http_query->str);
+      }
+      dFree(proxy_connect);
       dStr_free(http_query, 1);
 
    } else if (strcmp(server, "downloads") == 0) {