Add finger support to dirmngr.
authorWerner Koch <wk@gnupg.org>
Tue, 8 Feb 2011 20:11:19 +0000 (21:11 +0100)
committerWerner Koch <wk@gnupg.org>
Tue, 8 Feb 2011 20:11:19 +0000 (21:11 +0100)
The basic network code from http.c is used for finger.  This keeps the
network related code at one place and we are able to use the somewhat
matured code form http.c.  Unfortunately I had to enhance the http
code for more robustness and probably introduced new bugs.

Test this code using

  gpg --fetch-key finger:wk@g10code.com

(I might be the last user of finger ;-)

14 files changed:
common/ChangeLog
common/estream.c
common/http.c
common/http.h
dirmngr/ChangeLog
dirmngr/Makefile.am
dirmngr/ks-action.c
dirmngr/ks-action.h
dirmngr/ks-engine-finger.c [new file with mode: 0644]
dirmngr/ks-engine.h
dirmngr/server.c
g10/ChangeLog
g10/call-dirmngr.c
g10/keyserver.c

index 647f7d5..4d07a49 100644 (file)
@@ -1,3 +1,25 @@
+2011-02-08  Werner Koch  <wk@g10code.com>
+
+       * http.c (connect_server): Add arg R_HOST_NOT_FOUND.
+
+2011-02-07  Werner Koch  <wk@g10code.com>
+
+       * http.c (my_socket_new, my_socket_ref, my_socket_unref): New.
+       (cookie_close, cookie_read, cookie_write, http_close, _http_open)
+       (send_request): Replace use of an socket integer by the new socket
+       object.
+       (_http_raw_connect): New.
+       (fp_onclose_notification): New.
+       (_http_raw_connect, _http_wait_response, http_close): Register and
+       unregister this notification.
+       * http.h (http_raw_connect): New.
+
+       * http.h (parsed_uri_s): Add field IS_OPAQUE.
+       (http_req_t): Add HTTP_REQ_OPAQUE.
+       * http.c (do_parse_uri): Parse unknown schemes into PATH.
+       (my_socket_new, my_socket_ref, my_socket_unref): New.
+       (send_request): Simplify save_errno stuff.
+
 2011-02-03  Werner Koch  <wk@g10code.com>
 
        * status.h (STATUS_DECRYPTION_INFO): New.
index bc82051..a73d1f2 100644 (file)
@@ -3041,9 +3041,14 @@ es_fclose (estream_t stream)
    already registered notification; for this to work the value of FNC
    and FNC_VALUE must be the same as with the registration and
    FNC_VALUE must be a unique value.  No error will be returned if
-   MODE is 0.  Unregistered should only be used in the error case
-   because it may not remove memory internall allocated for the
-   onclose handler.
+   MODE is 0.
+
+   FIXME: I think the next comment is not anymore correct:
+   Unregister should only be used in the error case because it may not
+   be able to remove memory internally allocated for the onclose
+   handler.
+
+   FIXME: Unregister is not thread safe.
 
    The notification will be called right before the stream is closed.
    It may not call any estream function for STREAM, neither direct nor
index f8628e6..7df8457 100644 (file)
@@ -161,13 +161,25 @@ static char *build_rel_path (parsed_uri_t uri);
 static gpg_error_t parse_response (http_t hd);
 
 static int connect_server (const char *server, unsigned short port,
-                           unsigned int flags, const char *srvtag);
+                           unsigned int flags, const char *srvtag,
+                           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 int cookie_close (void *cookie);
 
+
+/* A socket object used to a allow ref counting of sockets.  */
+struct my_socket_s
+{
+  int fd;       /* The actual socket - shall never be -1.  */
+  int refcount; /* Number of references to this socket.  */
+};
+typedef struct my_socket_s *my_socket_t;
+
+
+/* Cookie function structure and cookie object.  */
 static es_cookie_io_functions_t cookie_functions =
   {
     cookie_read,
@@ -178,8 +190,8 @@ static es_cookie_io_functions_t cookie_functions =
 
 struct cookie_s
 {
-  /* File descriptor or -1 if already closed. */
-  int fd;
+  /* Socket object or NULL if already closed. */
+  my_socket_t sock;
 
   /* TLS session context or NULL if not used. */
   gnutls_session_t tls_session;
@@ -213,7 +225,7 @@ typedef struct header_s *header_t;
 struct http_context_s
 {
   unsigned int status_code;
-  int sock;
+  my_socket_t sock;
   unsigned int in_data:1;
   unsigned int is_http_0_9:1;
   estream_t fp_read;
@@ -279,6 +291,77 @@ init_sockets (void)
 #endif /*HAVE_W32_SYSTEM && !HTTP_NO_WSASTARTUP*/
 
 
+/* Create a new socket object.  Returns NULL and closes FD if not
+   enough memory is available.  */
+static my_socket_t
+my_socket_new (int fd)
+{
+  my_socket_t so;
+
+  so = xtrymalloc (sizeof *so);
+  if (!so)
+    {
+      int save_errno = errno;
+      sock_close (fd);
+      gpg_err_set_errno (save_errno);
+      return NULL;
+    }
+  so->fd = fd;
+  so->refcount = 1;
+  /* log_debug ("my_socket_new(%d): object %p for fd %d created\n", */
+  /*            lnr, so, so->fd); */
+  return so;
+}
+/* #define my_socket_new(a) _my_socket_new ((a),__LINE__) */
+
+/* Bump up the reference counter for the socket object SO.  */
+static my_socket_t
+my_socket_ref (my_socket_t so)
+{
+  so->refcount++;
+  /* log_debug ("my_socket_ref(%d): object %p for fd %d refcount now %d\n", */
+  /*            lnr, so, so->fd, so->refcount); */
+  return so;
+}
+/* #define my_socket_ref(a) _my_socket_ref ((a),__LINE__) */
+
+/* Bump down the reference counter for the socket object SO.  If SO
+   has no more references, close the socket and release the
+   object.  */
+static void
+my_socket_unref (my_socket_t so)
+{
+  if (so)
+    {
+      so->refcount--;
+      /* log_debug ("my_socket_unref(%d): object %p for fd %d ref now %d\n", */
+      /*            lnr, so, so->fd, so->refcount); */
+      if (!so->refcount)
+        {
+          sock_close (so->fd);
+          xfree (so);
+        }
+    }
+}
+/* #define my_socket_unref(a) _my_socket_unref ((a),__LINE__) */
+
+
+/* This notification function is called by estream whenever stream is
+   closed.  Its purpose is to mark the the closing in the handle so
+   that a http_close won't accidentally close the estream.  The function
+   http_close removes this notification so that it won't be called if
+   http_close was used before an es_fclose.  */
+static void
+fp_onclose_notification (estream_t stream, void *opaque)
+{
+  http_t hd = opaque;
+
+  if (hd->fp_read && hd->fp_read == stream)
+    hd->fp_read = NULL;
+  else if (hd->fp_write && hd->fp_write == stream)
+    hd->fp_write = NULL;
+}
+
 
 /*
  * Helper function to create an HTTP header with hex encoded data.  A
@@ -343,7 +426,7 @@ http_register_tls_callback ( gpg_error_t (*cb) (http_t, void *, int) )
 
 /* Start a HTTP retrieval and return on success in R_HD a context
    pointer for completing the the request and to wait for the
-   response. */
+   response.  */
 gpg_error_t
 _http_open (http_t *r_hd, http_req_t reqtype, const char *url,
             const char *auth, unsigned int flags, const char *proxy,
@@ -362,7 +445,6 @@ _http_open (http_t *r_hd, http_req_t reqtype, const char *url,
   hd = xtrycalloc (1, sizeof *hd);
   if (!hd)
     return gpg_error_from_syserror ();
-  hd->sock = -1;
   hd->req_type = reqtype;
   hd->flags = flags;
   hd->tls_context = tls_context;
@@ -373,8 +455,7 @@ _http_open (http_t *r_hd, http_req_t reqtype, const char *url,
 
   if (err)
     {
-      if (!hd->fp_read && !hd->fp_write && hd->sock != -1)
-        sock_close (hd->sock);
+      my_socket_unref (hd->sock);
       if (hd->fp_read)
         es_fclose (hd->fp_read);
       if (hd->fp_write)
@@ -387,6 +468,105 @@ _http_open (http_t *r_hd, http_req_t reqtype, const char *url,
 }
 
 
+/* This function is useful to connect to a generic TCP service using
+   this http abstraction layer.  This has the advantage of providing
+   service tags and an estream interface.  */
+gpg_error_t
+_http_raw_connect (http_t *r_hd, const char *server, unsigned short port,
+                   unsigned int flags, const char *srvtag,
+                   gpg_err_source_t errsource)
+{
+  gpg_error_t err = 0;
+  int sock;
+  http_t hd;
+  cookie_t cookie;
+  int hnf;
+
+  *r_hd = NULL;
+
+  /* Create the handle. */
+  hd = xtrycalloc (1, sizeof *hd);
+  if (!hd)
+    return gpg_error_from_syserror ();
+  hd->req_type = HTTP_REQ_OPAQUE;
+  hd->flags = flags;
+
+  /* Connect.  */
+  sock = connect_server (server, port, hd->flags, srvtag, &hnf);
+  if (sock == -1)
+    {
+      err = gpg_err_make (errsource, (hnf? GPG_ERR_UNKNOWN_HOST
+                                      :gpg_err_code_from_syserror ()));
+      xfree (hd);
+      return err;
+    }
+  hd->sock = my_socket_new (sock);
+  if (!hd->sock)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      xfree (hd);
+      return err;
+    }
+
+  /* Setup estreams for reading and writing.  */
+  cookie = xtrycalloc (1, sizeof *cookie);
+  if (!cookie)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      goto leave;
+    }
+  cookie->sock = my_socket_ref (hd->sock);
+  hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
+  if (!hd->fp_write)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      my_socket_unref (cookie->sock);
+      xfree (cookie);
+      goto leave;
+    }
+  hd->write_cookie = cookie; /* Cookie now owned by FP_WRITE.  */
+
+  cookie = xtrycalloc (1, sizeof *cookie);
+  if (!cookie)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      goto leave;
+    }
+  cookie->sock = my_socket_ref (hd->sock);
+  hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
+  if (!hd->fp_read)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      my_socket_unref (cookie->sock);
+      xfree (cookie);
+      goto leave;
+    }
+  hd->read_cookie = cookie; /* Cookie now owned by FP_READ.  */
+
+  /* Register close notification to interlock the use of es_fclose in
+     http_close and in user code.  */
+  err = es_onclose (hd->fp_write, 1, fp_onclose_notification, hd);
+  if (!err)
+    err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd);
+
+ leave:
+  if (err)
+    {
+      if (hd->fp_read)
+        es_fclose (hd->fp_read);
+      if (hd->fp_write)
+        es_fclose (hd->fp_write);
+      my_socket_unref (hd->sock);
+      xfree (hd);
+    }
+  else
+    *r_hd = hd;
+  return err;
+}
+
+
+
+
 void
 http_start_data (http_t hd)
 {
@@ -410,12 +590,12 @@ _http_wait_response (http_t hd, gpg_err_source_t errsource)
   /* Make sure that we are in the data. */
   http_start_data (hd);
 
-  /* Close the write stream but keep the socket open.  */
+  /* Close the write stream.  Note that the reference counted socket
+     object keeps the actual system socket open.  */
   cookie = hd->write_cookie;
   if (!cookie)
     return gpg_err_make (errsource, GPG_ERR_INTERNAL);
 
-  cookie->keep_socket = 1;
   es_fclose (hd->fp_write);
   hd->fp_write = NULL;
   /* The close has released the cookie and thus we better set it to NULL.  */
@@ -425,14 +605,14 @@ _http_wait_response (http_t hd, gpg_err_source_t errsource)
      is not required but some very old servers (e.g. the original pksd
      key server didn't worked without it.  */
   if ((hd->flags & HTTP_FLAG_SHUTDOWN))
-    shutdown (hd->sock, 1);
+    shutdown (hd->sock->fd, 1);
   hd->in_data = 0;
 
   /* Create a new cookie and a stream for reading.  */
   cookie = xtrycalloc (1, sizeof *cookie);
   if (!cookie)
     return gpg_err_make (errsource, gpg_err_code_from_syserror ());
-  cookie->fd = hd->sock;
+  cookie->sock = my_socket_ref (hd->sock);
   if (hd->uri->use_tls)
     cookie->tls_session = hd->tls_context;
 
@@ -440,12 +620,18 @@ _http_wait_response (http_t hd, gpg_err_source_t errsource)
   hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
   if (!hd->fp_read)
     {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      my_socket_unref (cookie->sock);
       xfree (cookie);
       hd->read_cookie = NULL;
-      return gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      return err;
     }
 
   err = parse_response (hd);
+
+  if (!err)
+    err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd);
+
   return err;
 }
 
@@ -480,8 +666,15 @@ http_close (http_t hd, int keep_read_stream)
 {
   if (!hd)
     return;
-  if (!hd->fp_read && !hd->fp_write && hd->sock != -1)
-    sock_close (hd->sock);
+
+  /* First remove the close notifications for the streams.  */
+  if (hd->fp_read)
+    es_onclose (hd->fp_read, 0, fp_onclose_notification, hd);
+  if (hd->fp_write)
+    es_onclose (hd->fp_write, 0, fp_onclose_notification, hd);
+
+  /* Now we can close the streams.  */
+  my_socket_unref (hd->sock);
   if (hd->fp_read && !keep_read_stream)
     es_fclose (hd->fp_read);
   if (hd->fp_write)
@@ -577,6 +770,7 @@ do_parse_uri (parsed_uri_t uri, int only_local_part, int no_scheme_check)
   uri->params = uri->query = NULL;
   uri->use_tls = 0;
   uri->is_http = 0;
+  uri->opaque = 0;
 
   /* A quick validity check. */
   if (strspn (p, VALID_URI_CHARS) != n)
@@ -614,14 +808,9 @@ do_parse_uri (parsed_uri_t uri, int only_local_part, int no_scheme_check)
 
       p = p2;
 
-      /* Find the hostname */
-      if (*p != '/')
-       return GPG_ERR_INV_URI; /* Does not start with a slash. */
-
-      p++;
-      if (*p == '/') /* There seems to be a hostname. */
+      if (*p == '/' && p[1] == '/' ) /* There seems to be a hostname. */
        {
-         p++;
+          p += 2;
          if ((p2 = strchr (p, '/')))
            *p2++ = 0;
 
@@ -659,6 +848,15 @@ do_parse_uri (parsed_uri_t uri, int only_local_part, int no_scheme_check)
            return GPG_ERR_BAD_URI;     /* Hostname incudes a Nul. */
          p = p2 ? p2 : NULL;
        }
+      else if (uri->is_http)
+       return GPG_ERR_INV_URI; /* No Leading double slash for HTTP.  */
+      else
+        {
+          uri->opaque = 1;
+          uri->path = p;
+          return 0;
+        }
+
     } /* End global URI part. */
 
   /* Parse the pathname part */
@@ -888,7 +1086,8 @@ send_request (http_t hd, const char *auth,
   const char *http_proxy = NULL;
   char *proxy_authstr = NULL;
   char *authstr = NULL;
-  int save_errno;
+  int sock;
+  int hnf;
 
   tls_session = hd->tls_context;
   if (hd->uri->use_tls && !tls_session)
@@ -906,6 +1105,7 @@ send_request (http_t hd, const char *auth,
             && *http_proxy ))
     {
       parsed_uri_t uri;
+      int save_errno;
 
       if (proxy)
        http_proxy = proxy;
@@ -932,32 +1132,42 @@ send_request (http_t hd, const char *auth,
             }
         }
 
-      hd->sock = connect_server (*uri->host ? uri->host : "localhost",
-                                uri->port ? uri->port : 80,
-                                 hd->flags, srvtag);
+      sock = connect_server (*uri->host ? uri->host : "localhost",
+                             uri->port ? uri->port : 80,
+                             hd->flags, srvtag, &hnf);
       save_errno = errno;
       http_release_parsed_uri (uri);
+      if (sock == -1)
+        gpg_err_set_errno (save_errno);
     }
   else
     {
-      hd->sock = connect_server (server, port, hd->flags, srvtag);
-      save_errno = errno;
+      sock = connect_server (server, port, hd->flags, srvtag, &hnf);
     }
 
-  if (hd->sock == -1)
+  if (sock == -1)
     {
       xfree (proxy_authstr);
-      return gpg_err_make (errsource, (save_errno
-                                       ? gpg_err_code_from_errno (save_errno)
-                                       : GPG_ERR_NOT_FOUND));
+      return gpg_err_make (errsource, (hnf? GPG_ERR_UNKNOWN_HOST
+                                       : gpg_err_code_from_syserror ()));
     }
+  hd->sock = my_socket_new (sock);
+  if (!hd->sock)
+    {
+      xfree (proxy_authstr);
+      return gpg_err_make (errsource, gpg_err_code_from_syserror ());
+    }
+
+
 
 #ifdef HTTP_USE_GNUTLS
   if (hd->uri->use_tls)
     {
       int rc;
 
-      gnutls_transport_set_ptr (tls_session, (gnutls_transport_ptr_t)hd->sock);
+      my_socket_ref (hd->sock);
+      gnutls_transport_set_ptr (tls_session,
+                                (gnutls_transport_ptr_t)(hd->sock->fd));
       do
         {
           rc = gnutls_handshake (tls_session);
@@ -1069,7 +1279,7 @@ send_request (http_t hd, const char *auth,
         err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
         goto leave;
       }
-    cookie->fd = hd->sock;
+    cookie->sock = my_socket_ref (hd->sock);
     hd->write_cookie = cookie;
     if (hd->uri->use_tls)
       cookie->tls_session = tls_session;
@@ -1078,6 +1288,7 @@ send_request (http_t hd, const char *auth,
     if (!hd->fp_write)
       {
         err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+        my_socket_unref (cookie->sock);
         xfree (cookie);
         hd->write_cookie = NULL;
       }
@@ -1469,7 +1680,7 @@ start_server ()
    error.  ERRNO is set on error. */
 static int
 connect_server (const char *server, unsigned short port,
-                unsigned int flags, const char *srvtag)
+                unsigned int flags, const char *srvtag, int *r_host_not_found)
 {
   int sock = -1;
   int srvcount = 0;
@@ -1483,6 +1694,7 @@ connect_server (const char *server, unsigned short port,
   /* Not currently using the flags */
   (void)flags;
 
+  *r_host_not_found = 0;
 #ifdef HAVE_W32_SYSTEM
 
 #ifndef HTTP_NO_WSASTARTUP
@@ -1655,6 +1867,8 @@ connect_server (const char *server, unsigned short port,
                  server,
                  hostfound? strerror (last_errno):"host not found");
 #endif
+      if (!hostfound)
+        *r_host_not_found = 1;
       if (sock != -1)
        sock_close (sock);
       gpg_err_set_errno (last_errno);
@@ -1758,12 +1972,12 @@ cookie_read (void *cookie, void *buffer, size_t size)
       do
         {
 #ifdef HAVE_PTH
-          nread = pth_read (c->fd, buffer, size);
+          nread = pth_read (c->sock->fd, buffer, size);
 #elif defined(HAVE_W32_SYSTEM)
           /* Under Windows we need to use recv for a socket.  */
-          nread = recv (c->fd, buffer, size, 0);
+          nread = recv (c->sock->fd, buffer, size, 0);
 #else
-          nread = read (c->fd, buffer, size);
+          nread = read (c->sock->fd, buffer, size);
 #endif
         }
       while (nread == -1 && errno == EINTR);
@@ -1819,7 +2033,7 @@ cookie_write (void *cookie, const void *buffer, size_t size)
   else
 #endif /*HTTP_USE_GNUTLS*/
     {
-      if ( write_server (c->fd, buffer, size) )
+      if ( write_server (c->sock->fd, buffer, size) )
         {
           gpg_err_set_errno (EIO);
           nwritten = -1;
@@ -1844,28 +2058,29 @@ cookie_close (void *cookie)
   if (c->tls_session && !c->keep_socket)
     {
       gnutls_bye (c->tls_session, GNUTLS_SHUT_RDWR);
+      my_socket_unref (c->sock);
     }
 #endif /*HTTP_USE_GNUTLS*/
-  if (c->fd != -1 && !c->keep_socket)
-    sock_close (c->fd);
+  if (c->sock && !c->keep_socket)
+    my_socket_unref (c->sock);
 
   xfree (c);
   return 0;
 }
 
 
-
 \f
 /**** Test code ****/
 #ifdef TEST
 
+#ifdef HTTP_USE_GNUTLS
 static gpg_error_t
 verify_callback (http_t hd, void *tls_context, int reserved)
 {
   log_info ("verification of certificates skipped\n");
   return 0;
 }
-
+#endif /*HTTP_USE_GNUTLS*/
 
 
 /* static void */
@@ -1938,7 +2153,7 @@ main (int argc, char **argv)
   http_register_tls_callback (verify_callback);
 #endif /*HTTP_USE_GNUTLS*/
 
-  rc = http_parse_uri (&uri, *argv, 0);
+  rc = http_parse_uri (&uri, *argv, 1);
   if (rc)
     {
       log_error ("`%s': %s\n", *argv, gpg_strerror (rc));
@@ -1946,35 +2161,41 @@ main (int argc, char **argv)
     }
 
   printf ("Scheme: %s\n", uri->scheme);
-  printf ("Host  : %s\n", uri->host);
-  printf ("Port  : %u\n", uri->port);
-  printf ("Path  : %s\n", uri->path);
-  for (r = uri->params; r; r = r->next)
-    {
-      printf ("Params: %s", r->name);
-      if (!r->no_value)
-       {
-         printf ("=%s", r->value);
-         if (strlen (r->value) != r->valuelen)
-           printf (" [real length=%d]", (int) r->valuelen);
-       }
-      putchar ('\n');
-    }
-  for (r = uri->query; r; r = r->next)
+  if (uri->opaque)
+    printf ("Value : %s\n", uri->path);
+  else
     {
-      printf ("Query : %s", r->name);
-      if (!r->no_value)
-       {
-         printf ("=%s", r->value);
-         if (strlen (r->value) != r->valuelen)
-           printf (" [real length=%d]", (int) r->valuelen);
-       }
-      putchar ('\n');
+      printf ("Auth  : %s\n", uri->auth? uri->auth:"[none]");
+      printf ("Host  : %s\n", uri->host);
+      printf ("Port  : %u\n", uri->port);
+      printf ("Path  : %s\n", uri->path);
+      for (r = uri->params; r; r = r->next)
+        {
+          printf ("Params: %s", r->name);
+          if (!r->no_value)
+            {
+              printf ("=%s", r->value);
+              if (strlen (r->value) != r->valuelen)
+                printf (" [real length=%d]", (int) r->valuelen);
+            }
+          putchar ('\n');
+        }
+      for (r = uri->query; r; r = r->next)
+        {
+          printf ("Query : %s", r->name);
+          if (!r->no_value)
+            {
+              printf ("=%s", r->value);
+              if (strlen (r->value) != r->valuelen)
+                printf (" [real length=%d]", (int) r->valuelen);
+            }
+          putchar ('\n');
+        }
     }
   http_release_parsed_uri (uri);
   uri = NULL;
 
-  rc = http_open_document (&hd, *argv, NULL, 0, NULL, tls_session);
+  rc = http_open_document (&hd, *argv, NULL, 0, NULL, tls_session, NULL, NULL);
   if (rc)
     {
       log_error ("can't get `%s': %s\n", *argv, gpg_strerror (rc));
@@ -2010,6 +2231,6 @@ main (int argc, char **argv)
 
 /*
 Local Variables:
-compile-command: "gcc -I.. -I../gl -DTEST -DHAVE_CONFIG_H -Wall -O2 -g -o http-test http.c -L. -lcommon -L../jnlib -ljnlib -lgcrypt -lpth -lgnutls"
+compile-command: "gcc -I.. -I../gl -DTEST -DHAVE_CONFIG_H -Wall -O2 -g -o http-test http.c -L. -lcommon -lgcrypt -lpth -lgnutls"
 End:
 */
index 50c478c..7a11b84 100644 (file)
@@ -40,7 +40,8 @@ struct parsed_uri_s
   char *scheme;                /* Pointer to the scheme string (always lowercase). */
   unsigned int is_http:1; /* This is a HTTP style URI.   */
   unsigned int use_tls:1; /* Whether TLS should be used. */
-  char *auth;           /* username/password for basic auth */
+  unsigned int opaque:1;/* Unknown scheme; PATH has the rest.  */
+  char *auth;           /* username/password for basic auth.  */
   char *host;          /* Host (converted to lowercase). */
   unsigned short port;  /* Port (always set if the host is set). */
   char *path;          /* Path. */
@@ -54,7 +55,8 @@ typedef enum
   {
     HTTP_REQ_GET  = 1,
     HTTP_REQ_HEAD = 2,
-    HTTP_REQ_POST = 3
+    HTTP_REQ_POST = 3,
+    HTTP_REQ_OPAQUE = 4  /* Internal use.  */
   }
 http_req_t;
 
@@ -79,6 +81,13 @@ gpg_error_t _http_parse_uri (parsed_uri_t *ret_uri, const char *uri,
 
 void http_release_parsed_uri (parsed_uri_t uri);
 
+gpg_error_t _http_raw_connect (http_t *r_hd,
+                               const char *server, unsigned short port,
+                               unsigned int flags, const char *srvtag,
+                               gpg_err_source_t errsource);
+#define http_raw_connect(a,b,c,d,e) \
+  _http_raw_connect ((a),(b),(c),(d),(e), GPG_ERR_SOURCE_DEFAULT)
+
 gpg_error_t _http_open (http_t *r_hd, http_req_t reqtype,
                         const char *url,
                         const char *auth,
index 3a01b97..1e575e1 100644 (file)
@@ -1,3 +1,9 @@
+2011-02-08  Werner Koch  <wk@g10code.com>
+
+       * server.c (cmd_ks_fetch): New.
+       * ks-action.c (ks_action_fetch): New.
+       * ks-engine-finger.c: New.
+
 2011-02-03  Werner Koch  <wk@g10code.com>
 
        * Makefile.am (dirmngr_LDADD): Remove -llber.
index d5aecc7..a030f38 100644 (file)
@@ -50,7 +50,8 @@ dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c  \
        ldapserver.h ldapserver.c certcache.c certcache.h \
        cdb.h cdblib.c ldap.c misc.c dirmngr-err.h w32-ldap-help.h \
        ocsp.c ocsp.h validate.c validate.h ldap-wrapper.h $(ldap_url) \
-       ks-action.c ks-action.h ks-engine.h ks-engine-hkp.c
+       ks-action.c ks-action.h ks-engine.h \
+        ks-engine-hkp.c ks-engine-finger.c
 
 if USE_LDAPWRAPPER
 dirmngr_SOURCES += ldap-wrapper.c
index 50f0d50..dff4997 100644 (file)
@@ -132,7 +132,7 @@ ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
               else
                 {
                   err = copy_stream (infp, outfp);
-                  /* Reading from the keyserver should nver fail, thus
+                  /* Reading from the keyserver should never fail, thus
                      return this error.  */
                   es_fclose (infp);
                   infp = NULL;
@@ -149,6 +149,49 @@ ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
 }
 
 
+/* Retrive keys from URL and write the result to the provided output
+   stream OUTFP.  */
+gpg_error_t
+ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp)
+{
+  gpg_error_t err = 0;
+  estream_t infp;
+  parsed_uri_t parsed_uri;  /* The broken down URI.  */
+
+  if (!url)
+    return gpg_error (GPG_ERR_INV_URI);
+
+  err = http_parse_uri (&parsed_uri, url, 1);
+  if (err)
+    return err;
+
+  if (parsed_uri->is_http)
+    {
+      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    }
+  else if (!parsed_uri->opaque)
+    {
+      err = gpg_error (GPG_ERR_INV_URI);
+    }
+  else if (!strcmp (parsed_uri->scheme, "finger"))
+    {
+      err = ks_finger_get (ctrl, parsed_uri, &infp);
+      if (!err)
+        {
+          err = copy_stream (infp, outfp);
+          /* Reading from the finger serrver should not fail, thus
+             return this error.  */
+          es_fclose (infp);
+        }
+    }
+  else
+    err = gpg_error (GPG_ERR_INV_URI);
+
+  http_release_parsed_uri (parsed_uri);
+  return err;
+}
+
+
 
 /* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
    is expected in OpenPGP binary transport format.  */
index b3bd3fc..bba53bc 100644 (file)
@@ -22,6 +22,7 @@
 
 gpg_error_t ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp);
 gpg_error_t ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp);
+gpg_error_t ks_action_fetch (ctrl_t ctrl, const char *url, estream_t outfp);
 gpg_error_t ks_action_put (ctrl_t ctrl, const void *data, size_t datalen);
 
 
diff --git a/dirmngr/ks-engine-finger.c b/dirmngr/ks-engine-finger.c
new file mode 100644 (file)
index 0000000..c54e343
--- /dev/null
@@ -0,0 +1,101 @@
+/* ks-engine-finger.c - HKP keyserver engine
+ * Copyright (C) 2011 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "dirmngr.h"
+#include "misc.h"
+#include "userids.h"
+#include "ks-engine.h"
+
+
+/* Get the key from URI which is expected to specify a finger scheme.
+   On success R_FP has an open stream to read the data.  */
+gpg_error_t
+ks_finger_get (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp)
+{
+  gpg_error_t err;
+  estream_t fp;
+  char *server;
+  char *name;
+  http_t http;
+
+  (void)ctrl;
+  *r_fp = NULL;
+
+  if (strcmp (uri->scheme, "finger") || !uri->opaque || !uri->path)
+    return gpg_error (GPG_ERR_INV_ARG);
+
+  name = xtrystrdup (uri->path);
+  if (!name)
+    return gpg_error_from_syserror ();
+
+  server = strchr (name, '@');
+  if (!server)
+    {
+      err = gpg_error (GPG_ERR_INV_URI);
+      xfree (name);
+      return err;
+    }
+  *server++ = 0;
+
+  err = http_raw_connect (&http, server, 79, 0, NULL);
+  if (err)
+    {
+      xfree (name);
+      return err;
+    }
+
+  fp = http_get_write_ptr (http);
+  if (!fp)
+    {
+      err = gpg_error (GPG_ERR_INTERNAL);
+      http_close (http, 0);
+      xfree (name);
+      return err;
+    }
+
+  if (es_fputs (name, fp) || es_fputs ("\r\n", fp) || es_fflush (fp))
+    {
+      err = gpg_error_from_syserror ();
+      http_close (http, 0);
+      xfree (name);
+      return err;
+    }
+  xfree (name);
+  es_fclose (fp);
+
+  fp = http_get_read_ptr (http);
+  if (!fp)
+    {
+      err = gpg_error (GPG_ERR_INTERNAL);
+      http_close (http, 0);
+      return err;
+    }
+
+  http_close (http, 1 /* Keep read ptr.  */);
+
+  *r_fp = fp;
+  return 0;
+}
index 304fc4d..50f42be 100644 (file)
@@ -31,6 +31,9 @@ gpg_error_t ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri,
 gpg_error_t ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri,
                         const void *data, size_t datalen);
 
+/*-- ks-engine-finger.c --*/
+gpg_error_t ks_finger_get (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp);
+
 
 
 #endif /*DIRMNGR_KS_ENGINE_H*/
index 86b21b6..403a136 100644 (file)
@@ -1541,6 +1541,34 @@ cmd_ks_get (assuan_context_t ctx, char *line)
 }
 
 
+static const char hlp_ks_fetch[] =
+  "KS_FETCH <URL>\n"
+  "\n"
+  "Get the key(s) from URL.";
+static gpg_error_t
+cmd_ks_fetch (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  estream_t outfp;
+
+  /* No options for now.  */
+  line = skip_options (line);
+
+  /* Setup an output stream and perform the get.  */
+  outfp = es_fopencookie (ctx, "w", data_line_cookie_functions);
+  if (!outfp)
+    err = set_error (GPG_ERR_ASS_GENERAL, "error setting up a data stream");
+  else
+    {
+      err = ks_action_fetch (ctrl, line, outfp);
+      es_fclose (outfp);
+    }
+
+  return leave_cmd (ctx, err);
+}
+
+
 \f
 static const char hlp_ks_put[] =
   "KS_PUT\n"
@@ -1742,6 +1770,7 @@ register_commands (assuan_context_t ctx)
     { "KEYSERVER",  cmd_keyserver,  hlp_keyserver },
     { "KS_SEARCH",  cmd_ks_search,  hlp_ks_search },
     { "KS_GET",     cmd_ks_get,     hlp_ks_get },
+    { "KS_FETCH",   cmd_ks_fetch,   hlp_ks_fetch },
     { "KS_PUT",     cmd_ks_put,     hlp_ks_put },
     { "GETINFO",    cmd_getinfo,    hlp_getinfo },
     { "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr },
index 8d850a6..8594110 100644 (file)
@@ -1,3 +1,8 @@
+2011-02-08  Werner Koch  <wk@g10code.com>
+
+       * call-dirmngr.c (gpg_dirmngr_ks_fetch): New.
+       * keyserver.c (keyserver_fetch): Rewrite to use dirmngr.
+
 2011-02-07  Werner Koch  <wk@g10code.com>
 
        * seskey.c (encode_md_value): Truncate to MDLEN and not to QBYTES
index 10c0e56..09ade4e 100644 (file)
@@ -354,7 +354,7 @@ gpg_dirmngr_ks_search (ctrl_t ctrl, const char *searchstr,
 
 
 \f
-/* Data callback for the KS_GET command. */
+/* Data callback for the KS_GET and KS_FETCH commands. */
 static gpg_error_t
 ks_get_data_cb (void *opaque, const void *data, size_t datalen)
 {
@@ -448,6 +448,65 @@ gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern, estream_t *r_fp)
 }
 
 
+/* Run the KS_FETCH and pass URL as argument.  On success an estream
+   object is returned to retrieve the keys.  On error an error code is
+   returned and NULL stored at R_FP.
+
+   The url is expected to point to a small set of keys; in many cases
+   only to one key.  However, schemes like finger may return several
+   keys.  Note that the configured keyservers are ignored by the
+   KS_FETCH command.  */
+gpg_error_t
+gpg_dirmngr_ks_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  struct ks_get_parm_s parm;
+  char *line = NULL;
+
+  memset (&parm, 0, sizeof parm);
+
+  *r_fp = NULL;
+
+  err = open_context (ctrl, &ctx);
+  if (err)
+    return err;
+
+  line = strconcat ("KS_FETCH -- ", url, NULL);
+  if (!line)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  if (strlen (line) + 2 >= ASSUAN_LINELENGTH)
+    {
+      err = gpg_error (GPG_ERR_TOO_LARGE);
+      goto leave;
+    }
+
+  parm.memfp = es_fopenmem (0, "rwb");
+  if (!parm.memfp)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  err = assuan_transact (ctx, line, ks_get_data_cb, &parm,
+                         NULL, NULL, NULL, NULL);
+  if (err)
+    goto leave;
+
+  es_rewind (parm.memfp);
+  *r_fp = parm.memfp;
+  parm.memfp = NULL;
+
+ leave:
+  es_fclose (parm.memfp);
+  xfree (line);
+  close_context (ctrl, ctx);
+  return err;
+}
+
+
 \f
 /* Handle the KS_PUT inquiries. */
 static gpg_error_t
index 2f055ad..be0049a 100644 (file)
@@ -1641,54 +1641,53 @@ keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
 }
 
 
-
 int
 keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
 {
-  KEYDB_SEARCH_DESC desc;
+  gpg_error_t err;
   strlist_t sl;
-  unsigned int options=opt.keyserver_options.import_options;
+  estream_t datastream;
+  unsigned int options = opt.keyserver_options.import_options;
 
   /* Switch on fast-import, since fetch can handle more than one
      import and we don't want each set to rebuild the trustdb.
      Instead we do it once at the end. */
-  opt.keyserver_options.import_options|=IMPORT_FAST;
-
-  /* A dummy desc since we're not actually fetching a particular key
-     ID */
-  memset(&desc,0,sizeof(desc));
-  desc.mode=KEYDB_SEARCH_MODE_EXACT;
+  opt.keyserver_options.import_options |= IMPORT_FAST;
 
-  for(sl=urilist;sl;sl=sl->next)
+  for (sl=urilist; sl; sl=sl->next)
     {
-      struct keyserver_spec *spec;
+      if (!opt.quiet)
+        log_info (_("requesting key from `%s'\n"), sl->d);
 
-      spec=parse_keyserver_uri(sl->d,1,NULL,0);
-      if(spec)
-       {
-         int rc;
+      err = gpg_dirmngr_ks_fetch (ctrl, sl->d, &datastream);
+      if (!err)
+        {
+          void *stats_handle;
 
-         rc = keyserver_get (ctrl, &desc, 1, spec);
-         if(rc)
-           log_info (_("WARNING: unable to fetch URI %s: %s\n"),
-                    sl->d,g10_errstr(rc));
+          stats_handle = import_new_stats_handle();
+          import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL,
+                                 opt.keyserver_options.import_options);
 
-         free_keyserver_spec(spec);
-       }
+          import_print_stats (stats_handle);
+          import_release_stats_handle (stats_handle);
+        }
       else
-       log_info (_("WARNING: unable to parse URI %s\n"),sl->d);
+        log_info (_("WARNING: unable to fetch URI %s: %s\n"),
+                  sl->d, gpg_strerror (err));
+      es_fclose (datastream);
     }
 
-  opt.keyserver_options.import_options=options;
+  opt.keyserver_options.import_options = options;
 
   /* If the original options didn't have fast import, and the trustdb
      is dirty, rebuild. */
-  if(!(opt.keyserver_options.import_options&IMPORT_FAST))
-    trustdb_check_or_update();
+  if (!(opt.keyserver_options.import_options&IMPORT_FAST))
+    trustdb_check_or_update ();
 
   return 0;
 }
 
+
 /* Import key in a CERT or pointed to by a CERT */
 int
 keyserver_import_cert (ctrl_t ctrl,