build: Require at least Libassuan 2.4.1.
[gnupg.git] / dirmngr / http.c
index e33a594..74b6911 100644 (file)
 #ifdef USE_NPTH
 # define my_select(a,b,c,d,e)  npth_select ((a), (b), (c), (d), (e))
 # define my_accept(a,b,c)      npth_accept ((a), (b), (c))
-# define my_unprotect()        npth_unprotect ()
-# define my_protect()          npth_protect ()
 #else
 # define my_select(a,b,c,d,e)  select ((a), (b), (c), (d), (e))
 # define my_accept(a,b,c)      accept ((a), (b), (c))
-# define my_unprotect()        do { } while(0)
-# define my_protect()          do { } while(0)
 #endif
 
 #ifdef HAVE_W32_SYSTEM
@@ -173,8 +169,9 @@ static assuan_fd_t connect_server (const char *server, unsigned short port,
                                    int *r_host_not_found);
 static gpg_error_t write_server (int sock, const char *data, size_t length);
 
-static ssize_t cookie_read (void *cookie, void *buffer, size_t size);
-static ssize_t cookie_write (void *cookie, const void *buffer, size_t size);
+static gpgrt_ssize_t cookie_read (void *cookie, void *buffer, size_t size);
+static gpgrt_ssize_t cookie_write (void *cookie,
+                                   const void *buffer, size_t size);
 static int cookie_close (void *cookie);
 
 
@@ -511,6 +508,27 @@ http_register_tls_ca (const char *fname)
 }
 
 
+#ifdef USE_TLS
+/* Free the TLS session associated with SESS, if any.  */
+static void
+close_tls_session (http_session_t sess)
+{
+  if (sess->tls_session)
+    {
+# ifdef HTTP_USE_GNUTLS
+      my_socket_t sock = gnutls_transport_get_ptr (sess->tls_session);
+      my_socket_unref (sock, NULL, NULL);
+      gnutls_deinit (sess->tls_session);
+      if (sess->certcred)
+        gnutls_certificate_free_credentials (sess->certcred);
+# endif /*HTTP_USE_GNUTLS*/
+      xfree (sess->servername);
+      sess->tls_session = NULL;
+    }
+}
+#endif /*USE_TLS*/
+
+
 /* Release a session.  Take care not to release it while it is being
    used by a http context object.  */
 static void
@@ -527,17 +545,7 @@ session_unref (int lnr, http_session_t sess)
     return;
 
 #ifdef USE_TLS
-# ifdef HTTP_USE_GNUTLS
-  if (sess->tls_session)
-    {
-      my_socket_t sock = gnutls_transport_get_ptr (sess->tls_session);
-      my_socket_unref (sock, NULL, NULL);
-      gnutls_deinit (sess->tls_session);
-    }
-  if (sess->certcred)
-    gnutls_certificate_free_credentials (sess->certcred);
-# endif /*HTTP_USE_GNUTLS*/
-  xfree (sess->servername);
+  close_tls_session (sess);
 #endif /*USE_TLS*/
 
   xfree (sess);
@@ -745,9 +753,7 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port,
     {
       int mode;
 
-#if ASSUAN_VERSION_NUMBER >= 0x020300 /* >= 2.3.0 */
       if (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode)
-#endif
         {
           log_error ("Tor support is not available\n");
           return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
@@ -1078,6 +1084,7 @@ do_parse_uri (parsed_uri_t uri, int only_local_part,
   uri->is_http = 0;
   uri->opaque = 0;
   uri->v6lit = 0;
+  uri->onion = 0;
 
   /* A quick validity check. */
   if (strspn (p, VALID_URI_CHARS) != n)
@@ -1164,49 +1171,54 @@ do_parse_uri (parsed_uri_t uri, int only_local_part,
         {
           uri->opaque = 1;
           uri->path = p;
+          if (is_onion_address (uri->path))
+            uri->onion = 1;
           return 0;
         }
 
     } /* End global URI part. */
 
-  /* Parse the pathname part */
-  if (!p || !*p)
-    return 0;  /* We don't have a path.  Okay. */
-
-  /* TODO: Here we have to check params. */
-
-  /* Do we have a query part? */
-  if ((p2 = strchr (p, '?')))
-    *p2++ = 0;
-
-  uri->path = p;
-  if ((n = remove_escapes (p)) < 0)
-    return GPG_ERR_BAD_URI;
-  if (n != strlen (p))
-    return GPG_ERR_BAD_URI;    /* Path includes a Nul. */
-  p = p2 ? p2 : NULL;
-
-  if (!p || !*p)
-    return 0; /* We don't have a query string.  Okay. */
-
-  /* Now parse the query string. */
-  tail = &uri->query;
-  for (;;)
+  /* Parse the pathname part if any.  */
+  if (p && *p)
     {
-      uri_tuple_t elem;
+      /* TODO: Here we have to check params. */
 
-      if ((p2 = strchr (p, '&')))
-       *p2++ = 0;
-      if (!(elem = parse_tuple (p)))
-       return GPG_ERR_BAD_URI;
-      *tail = elem;
-      tail = &elem->next;
+      /* Do we have a query part? */
+      if ((p2 = strchr (p, '?')))
+        *p2++ = 0;
 
-      if (!p2)
-       break; /* Ready. */
-      p = p2;
+      uri->path = p;
+      if ((n = remove_escapes (p)) < 0)
+        return GPG_ERR_BAD_URI;
+      if (n != strlen (p))
+        return GPG_ERR_BAD_URI;        /* Path includes a Nul. */
+      p = p2 ? p2 : NULL;
+
+      /* Parse a query string if any.  */
+      if (p && *p)
+        {
+          tail = &uri->query;
+          for (;;)
+            {
+              uri_tuple_t elem;
+
+              if ((p2 = strchr (p, '&')))
+                *p2++ = 0;
+              if (!(elem = parse_tuple (p)))
+                return GPG_ERR_BAD_URI;
+              *tail = elem;
+              tail = &elem->next;
+
+              if (!p2)
+                break; /* Ready. */
+              p = p2;
+            }
+        }
     }
 
+  if (is_onion_address (uri->host))
+    uri->onion = 1;
+
   return 0;
 }
 
@@ -1467,9 +1479,7 @@ send_request (http_t hd, const char *httphost, const char *auth,
     {
       int mode;
 
-#if ASSUAN_VERSION_NUMBER >= 0x020300 /* >= 2.3.0 */
       if (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode)
-#endif
         {
           log_error ("Tor support is not available\n");
           return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
@@ -2217,11 +2227,8 @@ connect_server (const char *server, unsigned short port,
     {
 #ifdef ASSUAN_SOCK_TOR
 
-      my_unprotect ();
       sock = assuan_sock_connect_byname (server, port, 0, NULL,
                                          ASSUAN_SOCK_TOR);
-      my_protect ();
-
       if (sock == ASSUAN_INVALID_FD)
         {
           if (errno == EHOSTUNREACH)
@@ -2316,9 +2323,7 @@ connect_server (const char *server, unsigned short port,
             }
 
           anyhostaddr = 1;
-          my_unprotect ();
           ret = assuan_sock_connect (sock, ai->addr, ai->addrlen);
-          my_protect ();
           if (ret)
             last_errno = errno;
           else
@@ -2413,7 +2418,7 @@ write_server (int sock, const char *data, size_t length)
 
 \f
 /* Read handler for estream.  */
-static ssize_t
+static gpgrt_ssize_t
 cookie_read (void *cookie, void *buffer, size_t size)
 {
   cookie_t c = cookie;
@@ -2447,6 +2452,13 @@ cookie_read (void *cookie, void *buffer, size_t size)
             }
           if (nread == GNUTLS_E_REHANDSHAKE)
             goto again; /* A client is allowed to just ignore this request. */
+          if (nread == GNUTLS_E_PREMATURE_TERMINATION)
+            {
+              /* The server terminated the connection.  Close the TLS
+                 session, and indicate EOF using a short read.  */
+              close_tls_session (c->session);
+              return 0;
+            }
           log_info ("TLS network read failed: %s\n", gnutls_strerror (nread));
           gpg_err_set_errno (EIO);
           return -1;
@@ -2488,11 +2500,11 @@ cookie_read (void *cookie, void *buffer, size_t size)
         c->content_length = 0;
     }
 
-  return nread;
+  return (gpgrt_ssize_t)nread;
 }
 
 /* Write handler for estream.  */
-static ssize_t
+static gpgrt_ssize_t
 cookie_write (void *cookie, const void *buffer_arg, size_t size)
 {
   const char *buffer = buffer_arg;
@@ -2541,7 +2553,7 @@ cookie_write (void *cookie, const void *buffer_arg, size_t size)
         nwritten = size;
     }
 
-  return nwritten;
+  return (gpgrt_ssize_t)nwritten;
 }