aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/ChangeLog22
-rw-r--r--common/estream.c11
-rw-r--r--common/http.c359
-rw-r--r--common/http.h13
-rw-r--r--dirmngr/ChangeLog6
-rw-r--r--dirmngr/Makefile.am3
-rw-r--r--dirmngr/ks-action.c45
-rw-r--r--dirmngr/ks-action.h1
-rw-r--r--dirmngr/ks-engine-finger.c101
-rw-r--r--dirmngr/ks-engine.h3
-rw-r--r--dirmngr/server.c29
-rw-r--r--g10/ChangeLog5
-rw-r--r--g10/call-dirmngr.c61
-rw-r--r--g10/keyserver.c49
14 files changed, 606 insertions, 102 deletions
diff --git a/common/ChangeLog b/common/ChangeLog
index 647f7d582..4d07a4919 100644
--- a/common/ChangeLog
+++ b/common/ChangeLog
@@ -1,3 +1,25 @@
+2011-02-08 Werner Koch <[email protected]>
+
+ * http.c (connect_server): Add arg R_HOST_NOT_FOUND.
+
+2011-02-07 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
* status.h (STATUS_DECRYPTION_INFO): New.
diff --git a/common/estream.c b/common/estream.c
index bc820513e..a73d1f2c4 100644
--- a/common/estream.c
+++ b/common/estream.c
@@ -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
diff --git a/common/http.c b/common/http.c
index f8628e622..7df84576f 100644
--- a/common/http.c
+++ b/common/http.c
@@ -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;
}
-
/**** 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:
*/
diff --git a/common/http.h b/common/http.h
index 50c478c84..7a11b84f3 100644
--- a/common/http.h
+++ b/common/http.h
@@ -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,
diff --git a/dirmngr/ChangeLog b/dirmngr/ChangeLog
index 3a01b97af..1e575e1e9 100644
--- a/dirmngr/ChangeLog
+++ b/dirmngr/ChangeLog
@@ -1,3 +1,9 @@
+2011-02-08 Werner Koch <[email protected]>
+
+ * server.c (cmd_ks_fetch): New.
+ * ks-action.c (ks_action_fetch): New.
+ * ks-engine-finger.c: New.
+
2011-02-03 Werner Koch <[email protected]>
* Makefile.am (dirmngr_LDADD): Remove -llber.
diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am
index d5aecc7ab..a030f3861 100644
--- a/dirmngr/Makefile.am
+++ b/dirmngr/Makefile.am
@@ -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
diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c
index 50f0d5063..dff49979f 100644
--- a/dirmngr/ks-action.c
+++ b/dirmngr/ks-action.c
@@ -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. */
diff --git a/dirmngr/ks-action.h b/dirmngr/ks-action.h
index b3bd3fc46..bba53bc04 100644
--- a/dirmngr/ks-action.h
+++ b/dirmngr/ks-action.h
@@ -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
index 000000000..c54e34351
--- /dev/null
+++ b/dirmngr/ks-engine-finger.c
@@ -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;
+}
diff --git a/dirmngr/ks-engine.h b/dirmngr/ks-engine.h
index 304fc4d1a..50f42be4b 100644
--- a/dirmngr/ks-engine.h
+++ b/dirmngr/ks-engine.h
@@ -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*/
diff --git a/dirmngr/server.c b/dirmngr/server.c
index 86b21b67b..403a13692 100644
--- a/dirmngr/server.c
+++ b/dirmngr/server.c
@@ -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);
+}
+
+
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 },
diff --git a/g10/ChangeLog b/g10/ChangeLog
index 8d850a65f..8594110f5 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,3 +1,8 @@
+2011-02-08 Werner Koch <[email protected]>
+
+ * call-dirmngr.c (gpg_dirmngr_ks_fetch): New.
+ * keyserver.c (keyserver_fetch): Rewrite to use dirmngr.
+
2011-02-07 Werner Koch <[email protected]>
* seskey.c (encode_md_value): Truncate to MDLEN and not to QBYTES
diff --git a/g10/call-dirmngr.c b/g10/call-dirmngr.c
index 10c0e568c..09ade4eb9 100644
--- a/g10/call-dirmngr.c
+++ b/g10/call-dirmngr.c
@@ -354,7 +354,7 @@ gpg_dirmngr_ks_search (ctrl_t ctrl, const char *searchstr,
-/* 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;
+}
+
+
/* Handle the KS_PUT inquiries. */
static gpg_error_t
diff --git a/g10/keyserver.c b/g10/keyserver.c
index 2f055ada5..be0049a18 100644
--- a/g10/keyserver.c
+++ b/g10/keyserver.c
@@ -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,