Add finger support to dirmngr.

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 ;-)
This commit is contained in:
Werner Koch 2011-02-08 21:11:19 +01:00
parent 8a7336e0bf
commit 2c79a2832c
14 changed files with 606 additions and 102 deletions

View 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.

View 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

View 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;
}
/**** 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)
if (uri->opaque)
printf ("Value : %s\n", uri->path);
else
{
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');
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:
*/

View 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,

View 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.

View 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

View 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. */

View 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);

101
dirmngr/ks-engine-finger.c Normal file
View File

@ -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;
}

View 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*/

View 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);
}
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 },

View 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

View File

@ -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

View 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;
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;
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,