dirmngr: Fix HKP host selection code.

* dirmngr/server.c (cmd_keyserver): Add option --resolve and change
--print-hosttable to --hosttable.
* dirmngr/ks-action.c (ks_printf_help): New.
(ks_action_resolve): New.
* dirmngr/ks-engine-hkp.c (select_random_host): Fix selection.
(ks_hkp_print_hosttable): Print to assuan stream.
(map_host): Remove debug code.  Add arg FORCE_SELECT.  Return numeric
IP addr if it can't be resolved.
(make_host_part): Add arg FORCE_SELECT; change callers to pass false.
(ks_hkp_resolve): New.
--

The new options for the keyserver command are useful for debugging.
For example:

  $ tools/gpg-connect-agent -S /usr/local/var/run/gnupg/S.dirmngr \
          'keyserver hkp://keys.gnupg.net' \
          'keyserver http://http-keys.gnupg.net' \
          'keyserver --resolve --hosttable' /bye

yields:

  OK
  OK
  S # http://astrath.net:80
  S # http://2001:41d0:1:e673::1:11371
  S # hosttable (idx, ipv4, ipv6, dead, name):
  S #   0       http-keys.gnupg.net
  S #   .   --> 10 11 12 1 5 8 7 4* 2 9 6 3
  S #   1 4     37.250.168.245.bredband.tre.se
  S #   2 4 6   keys.exosphere.de
  S #   3 4 6   poseidon.muc.drweb-av.de
  S #   4 4     astrath.net
  S #   5 4     79.143.214.216
  S #   6 4     openpgp.andrew.kvalhe.im
  S #   7 4     app.aaiedu.hr
  S #   8 4 6   alita.karotte.org
  S #   9 4 6   keyserver.bau5net.com
  S #  10 4     194.94.127.122
  S #  11   6   2001:4d88:1ffc:477::7
  S #  12   6   2a00:1280:8000:2:1:8:0:1
  S #  13       keys.gnupg.net
  S #   .   --> 23 28* 30 17 22 8 7 27 25 14 21 20 19 29 [...]
  S #  14 4     hufu.ki.iif.hu
  S #  15 4     pks.ms.mff.cuni.cz
  S #  16 4     pgpkeys.co.uk
  S #  17 4     80-239-156-219.customer.teliacarrier.com
  S #  18 4     srv01.secure-u.de
  S #  19 4     mallos.xs4all.nl
  S #  20 4     kronecker.scientia.net
  S #  21 4     keyserver.ut.mephi.ru
  S #  22 4     89-68-150-88.dynamic.chello.pl
  S #  23   6   2001:1608:21:6:84:200:66:125
  S #  24   6   sks.es.net
  S #  25   6   gstueve-1-pt.tunnel.tserv13.ash1.ipv6.he.net
  S #  26   6   sks.mrball.net
  S #  27   6   gozer.rediris.es
  S #  28   6   2001:41d0:1:e673::1
  S #  29   6   oteiza.siccegge.de
  S #  30   6   2403:4200:401:10::13
  S #  31   6   statler.serviz.fr
  OK
This commit is contained in:
Werner Koch 2014-03-11 14:26:39 +01:00
parent f30d8b0188
commit 3c35b46a32
5 changed files with 158 additions and 49 deletions

View File

@ -1,5 +1,6 @@
/* ks-action.c - OpenPGP keyserver actions
* Copyright (C) 2011 Free Software Foundation, Inc.
* Copyright (C) 2011, 2014 Werner Koch
*
* This file is part of GnuPG.
*
@ -57,6 +58,25 @@ ks_print_help (ctrl_t ctrl, const char *text)
}
/* Called by the engine's help functions to print the actual help. */
gpg_error_t
ks_printf_help (ctrl_t ctrl, const char *format, ...)
{
va_list arg_ptr;
gpg_error_t err;
char *buf;
va_start (arg_ptr, format);
buf = es_vasprintf (format, arg_ptr);
err = buf? 0 : gpg_error_from_syserror ();
va_end (arg_ptr);
if (!err)
err = dirmngr_status_help (ctrl, buf);
es_free (buf);
return err;
}
/* Run the help command for the engine responsible for URI. */
gpg_error_t
ks_action_help (ctrl_t ctrl, const char *url)
@ -94,6 +114,32 @@ ks_action_help (ctrl_t ctrl, const char *url)
}
/* Resolve all host names. This is useful for looking at the status
of configured keyservers. */
gpg_error_t
ks_action_resolve (ctrl_t ctrl)
{
gpg_error_t err = 0;
int any = 0;
uri_item_t uri;
for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
{
if (uri->parsed_uri->is_http)
{
any = 1;
err = ks_hkp_resolve (ctrl, uri->parsed_uri);
if (err)
break;
}
}
if (!any)
err = gpg_error (GPG_ERR_NO_KEYSERVER);
return err;
}
/* Search all configured keyservers for keys matching PATTERNS and
write the result to the provided output stream. */
gpg_error_t
@ -193,7 +239,7 @@ ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
}
/* Retrive keys from URL and write the result to the provided output
/* Retrieve 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)

View File

@ -21,6 +21,7 @@
#define DIRMNGR_KS_ACTION_H 1
gpg_error_t ks_action_help (ctrl_t ctrl, const char *url);
gpg_error_t ks_action_resolve (ctrl_t ctrl);
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);

View File

@ -1,5 +1,6 @@
/* ks-engine-hkp.c - HKP keyserver engine
* Copyright (C) 2011, 2012 Free Software Foundation, Inc.
* Copyright (C) 2011, 2012, 2014 Werner Koch
*
* This file is part of GnuPG.
*
@ -94,6 +95,7 @@ create_new_hostinfo (const char *name)
hi->lastfail = (time_t)(-1);
hi->v4 = 0;
hi->v6 = 0;
hi->dead = 0;
/* Add it to the hosttable. */
for (idx=0; idx < hosttable_size; idx++)
@ -161,8 +163,8 @@ select_random_host (int *table)
size_t tblsize;
int pidx, idx;
/* We create a new table so that we select only from currently alive
hosts. */
/* We create a new table so that we randomly select only from
currently alive hosts. */
for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++)
if (hosttable[pidx] && !hosttable[pidx]->dead)
tblsize++;
@ -179,7 +181,7 @@ select_random_host (int *table)
if (tblsize == 1) /* Save a get_uint_nonce. */
pidx = tbl[0];
else
pidx = get_uint_nonce () % tblsize;
pidx = tbl[get_uint_nonce () % tblsize];
xfree (tbl);
return pidx;
@ -190,9 +192,10 @@ select_random_host (int *table)
allows us to manage round robin DNS names. We use our own strategy
to choose one of the hosts. For example we skip those hosts which
failed for some time and we stick to one host for a time
independent of DNS retry times. */
independent of DNS retry times. If FORCE_RESELECT is true a new
host is always selected. */
static char *
map_host (const char *name)
map_host (const char *name, int force_reselect)
{
hostinfo_t hi;
int idx;
@ -241,12 +244,13 @@ map_host (const char *name)
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
continue;
log_printhex ("getaddrinfo returned", ai->ai_addr,ai->ai_addrlen);
if ((ec=getnameinfo (ai->ai_addr, ai->ai_addrlen,
tmphost, sizeof tmphost,
NULL, 0, NI_NAMEREQD)))
log_info ("getnameinfo failed while checking '%s': %s\n",
name, gai_strerror (ec));
NULL, 0, 0)))
{
log_info ("getnameinfo failed while checking '%s': %s\n",
name, gai_strerror (ec));
}
else if (refidx+1 >= reftblsize)
{
log_error ("getnameinfo returned for '%s': '%s'"
@ -266,7 +270,7 @@ map_host (const char *name)
for (i=0; i < refidx; i++)
if (reftbl[i] == tmpidx)
break;
break;
if (!(i < refidx) && tmpidx != idx)
reftbl[refidx++] = tmpidx;
}
@ -319,8 +323,10 @@ map_host (const char *name)
{
/* If the currently selected host is now marked dead, force a
re-selection . */
if (hi->poolidx >= 0 && hi->poolidx < hosttable_size
&& hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead)
if (force_reselect)
hi->poolidx = -1;
else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size
&& hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead)
hi->poolidx = -1;
/* Select a host if needed. */
@ -369,33 +375,48 @@ mark_host_dead (const char *name)
/* Debug function to print the entire hosttable. */
void
ks_hkp_print_hosttable (void)
gpg_error_t
ks_hkp_print_hosttable (ctrl_t ctrl)
{
gpg_error_t err;
int idx, idx2;
hostinfo_t hi;
membuf_t mb;
char *p;
err = ks_print_help (ctrl, "hosttable (idx, ipv4, ipv6, dead, name):");
if (err)
return err;
for (idx=0; idx < hosttable_size; idx++)
if ((hi=hosttable[idx]))
{
log_info ("hosttable %3d %s %s %s %s\n",
idx, hi->v4? "4":" ", hi->v6? "6":" ",
hi->dead? "d":" ", hi->name);
err = ks_printf_help (ctrl, "%3d %s %s %s %s\n",
idx, hi->v4? "4":" ", hi->v6? "6":" ",
hi->dead? "d":" ", hi->name);
if (err)
return err;
if (hi->pool)
{
log_info (" -->");
init_membuf (&mb, 256);
put_membuf_printf (&mb, " . -->");
for (idx2=0; hi->pool[idx2] != -1; idx2++)
{
log_printf (" %d", hi->pool[idx2]);
if (hi->poolidx == idx2)
log_printf ("*");
put_membuf_printf (&mb, " %d", hi->pool[idx2]);
if (hi->poolidx == hi->pool[idx2])
put_membuf_printf (&mb, "*");
}
log_printf ("\n");
/* for (idx2=0; hi->pool[idx2] != -1; idx2++) */
/* log_info (" (%s)\n", */
/* hosttable[hi->pool[idx2]]->name); */
put_membuf( &mb, "", 1);
p = get_membuf (&mb, NULL);
if (!p)
return gpg_error_from_syserror ();
err = ks_print_help (ctrl, p);
xfree (p);
if (err)
return err;
}
}
return 0;
}
@ -425,7 +446,8 @@ ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri)
PORT. Returns an allocated string or NULL on failure and sets
ERRNO. */
static char *
make_host_part (const char *scheme, const char *host, unsigned short port)
make_host_part (const char *scheme, const char *host, unsigned short port,
int force_reselect)
{
char portstr[10];
char *hostname;
@ -449,7 +471,7 @@ make_host_part (const char *scheme, const char *host, unsigned short port)
/*fixme_do_srv_lookup ()*/
}
hostname = map_host (host);
hostname = map_host (host, force_reselect);
if (!hostname)
return NULL;
@ -459,6 +481,32 @@ make_host_part (const char *scheme, const char *host, unsigned short port)
}
/* Resolve all known keyserver names and update the hosttable. This
is mainly useful for debugging because the resolving is anyway done
on demand. */
gpg_error_t
ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri)
{
gpg_error_t err;
char *hostport = NULL;
hostport = make_host_part (uri->scheme, uri->host, uri->port, 1);
if (!hostport)
{
err = gpg_error_from_syserror ();
err = ks_printf_help (ctrl, "%s://%s:%hu: resolve failed: %s",
uri->scheme, uri->host, uri->port,
gpg_strerror (err));
}
else
{
err = ks_printf_help (ctrl, "%s", hostport);
xfree (hostport);
}
return err;
}
/* Send an HTTP request. On success returns an estream object at
R_FP. HOSTPORTSTR is only used for diagnostics. If POST_CB is not
NULL a post request is used and that callback is called to allow
@ -636,7 +684,6 @@ armor_data (char **r_string, const void *data, size_t datalen)
}
/* Search the keyserver identified by URI for keys matching PATTERN.
On success R_FP has an open stream to read the data. */
@ -694,7 +741,7 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
{
char *searchkey;
hostport = make_host_part (uri->scheme, uri->host, uri->port);
hostport = make_host_part (uri->scheme, uri->host, uri->port, 0);
if (!hostport)
{
err = gpg_error_from_syserror ();
@ -725,7 +772,7 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
if (err)
goto leave;
/* Start reading the response. */
/* Peek at the response. */
{
int c = es_getc (fp);
if (c == -1)
@ -736,8 +783,8 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
}
if (c == '<')
{
/* The document begins with a '<', assume it's a HTML
response, which we don't support. */
/* The document begins with a '<': Assume a HTML response,
which we don't support. */
err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
goto leave;
}
@ -800,7 +847,7 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
}
/* Build the request string. */
hostport = make_host_part (uri->scheme, uri->host, uri->port);
hostport = make_host_part (uri->scheme, uri->host, uri->port, 0);
if (!hostport)
{
err = gpg_error_from_syserror ();
@ -892,7 +939,7 @@ ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
armored = NULL;
/* Build the request string. */
hostport = make_host_part (uri->scheme, uri->host, uri->port);
hostport = make_host_part (uri->scheme, uri->host, uri->port, 0);
if (!hostport)
{
err = gpg_error_from_syserror ();

View File

@ -25,9 +25,12 @@
/*-- ks-action.c --*/
gpg_error_t ks_print_help (ctrl_t ctrl, const char *text);
gpg_error_t ks_printf_help (ctrl_t ctrl, const char *format,
...) _ESTREAM_GCC_A_PRINTF(2,3);
/*-- ks-engine-hkp.c --*/
void ks_hkp_print_hosttable (void);
gpg_error_t ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri);
gpg_error_t ks_hkp_print_hosttable (ctrl_t ctrl);
gpg_error_t ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri);
gpg_error_t ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
estream_t *r_fp);

View File

@ -1,15 +1,16 @@
/* dirmngr.c - LDAP access
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
* Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2011 g10 Code GmbH
/* server.c - LDAP and Keyserver access server
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
* Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2011 g10 Code GmbH
* Copyright (C) 2014 Werner Koch
*
* This file is part of DirMngr.
* This file is part of GnuPG.
*
* DirMngr is free software; you can redistribute it and/or modify
* 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 2 of the License, or
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* DirMngr is distributed in the hope that it will be useful,
* 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.
@ -120,6 +121,7 @@ release_ctrl_keyservers (ctrl_t ctrl)
/* Helper to print a message while leaving a command. */
static gpg_error_t
leave_cmd (assuan_context_t ctx, gpg_error_t err)
{
if (err)
{
@ -1374,14 +1376,15 @@ static gpg_error_t
cmd_keyserver (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
int clear_flag, add_flag, help_flag, host_flag;
gpg_error_t err = 0;
int clear_flag, add_flag, help_flag, host_flag, resolve_flag;
uri_item_t item = NULL; /* gcc 4.4.5 is not able to detect that it
is always initialized. */
clear_flag = has_option (line, "--clear");
help_flag = has_option (line, "--help");
host_flag = has_option (line, "--print-hosttable");
resolve_flag = has_option (line, "--resolve");
host_flag = has_option (line, "--hosttable");
line = skip_options (line);
add_flag = !!*line;
@ -1391,12 +1394,21 @@ cmd_keyserver (assuan_context_t ctx, char *line)
goto leave;
}
if (resolve_flag)
{
err = ks_action_resolve (ctrl);
if (err)
goto leave;
}
if (host_flag)
{
ks_hkp_print_hosttable ();
err = 0;
goto leave;
err = ks_hkp_print_hosttable (ctrl);
if (err)
goto leave;
}
if (resolve_flag || host_flag)
goto leave;
if (add_flag)
{