Merge T3490-proposal1 into master
[gnupg.git] / dirmngr / http.c
index 0544c9b..8e778df 100644 (file)
@@ -955,6 +955,10 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port,
           log_error ("Tor support is not available\n");
           return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
         }
+      /* Non-blocking connects do not work with our Tor proxy because
+       * we can't continue the Socks protocol after the EINPROGRESS.
+       * Disable the timeout to use a blocking connect.  */
+      timeout = 0;
     }
 
   /* Create the handle. */
@@ -1064,6 +1068,7 @@ http_wait_response (http_t hd)
 {
   gpg_error_t err;
   cookie_t cookie;
+  int use_tls;
 
   /* Make sure that we are in the data. */
   http_start_data (hd);
@@ -1074,6 +1079,7 @@ http_wait_response (http_t hd)
   if (!cookie)
     return gpg_err_make (default_errsource, GPG_ERR_INTERNAL);
 
+  use_tls = cookie->use_tls;
   es_fclose (hd->fp_write);
   hd->fp_write = NULL;
   /* The close has released the cookie and thus we better set it to NULL.  */
@@ -1092,7 +1098,7 @@ http_wait_response (http_t hd)
     return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
   cookie->sock = my_socket_ref (hd->sock);
   cookie->session = http_session_ref (hd->session);
-  cookie->use_tls = hd->uri->use_tls;
+  cookie->use_tls = use_tls;
 
   hd->read_cookie = cookie;
   hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
@@ -1219,14 +1225,16 @@ parse_uri (parsed_uri_t *ret_uri, const char *uri,
 {
   gpg_err_code_t ec;
 
-  *ret_uri = xtrycalloc (1, sizeof **ret_uri + strlen (uri));
+  *ret_uri = xtrycalloc (1, sizeof **ret_uri + 2 * strlen (uri) + 1);
   if (!*ret_uri)
     return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
   strcpy ((*ret_uri)->buffer, uri);
+  strcpy ((*ret_uri)->buffer + strlen (uri) + 1, uri);
+  (*ret_uri)->original = (*ret_uri)->buffer + strlen (uri) + 1;
   ec = do_parse_uri (*ret_uri, 0, no_scheme_check, force_tls);
   if (ec)
     {
-      xfree (*ret_uri);
+      http_release_parsed_uri (*ret_uri);
       *ret_uri = NULL;
     }
   return gpg_err_make (default_errsource, ec);
@@ -1255,6 +1263,11 @@ http_release_parsed_uri (parsed_uri_t uri)
     {
       uri_tuple_t r, r2;
 
+      for (r = uri->params; r; r = r2)
+       {
+         r2 = r->next;
+         xfree (r);
+       }
       for (r = uri->query; r; r = r2)
        {
          r2 = r->next;
@@ -1663,6 +1676,9 @@ send_request (http_t hd, const char *httphost, const char *auth,
   char *proxy_authstr = NULL;
   char *authstr = NULL;
   assuan_fd_t sock;
+#ifdef USE_TLS
+  int have_http_proxy = 0;
+#endif
 
   if (hd->uri->use_tls && !hd->session)
     {
@@ -1686,6 +1702,10 @@ send_request (http_t hd, const char *httphost, const char *auth,
           log_error ("Tor support is not available\n");
           return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
         }
+      /* Non-blocking connects do not work with our Tor proxy because
+       * we can't continue the Socks protocol after the EINPROGRESS.
+       * Disable the timeout to use a blocking connect.  */
+      timeout = 0;
     }
 
   server = *hd->uri->host ? hd->uri->host : "localhost";
@@ -1749,9 +1769,12 @@ send_request (http_t hd, const char *httphost, const char *auth,
 
       if (err)
         ;
-      else if (!strcmp (uri->scheme, "http") || !strcmp (uri->scheme, "socks4"))
-        ;
-      else if (!strcmp (uri->scheme, "socks5h"))
+#ifdef USE_TLS
+      else if (!strcmp (uri->scheme, "http"))
+        have_http_proxy = 1;
+#endif
+      else if (!strcmp (uri->scheme, "socks4")
+               || !strcmp (uri->scheme, "socks5h"))
         err = gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
       else
         err = gpg_err_make (default_errsource, GPG_ERR_INV_URI);
@@ -1780,7 +1803,7 @@ send_request (http_t hd, const char *httphost, const char *auth,
 
       err = connect_server (*uri->host ? uri->host : "localhost",
                             uri->port ? uri->port : 80,
-                            hd->flags, srvtag, timeout, &sock);
+                            hd->flags, NULL, timeout, &sock);
       http_release_parsed_uri (uri);
     }
   else
@@ -1800,6 +1823,94 @@ send_request (http_t hd, const char *httphost, const char *auth,
       return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
     }
 
+#if USE_TLS
+  if (have_http_proxy && hd->uri->use_tls)
+    {
+      int saved_flags;
+      cookie_t cookie;
+
+      /* Try to use the CONNECT method to proxy our TLS stream.  */
+      request = es_bsprintf
+        ("CONNECT %s:%hu HTTP/1.0\r\nHost: %s:%hu\r\n%s",
+         httphost ? httphost : server,
+         port,
+         httphost ? httphost : server,
+         port,
+         proxy_authstr ? proxy_authstr : "");
+      xfree (proxy_authstr);
+      proxy_authstr = NULL;
+
+      if (! request)
+        return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+
+      if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
+        log_debug_with_string (request, "http.c:request:");
+
+      cookie = xtrycalloc (1, sizeof *cookie);
+      if (! cookie)
+        {
+          err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+          xfree (request);
+          return err;
+        }
+      cookie->sock = my_socket_ref (hd->sock);
+      hd->write_cookie = cookie;
+
+      hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
+      if (! hd->fp_write)
+        {
+          err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+          my_socket_unref (cookie->sock, NULL, NULL);
+          xfree (cookie);
+          xfree (request);
+          hd->write_cookie = NULL;
+          return err;
+        }
+      else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
+        err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+
+      xfree (request);
+      request = NULL;
+
+      /* Make sure http_wait_response doesn't close the stream.  */
+      saved_flags = hd->flags;
+      hd->flags &= ~HTTP_FLAG_SHUTDOWN;
+
+      /* Get the response.  */
+      err = http_wait_response (hd);
+
+      /* Restore flags, destroy stream.  */
+      hd->flags = saved_flags;
+      es_fclose (hd->fp_read);
+      hd->fp_read = NULL;
+      hd->read_cookie = NULL;
+
+      /* Reset state.  */
+      hd->in_data = 0;
+
+      if (err)
+        return err;
+
+      if (hd->status_code != 200)
+        {
+          request = es_bsprintf
+            ("CONNECT %s:%hu",
+             httphost ? httphost : server,
+             port);
+
+          log_error (_("error accessing '%s': http status %u\n"),
+                     request ? request : "out of core",
+                     http_get_status_code (hd));
+
+          xfree (request);
+          return gpg_error (GPG_ERR_NO_DATA);
+        }
+
+      /* We are done with the proxy, the code below will establish a
+       * TLS session and talk directly to the target server.  */
+      http_proxy = NULL;
+    }
+#endif /* USE_TLS */
 
 #if HTTP_USE_NTBTLS
   if (hd->uri->use_tls)
@@ -2334,7 +2445,7 @@ parse_response (http_t hd)
       if (!len)
        return GPG_ERR_EOF;
 
-      if ((hd->flags & HTTP_FLAG_LOG_RESP))
+      if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
         log_debug_with_string (line, "http.c:response:\n");
     }
   while (!*line);
@@ -2379,7 +2490,7 @@ parse_response (http_t hd)
       /* Trim line endings of empty lines. */
       if ((*line == '\r' && line[1] == '\n') || *line == '\n')
        *line = 0;
-      if ((hd->flags & HTTP_FLAG_LOG_RESP))
+      if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
         log_info ("http.c:RESP: '%.*s'\n",
                   (int)strlen(line)-(*line&&line[1]?2:0),line);
       if (*line)
@@ -2639,14 +2750,18 @@ connect_with_timeout (assuan_fd_t sock,
       return 0; /* Success.  */
     }
   err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
-  if (gpg_err_code (err) != GPG_ERR_EINPROGRESS)
+  if (gpg_err_code (err) != GPG_ERR_EINPROGRESS
+#ifdef HAVE_W32_SYSTEM
+      && gpg_err_code (err) != GPG_ERR_EAGAIN
+#endif
+      )
     {
       RESTORE_BLOCKING ();
       return err;
     }
 
   FD_ZERO (&rset);
-  FD_SET (sock, &rset);
+  FD_SET (FD2INT (sock), &rset);
   wset = rset;
   tval.tv_sec = timeout / 1000;
   tval.tv_usec = (timeout % 1000) * 1000;
@@ -3183,7 +3298,7 @@ gpg_error_t
 http_verify_server_credentials (http_session_t sess)
 {
 #if HTTP_USE_GNUTLS
-  static const char const errprefix[] = "TLS verification of peer failed";
+  static const char errprefix[] = "TLS verification of peer failed";
   int rc;
   unsigned int status;
   const char *hostname;