dirmngr: Improve dead host detection.
[gnupg.git] / dirmngr / ks-engine-hkp.c
1 /* ks-engine-hkp.c - HKP keyserver engine
2  * Copyright (C) 2011, 2012 Free Software Foundation, Inc.
3  * Copyright (C) 2011, 2012, 2014 Werner Koch
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27 #ifdef HAVE_W32_SYSTEM
28 # ifdef HAVE_WINSOCK2_H
29 #  include <winsock2.h>
30 # endif
31 # include <windows.h>
32 #else /*!HAVE_W32_SYSTEM*/
33 # include <sys/types.h>
34 # include <sys/socket.h>
35 # include <netdb.h>
36 #endif /*!HAVE_W32_SYSTEM*/
37
38 #include "dirmngr.h"
39 #include "misc.h"
40 #include "userids.h"
41 #include "ks-engine.h"
42
43 /* Substitutes for missing Mingw macro.  The EAI_SYSTEM mechanism
44    seems not to be available (probably because there is only one set
45    of error codes anyway).  For now we use WSAEINVAL. */
46 #ifndef EAI_OVERFLOW
47 # define EAI_OVERFLOW EAI_FAIL
48 #endif
49 #ifdef HAVE_W32_SYSTEM
50 # ifndef EAI_SYSTEM
51 #  define EAI_SYSTEM WSAEINVAL
52 # endif
53 #endif
54
55
56 /* Number of seconds after a host is marked as resurrected.  */
57 #define RESURRECT_INTERVAL  (3600*3)  /* 3 hours */
58
59 /* To match the behaviour of our old gpgkeys helper code we escape
60    more characters than actually needed. */
61 #define EXTRA_ESCAPE_CHARS "@!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
62
63 /* How many redirections do we allow.  */
64 #define MAX_REDIRECTS 2
65
66 /* Number of retries done for a dead host etc.  */
67 #define SEND_REQUEST_RETRIES 3
68
69 /* Objects used to maintain information about hosts.  */
70 struct hostinfo_s;
71 typedef struct hostinfo_s *hostinfo_t;
72 struct hostinfo_s
73 {
74   time_t lastfail;   /* Time we tried to connect and failed.  */
75   time_t lastused;   /* Time of last use.  */
76   int *pool;         /* A -1 terminated array with indices into
77                         HOSTTABLE or NULL if NAME is not a pool
78                         name.  */
79   int poolidx;       /* Index into POOL with the used host.  -1 if not set.  */
80   unsigned int v4:1; /* Host supports AF_INET.  */
81   unsigned int v6:1; /* Host supports AF_INET6.  */
82   unsigned int dead:1; /* Host is currently unresponsive.  */
83   time_t died_at;    /* The time the host was marked dead.  If this is
84                         0 the host has been manually marked dead.  */
85   char *cname;       /* Canonical name of the host.  Only set if this
86                         is a pool.  */
87   char *v4addr;      /* A string with the v4 IP address of the host.
88                         NULL if NAME has a numeric IP address or no v4
89                         address is available.  */
90   char *v6addr;      /* A string with the v6 IP address of the host.
91                         NULL if NAME has a numeric IP address or no v4
92                         address is available.  */
93   char name[1];      /* The hostname.  */
94 };
95
96
97 /* An array of hostinfo_t for all hosts requested by the caller or
98    resolved from a pool name and its allocated size.*/
99 static hostinfo_t *hosttable;
100 static int hosttable_size;
101
102 /* The number of host slots we initally allocate for HOSTTABLE.  */
103 #define INITIAL_HOSTTABLE_SIZE 10
104
105
106 /* Create a new hostinfo object, fill in NAME and put it into
107    HOSTTABLE.  Return the index into hosttable on success or -1 on
108    error. */
109 static int
110 create_new_hostinfo (const char *name)
111 {
112   hostinfo_t hi, *newtable;
113   int newsize;
114   int idx, rc;
115
116   hi = xtrymalloc (sizeof *hi + strlen (name));
117   if (!hi)
118     return -1;
119   strcpy (hi->name, name);
120   hi->pool = NULL;
121   hi->poolidx = -1;
122   hi->lastused = (time_t)(-1);
123   hi->lastfail = (time_t)(-1);
124   hi->v4 = 0;
125   hi->v6 = 0;
126   hi->dead = 0;
127   hi->died_at = 0;
128   hi->cname = NULL;
129   hi->v4addr = NULL;
130   hi->v6addr = NULL;
131
132   /* Add it to the hosttable. */
133   for (idx=0; idx < hosttable_size; idx++)
134     if (!hosttable[idx])
135       {
136         hosttable[idx] = hi;
137         return idx;
138       }
139   /* Need to extend the hosttable.  */
140   newsize = hosttable_size + INITIAL_HOSTTABLE_SIZE;
141   newtable = xtryrealloc (hosttable, newsize * sizeof *hosttable);
142   if (!newtable)
143     {
144       xfree (hi);
145       return -1;
146     }
147   hosttable = newtable;
148   idx = hosttable_size;
149   hosttable_size = newsize;
150   rc = idx;
151   hosttable[idx++] = hi;
152   while (idx < hosttable_size)
153     hosttable[idx++] = NULL;
154
155   return rc;
156 }
157
158
159 /* Find the host NAME in our table.  Return the index into the
160    hosttable or -1 if not found.  */
161 static int
162 find_hostinfo (const char *name)
163 {
164   int idx;
165
166   for (idx=0; idx < hosttable_size; idx++)
167     if (hosttable[idx] && !ascii_strcasecmp (hosttable[idx]->name, name))
168       return idx;
169   return -1;
170 }
171
172
173 static int
174 sort_hostpool (const void *xa, const void *xb)
175 {
176   int a = *(int *)xa;
177   int b = *(int *)xb;
178
179   assert (a >= 0 && a < hosttable_size);
180   assert (b >= 0 && b < hosttable_size);
181   assert (hosttable[a]);
182   assert (hosttable[b]);
183
184   return ascii_strcasecmp (hosttable[a]->name, hosttable[b]->name);
185 }
186
187
188 /* Return true if the host with the hosttable index TBLIDX is in POOL.  */
189 static int
190 host_in_pool_p (int *pool, int tblidx)
191 {
192   int i, pidx;
193
194   for (i=0; (pidx = pool[i]) != -1; i++)
195     if (pidx == tblidx && hosttable[pidx])
196       return 1;
197   return 0;
198 }
199
200
201 /* Select a random host.  Consult TABLE which indices into the global
202    hosttable.  Returns index into TABLE or -1 if no host could be
203    selected.  */
204 static int
205 select_random_host (int *table)
206 {
207   int *tbl;
208   size_t tblsize;
209   int pidx, idx;
210
211   /* We create a new table so that we randomly select only from
212      currently alive hosts.  */
213   for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++)
214     if (hosttable[pidx] && !hosttable[pidx]->dead)
215       tblsize++;
216   if (!tblsize)
217     return -1; /* No hosts.  */
218
219   tbl = xtrymalloc (tblsize * sizeof *tbl);
220   if (!tbl)
221     return -1;
222   for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++)
223     if (hosttable[pidx] && !hosttable[pidx]->dead)
224       tbl[tblsize++] = pidx;
225
226   if (tblsize == 1)  /* Save a get_uint_nonce.  */
227     pidx = tbl[0];
228   else
229     pidx = tbl[get_uint_nonce () % tblsize];
230
231   xfree (tbl);
232   return pidx;
233 }
234
235
236 /* Simplified version of getnameinfo which also returns a numeric
237    hostname inside of brackets.  The caller should provide a buffer
238    for HOST which is 2 bytes larger than the largest hostname.  If
239    NUMERIC is true the returned value is numeric IP address.  Returns
240    0 on success or an EAI error code.  True is stored at R_ISNUMERIC
241    if HOST has a numeric IP address. */
242 static int
243 my_getnameinfo (struct addrinfo *ai, char *host, size_t hostlen,
244                 int numeric, int *r_isnumeric)
245 {
246   int ec;
247   char *p;
248
249   *r_isnumeric = 0;
250
251   if (hostlen < 5)
252     return EAI_OVERFLOW;
253
254   if (numeric)
255     ec = EAI_NONAME;
256   else
257     ec = getnameinfo (ai->ai_addr, ai->ai_addrlen,
258                       host, hostlen, NULL, 0, NI_NAMEREQD);
259
260   if (!ec && *host == '[')
261     ec = EAI_FAIL;  /* A name may never start with a bracket.  */
262   else if (ec == EAI_NONAME)
263     {
264       p = host;
265       if (ai->ai_family == AF_INET6)
266         {
267           *p++ = '[';
268           hostlen -= 2;
269         }
270       ec = getnameinfo (ai->ai_addr, ai->ai_addrlen,
271                         p, hostlen, NULL, 0, NI_NUMERICHOST);
272       if (!ec && ai->ai_family == AF_INET6)
273         strcat (host, "]");
274
275       *r_isnumeric = 1;
276     }
277
278   return ec;
279 }
280
281
282 /* Check whether NAME is an IP address.  */
283 static int
284 is_ip_address (const char *name)
285 {
286   int ndots, n;
287
288   if (*name == '[')
289     return 1;
290   /* Check whether it is legacy IP address.  */
291   if (*name == '.')
292     return 0; /* No.  */
293   ndots = n = 0;
294   for (; *name; name++)
295     {
296       if (*name == '.')
297         {
298           if (name[1] == '.')
299             return 0; /* No. */
300           if (atoi (name+1) > 255)
301             return 0; /* Value too large.  */
302           ndots++;
303           n = 0;
304         }
305       else if (!strchr ("012345678", *name))
306         return 0; /* Not a digit.  */
307       else if (++n > 3)
308         return 0; /* More than 3 digits.  */
309     }
310   return !!(ndots == 3);
311 }
312
313
314 /* Map the host name NAME to the actual to be used host name.  This
315    allows us to manage round robin DNS names.  We use our own strategy
316    to choose one of the hosts.  For example we skip those hosts which
317    failed for some time and we stick to one host for a time
318    independent of DNS retry times.  If FORCE_RESELECT is true a new
319    host is always selected.  The selected host is stored as a malloced
320    string at R_HOST; on error NULL is stored.  If R_HTTPFLAGS is not
321    NULL it will receive flags which are to be passed to http_open.  If
322    R_POOLNAME is not NULL a malloced name of the pool is stored or
323    NULL if it is not a pool. */
324 static gpg_error_t
325 map_host (ctrl_t ctrl, const char *name, int force_reselect,
326           char **r_host, unsigned int *r_httpflags, char **r_poolname)
327 {
328   hostinfo_t hi;
329   int idx;
330
331   *r_host = NULL;
332   if (r_httpflags)
333     *r_httpflags = 0;
334   if (r_poolname)
335     *r_poolname = NULL;
336
337   /* No hostname means localhost.  */
338   if (!name || !*name)
339     {
340       *r_host = xtrystrdup ("localhost");
341       return *r_host? 0 : gpg_error_from_syserror ();
342     }
343
344   /* See whether the host is in our table.  */
345   idx = find_hostinfo (name);
346   if (idx == -1)
347     {
348       /* We never saw this host.  Allocate a new entry.  */
349       struct addrinfo hints, *aibuf, *ai;
350       int *reftbl;
351       size_t reftblsize;
352       int refidx;
353       int is_pool = 0;
354
355       reftblsize = 100;
356       reftbl = xtrymalloc (reftblsize * sizeof *reftbl);
357       if (!reftbl)
358         return gpg_error_from_syserror ();
359       refidx = 0;
360
361       idx = create_new_hostinfo (name);
362       if (idx == -1)
363         {
364           xfree (reftbl);
365           return gpg_error_from_syserror ();
366         }
367       hi = hosttable[idx];
368
369       /* Find all A records for this entry and put them into the pool
370          list - if any.  */
371       memset (&hints, 0, sizeof (hints));
372       hints.ai_family = AF_UNSPEC;
373       hints.ai_socktype = SOCK_STREAM;
374       hints.ai_flags = AI_CANONNAME;
375       /* We can't use the the AI_IDN flag because that does the
376          conversion using the current locale.  However, GnuPG always
377          used UTF-8.  To support IDN we would need to make use of the
378          libidn API.  */
379       if (!getaddrinfo (name, NULL, &hints, &aibuf))
380         {
381           int n_v6, n_v4;
382
383           /* First figure out whether this is a pool.  For a pool we
384              use a different strategy than for a plains erver: We use
385              the canonical name of the pool as the virtual host along
386              with the IP addresses.  If it is not a pool, we use the
387              specified name. */
388           n_v6 = n_v4 = 0;
389           for (ai = aibuf; ai; ai = ai->ai_next)
390             {
391               if (ai->ai_family != AF_INET6)
392                 n_v6++;
393               else if (ai->ai_family != AF_INET)
394                 n_v4++;
395             }
396           if (n_v6 > 1 || n_v4 > 1)
397             is_pool = 1;
398           if (is_pool && aibuf->ai_canonname)
399             hi->cname = xtrystrdup (aibuf->ai_canonname);
400
401           for (ai = aibuf; ai; ai = ai->ai_next)
402             {
403               char tmphost[NI_MAXHOST + 2];
404               int tmpidx;
405               int is_numeric;
406               int ec;
407               int i;
408
409               if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
410                 continue;
411
412               dirmngr_tick (ctrl);
413
414               if (!is_pool && !is_ip_address (name))
415                 {
416                   /* This is a hostname but not a pool.  Use the name
417                      as given without going through getnameinfo.  */
418                   if (strlen (name)+1 > sizeof tmphost)
419                     {
420                       ec = EAI_SYSTEM;
421                       gpg_err_set_errno (EINVAL);
422                     }
423                   else
424                     {
425                       ec = 0;
426                       strcpy (tmphost, name);
427                     }
428                   is_numeric = 0;
429                 }
430               else
431                 ec = my_getnameinfo (ai, tmphost, sizeof tmphost,
432                                      0, &is_numeric);
433
434               if (ec)
435                 {
436                   log_info ("getnameinfo failed while checking '%s': %s\n",
437                             name, gai_strerror (ec));
438                 }
439               else if (refidx+1 >= reftblsize)
440                 {
441                   log_error ("getnameinfo returned for '%s': '%s'"
442                             " [index table full - ignored]\n", name, tmphost);
443                 }
444               else
445                 {
446                   tmpidx = find_hostinfo (tmphost);
447                   log_info ("getnameinfo returned for '%s': '%s'%s\n",
448                             name, tmphost,
449                             tmpidx == -1? "" : " [already known]");
450
451                   if (tmpidx == -1) /* Create a new entry.  */
452                     tmpidx = create_new_hostinfo (tmphost);
453
454                   if (tmpidx == -1)
455                     {
456                       log_error ("map_host for '%s' problem: %s - '%s'"
457                                  " [ignored]\n",
458                                  name, strerror (errno), tmphost);
459                     }
460                   else  /* Set or update the entry. */
461                     {
462                       char *ipaddr = NULL;
463
464                       if (!is_numeric)
465                         {
466                           ec = my_getnameinfo (ai, tmphost, sizeof tmphost,
467                                                1, &is_numeric);
468                           if (!ec && !(ipaddr = xtrystrdup (tmphost)))
469                             ec = EAI_SYSTEM;
470                           if (ec)
471                             log_info ("getnameinfo failed: %s\n",
472                                       gai_strerror (ec));
473                         }
474
475                       if (ai->ai_family == AF_INET6)
476                         {
477                           hosttable[tmpidx]->v6 = 1;
478                           xfree (hosttable[tmpidx]->v6addr);
479                           hosttable[tmpidx]->v6addr = ipaddr;
480                         }
481                       else if (ai->ai_family == AF_INET)
482                         {
483                           hosttable[tmpidx]->v4 = 1;
484                           xfree (hosttable[tmpidx]->v4addr);
485                           hosttable[tmpidx]->v4addr = ipaddr;
486                         }
487                       else
488                         BUG ();
489
490                       for (i=0; i < refidx; i++)
491                         if (reftbl[i] == tmpidx)
492                           break;
493                       if (!(i < refidx) && tmpidx != idx)
494                         reftbl[refidx++] = tmpidx;
495                     }
496                 }
497             }
498           freeaddrinfo (aibuf);
499         }
500       reftbl[refidx] = -1;
501       if (refidx && is_pool)
502         {
503           assert (!hi->pool);
504           hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl);
505           if (!hi->pool)
506             {
507               log_error ("shrinking index table in map_host failed: %s\n",
508                          strerror (errno));
509               xfree (reftbl);
510             }
511           qsort (reftbl, refidx, sizeof *reftbl, sort_hostpool);
512         }
513       else
514         xfree (reftbl);
515     }
516
517   hi = hosttable[idx];
518   if (hi->pool)
519     {
520       /* If the currently selected host is now marked dead, force a
521          re-selection .  */
522       if (force_reselect)
523         hi->poolidx = -1;
524       else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size
525                && hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead)
526         hi->poolidx = -1;
527
528       /* Select a host if needed.  */
529       if (hi->poolidx == -1)
530         {
531           hi->poolidx = select_random_host (hi->pool);
532           if (hi->poolidx == -1)
533             {
534               log_error ("no alive host found in pool '%s'\n", name);
535               return gpg_error (GPG_ERR_NO_KEYSERVER);
536             }
537         }
538
539       assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size);
540       hi = hosttable[hi->poolidx];
541       assert (hi);
542     }
543
544   if (hi->dead)
545     {
546       log_error ("host '%s' marked as dead\n", hi->name);
547       return gpg_error (GPG_ERR_NO_KEYSERVER);
548     }
549
550   if (r_httpflags)
551     {
552       /* If the hosttable does not indicate that a certain host
553          supports IPv<N>, we explicit set the corresponding http
554          flags.  The reason for this is that a host might be listed in
555          a pool as not v6 only but actually support v6 when later
556          the name is resolved by our http layer.  */
557       if (!hi->v4)
558         *r_httpflags |= HTTP_FLAG_IGNORE_IPv4;
559       if (!hi->v6)
560         *r_httpflags |= HTTP_FLAG_IGNORE_IPv6;
561     }
562
563   if (r_poolname && hi->pool && hi->cname)
564     {
565       *r_poolname = xtrystrdup (hi->cname);
566       if (!*r_poolname)
567         return gpg_error_from_syserror ();
568     }
569
570   *r_host = xtrystrdup (hi->name);
571   if (!*r_host)
572     {
573       if (r_poolname)
574         {
575           xfree (*r_poolname);
576           *r_poolname = NULL;
577         }
578       return gpg_error_from_syserror ();
579     }
580   return 0;
581 }
582
583
584 /* Mark the host NAME as dead.  NAME may be given as an URL.  Returns
585    true if a host was really marked as dead or was already marked dead
586    (e.g. by a concurrent session).  */
587 static int
588 mark_host_dead (const char *name)
589 {
590   const char *host;
591   char *host_buffer = NULL;
592   parsed_uri_t parsed_uri = NULL;
593   int done = 0;
594
595   if (name && *name && !http_parse_uri (&parsed_uri, name, 1))
596     {
597       if (parsed_uri->v6lit)
598         {
599           host_buffer = strconcat ("[", parsed_uri->host, "]", NULL);
600           if (!host_buffer)
601             log_error ("out of core in mark_host_dead");
602           host = host_buffer;
603         }
604       else
605         host = parsed_uri->host;
606     }
607   else
608     host = name;
609
610   if (host && *host && strcmp (host, "localhost"))
611     {
612       hostinfo_t hi;
613       int idx;
614
615       idx = find_hostinfo (host);
616       if (idx != -1)
617         {
618           hi = hosttable[idx];
619           log_info ("marking host '%s' as dead%s\n",
620                     hi->name, hi->dead? " (again)":"");
621           hi->dead = 1;
622           hi->died_at = gnupg_get_time ();
623           if (!hi->died_at)
624             hi->died_at = 1;
625           done = 1;
626         }
627     }
628
629   http_release_parsed_uri (parsed_uri);
630   xfree (host_buffer);
631   return done;
632 }
633
634
635 /* Mark a host in the hosttable as dead or - if ALIVE is true - as
636    alive.  */
637 gpg_error_t
638 ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive)
639 {
640   gpg_error_t err = 0;
641   hostinfo_t hi, hi2;
642   int idx, idx2, idx3, n;
643
644   if (!name || !*name || !strcmp (name, "localhost"))
645     return 0;
646
647   idx = find_hostinfo (name);
648   if (idx == -1)
649     return gpg_error (GPG_ERR_NOT_FOUND);
650
651   hi = hosttable[idx];
652   if (alive && hi->dead)
653     {
654       hi->dead = 0;
655       err = ks_printf_help (ctrl, "marking '%s' as alive", name);
656     }
657   else if (!alive && !hi->dead)
658     {
659       hi->dead = 1;
660       hi->died_at = 0; /* Manually set dead.  */
661       err = ks_printf_help (ctrl, "marking '%s' as dead", name);
662     }
663
664   /* If the host is a pool mark all member hosts. */
665   if (!err && hi->pool)
666     {
667       for (idx2=0; !err && (n=hi->pool[idx2]) != -1; idx2++)
668         {
669           assert (n >= 0 && n < hosttable_size);
670
671           if (!alive)
672             {
673               /* Do not mark a host from a pool dead if it is also a
674                  member in another pool.  */
675               for (idx3=0; idx3 < hosttable_size; idx3++)
676                 {
677                   if (hosttable[idx3] && hosttable[idx3]
678                       && hosttable[idx3]->pool
679                       && idx3 != idx
680                       && host_in_pool_p (hosttable[idx3]->pool, n))
681                     break;
682                 }
683               if (idx3 < hosttable_size)
684                 continue;  /* Host is also a member of another pool.  */
685             }
686
687           hi2 = hosttable[n];
688           if (!hi2)
689             ;
690           else if (alive && hi2->dead)
691             {
692               hi2->dead = 0;
693               err = ks_printf_help (ctrl, "marking '%s' as alive",
694                                     hi2->name);
695             }
696           else if (!alive && !hi2->dead)
697             {
698               hi2->dead = 1;
699               hi2->died_at = 0; /* Manually set dead. */
700               err = ks_printf_help (ctrl, "marking '%s' as dead",
701                                     hi2->name);
702             }
703         }
704     }
705
706   return err;
707 }
708
709
710 /* Debug function to print the entire hosttable.  */
711 gpg_error_t
712 ks_hkp_print_hosttable (ctrl_t ctrl)
713 {
714   gpg_error_t err;
715   int idx, idx2;
716   hostinfo_t hi;
717   membuf_t mb;
718   time_t curtime;
719   char *p, *died;
720   const char *diedstr;
721
722   err = ks_print_help (ctrl, "hosttable (idx, ipv6, ipv4, dead, name, time):");
723   if (err)
724     return err;
725
726   curtime = gnupg_get_time ();
727   for (idx=0; idx < hosttable_size; idx++)
728     if ((hi=hosttable[idx]))
729       {
730         if (hi->dead && hi->died_at)
731           {
732             died = elapsed_time_string (hi->died_at, curtime);
733             diedstr = died? died : "error";
734           }
735         else
736           diedstr = died = NULL;
737         err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s%s\n",
738                               idx, hi->v6? "6":" ", hi->v4? "4":" ",
739                               hi->dead? "d":" ",
740                               hi->name,
741                               hi->v6addr? " v6=":"",
742                               hi->v6addr? hi->v6addr:"",
743                               hi->v4addr? " v4=":"",
744                               hi->v4addr? hi->v4addr:"",
745                               diedstr? "  (":"",
746                               diedstr? diedstr:"",
747                               diedstr? ")":""   );
748         xfree (died);
749         if (err)
750           return err;
751
752         if (hi->cname)
753           err = ks_printf_help (ctrl, "  .       %s", hi->cname);
754         if (err)
755           return err;
756
757         if (hi->pool)
758           {
759             init_membuf (&mb, 256);
760             put_membuf_printf (&mb, "  .   -->");
761             for (idx2=0; hi->pool[idx2] != -1; idx2++)
762               {
763                 put_membuf_printf (&mb, " %d", hi->pool[idx2]);
764                 if (hi->poolidx == hi->pool[idx2])
765                   put_membuf_printf (&mb, "*");
766               }
767             put_membuf( &mb, "", 1);
768             p = get_membuf (&mb, NULL);
769             if (!p)
770               return gpg_error_from_syserror ();
771             err = ks_print_help (ctrl, p);
772             xfree (p);
773             if (err)
774               return err;
775           }
776       }
777   return 0;
778 }
779
780
781
782 /* Print a help output for the schemata supported by this module. */
783 gpg_error_t
784 ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri)
785 {
786   const char const data[] =
787     "Handler for HKP URLs:\n"
788     "  hkp://\n"
789 #if  HTTP_USE_GNUTLS || HTTP_USE_NTBTLS
790     "  hkps://\n"
791 #endif
792     "Supported methods: search, get, put\n";
793   gpg_error_t err;
794
795 #if  HTTP_USE_GNUTLS || HTTP_USE_NTBTLS
796   const char data2[] = "  hkp\n  hkps";
797 #else
798   const char data2[] = "  hkp";
799 #endif
800
801   if (!uri)
802     err = ks_print_help (ctrl, data2);
803   else if (uri->is_http && (!strcmp (uri->scheme, "hkp")
804                             || !strcmp (uri->scheme, "hkps")))
805     err = ks_print_help (ctrl, data);
806   else
807     err = 0;
808
809   return err;
810 }
811
812
813 /* Build the remote part of the URL from SCHEME, HOST and an optional
814    PORT.  Returns an allocated string at R_HOSTPORT or NULL on failure
815    If R_POOLNAME is not NULL it receives a malloced string with the
816    poolname.  */
817 static gpg_error_t
818 make_host_part (ctrl_t ctrl,
819                 const char *scheme, const char *host, unsigned short port,
820                 int force_reselect,
821                 char **r_hostport, unsigned int *r_httpflags, char **r_poolname)
822 {
823   gpg_error_t err;
824   char portstr[10];
825   char *hostname;
826
827   *r_hostport = NULL;
828
829   /* Map scheme and port.  */
830   if (!strcmp (scheme, "hkps") || !strcmp (scheme,"https"))
831     {
832       scheme = "https";
833       strcpy (portstr, "443");
834     }
835   else /* HKP or HTTP.  */
836     {
837       scheme = "http";
838       strcpy (portstr, "11371");
839     }
840   if (port)
841     snprintf (portstr, sizeof portstr, "%hu", port);
842   else
843     {
844       /*fixme_do_srv_lookup ()*/
845     }
846
847   err = map_host (ctrl, host, force_reselect,
848                   &hostname, r_httpflags, r_poolname);
849   if (err)
850     return err;
851
852   *r_hostport = strconcat (scheme, "://", hostname, ":", portstr, NULL);
853   xfree (hostname);
854   if (!*r_hostport)
855     {
856       if (r_poolname)
857         {
858           xfree (*r_poolname);
859           *r_poolname = NULL;
860         }
861       return gpg_error_from_syserror ();
862     }
863   return 0;
864 }
865
866
867 /* Resolve all known keyserver names and update the hosttable.  This
868    is mainly useful for debugging because the resolving is anyway done
869    on demand.  */
870 gpg_error_t
871 ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri)
872 {
873   gpg_error_t err;
874   char *hostport = NULL;
875
876   err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, 1,
877                         &hostport, NULL, NULL);
878   if (err)
879     {
880       err = ks_printf_help (ctrl, "%s://%s:%hu: resolve failed: %s",
881                             uri->scheme, uri->host, uri->port,
882                             gpg_strerror (err));
883     }
884   else
885     {
886       err = ks_printf_help (ctrl, "%s", hostport);
887       xfree (hostport);
888     }
889   return err;
890 }
891
892
893 /* Housekeeping function called from the housekeeping thread.  It is
894    used to mark dead hosts alive so that they may be tried again after
895    some time.  */
896 void
897 ks_hkp_housekeeping (time_t curtime)
898 {
899   int idx;
900   hostinfo_t hi;
901
902   for (idx=0; idx < hosttable_size; idx++)
903     {
904       hi = hosttable[idx];
905       if (!hi)
906         continue;
907       if (!hi->dead)
908         continue;
909       if (!hi->died_at)
910         continue; /* Do not resurrect manually shot hosts.  */
911       if (hi->died_at + RESURRECT_INTERVAL <= curtime
912           || hi->died_at > curtime)
913         {
914           hi->dead = 0;
915           log_info ("resurrected host '%s'", hi->name);
916         }
917     }
918 }
919
920
921 /* Send an HTTP request.  On success returns an estream object at
922    R_FP.  HOSTPORTSTR is only used for diagnostics.  If HTTPHOST is
923    not NULL it will be used as HTTP "Host" header.  If POST_CB is not
924    NULL a post request is used and that callback is called to allow
925    writing the post data.  */
926 static gpg_error_t
927 send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
928               const char *httphost, unsigned int httpflags,
929               gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value,
930               estream_t *r_fp)
931 {
932   gpg_error_t err;
933   http_session_t session = NULL;
934   http_t http = NULL;
935   int redirects_left = MAX_REDIRECTS;
936   estream_t fp = NULL;
937   char *request_buffer = NULL;
938
939   *r_fp = NULL;
940
941   err = http_session_new (&session, NULL);
942   if (err)
943     goto leave;
944   http_session_set_log_cb (session, cert_log_cb);
945
946  once_more:
947   err = http_open (&http,
948                    post_cb? HTTP_REQ_POST : HTTP_REQ_GET,
949                    request,
950                    httphost,
951                    /* fixme: AUTH */ NULL,
952                    httpflags,
953                    /* fixme: proxy*/ NULL,
954                    session,
955                    NULL,
956                    /*FIXME curl->srvtag*/NULL);
957   if (!err)
958     {
959       fp = http_get_write_ptr (http);
960       /* Avoid caches to get the most recent copy of the key.  We set
961          both the Pragma and Cache-Control versions of the header, so
962          we're good with both HTTP 1.0 and 1.1.  */
963       es_fputs ("Pragma: no-cache\r\n"
964                 "Cache-Control: no-cache\r\n", fp);
965       if (post_cb)
966         err = post_cb (post_cb_value, http);
967       if (!err)
968         {
969           http_start_data (http);
970           if (es_ferror (fp))
971             err = gpg_error_from_syserror ();
972         }
973     }
974   if (err)
975     {
976       /* Fixme: After a redirection we show the old host name.  */
977       log_error (_("error connecting to '%s': %s\n"),
978                  hostportstr, gpg_strerror (err));
979       goto leave;
980     }
981
982   /* Wait for the response.  */
983   dirmngr_tick (ctrl);
984   err = http_wait_response (http);
985   if (err)
986     {
987       log_error (_("error reading HTTP response for '%s': %s\n"),
988                  hostportstr, gpg_strerror (err));
989       goto leave;
990     }
991
992   if (http_get_tls_info (http, NULL))
993     {
994       /* Update the httpflags so that a redirect won't fallback to an
995          unencrypted connection.  */
996       httpflags |= HTTP_FLAG_FORCE_TLS;
997     }
998
999   switch (http_get_status_code (http))
1000     {
1001     case 200:
1002       err = 0;
1003       break; /* Success.  */
1004
1005     case 301:
1006     case 302:
1007     case 307:
1008       {
1009         const char *s = http_get_header (http, "Location");
1010
1011         log_info (_("URL '%s' redirected to '%s' (%u)\n"),
1012                   request, s?s:"[none]", http_get_status_code (http));
1013         if (s && *s && redirects_left-- )
1014           {
1015             xfree (request_buffer);
1016             request_buffer = xtrystrdup (s);
1017             if (request_buffer)
1018               {
1019                 request = request_buffer;
1020                 http_close (http, 0);
1021                 http = NULL;
1022                 goto once_more;
1023               }
1024             err = gpg_error_from_syserror ();
1025           }
1026         else
1027           err = gpg_error (GPG_ERR_NO_DATA);
1028         log_error (_("too many redirections\n"));
1029       }
1030       goto leave;
1031
1032     default:
1033       log_error (_("error accessing '%s': http status %u\n"),
1034                  request, http_get_status_code (http));
1035       err = gpg_error (GPG_ERR_NO_DATA);
1036       goto leave;
1037     }
1038
1039   /* FIXME: We should register a permanent redirection and whether a
1040      host has ever used TLS so that future calls will always use
1041      TLS. */
1042
1043   fp = http_get_read_ptr (http);
1044   if (!fp)
1045     {
1046       err = gpg_error (GPG_ERR_BUG);
1047       goto leave;
1048     }
1049
1050   /* Return the read stream and close the HTTP context.  */
1051   *r_fp = fp;
1052   http_close (http, 1);
1053   http = NULL;
1054
1055  leave:
1056   http_close (http, 0);
1057   http_session_release (session);
1058   xfree (request_buffer);
1059   return err;
1060 }
1061
1062
1063 /* Helper to evaluate the error code ERR form a send_request() call
1064    with REQUEST.  The function returns true if the caller shall try
1065    again.  TRIES_LEFT points to a variable to track the number of
1066    retries; this function decrements it and won't return true if it is
1067    down to zero. */
1068 static int
1069 handle_send_request_error (gpg_error_t err, const char *request,
1070                            unsigned int *tries_left)
1071 {
1072   int retry = 0;
1073
1074   switch (gpg_err_code (err))
1075     {
1076     case GPG_ERR_ECONNREFUSED:
1077     case GPG_ERR_ENETUNREACH:
1078     case GPG_ERR_UNKNOWN_HOST:
1079     case GPG_ERR_NETWORK:
1080       if (mark_host_dead (request) && *tries_left)
1081         retry = 1;
1082       break;
1083
1084     case GPG_ERR_ETIMEDOUT:
1085       if (*tries_left)
1086         {
1087           log_info ("selecting a different host due to a timeout\n");
1088           retry = 1;
1089         }
1090
1091     default:
1092       break;
1093     }
1094
1095   if (*tries_left)
1096     --*tries_left;
1097
1098   return retry;
1099 }
1100
1101 static gpg_error_t
1102 armor_data (char **r_string, const void *data, size_t datalen)
1103 {
1104   gpg_error_t err;
1105   struct b64state b64state;
1106   estream_t fp;
1107   long length;
1108   char *buffer;
1109   size_t nread;
1110
1111   *r_string = NULL;
1112
1113   fp = es_fopenmem (0, "rw,samethread");
1114   if (!fp)
1115     return gpg_error_from_syserror ();
1116
1117   if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK"))
1118       || (err=b64enc_write (&b64state, data, datalen))
1119       || (err = b64enc_finish (&b64state)))
1120     {
1121       es_fclose (fp);
1122       return err;
1123     }
1124
1125   /* FIXME: To avoid the extra buffer allocation estream should
1126      provide a function to snatch the internal allocated memory from
1127      such a memory stream.  */
1128   length = es_ftell (fp);
1129   if (length < 0)
1130     {
1131       err = gpg_error_from_syserror ();
1132       es_fclose (fp);
1133       return err;
1134     }
1135
1136   buffer = xtrymalloc (length+1);
1137   if (!buffer)
1138     {
1139       err = gpg_error_from_syserror ();
1140       es_fclose (fp);
1141       return err;
1142     }
1143
1144   es_rewind (fp);
1145   if (es_read (fp, buffer, length, &nread))
1146     {
1147       err = gpg_error_from_syserror ();
1148       es_fclose (fp);
1149       return err;
1150     }
1151   buffer[nread] = 0;
1152   es_fclose (fp);
1153
1154   *r_string = buffer;
1155   return 0;
1156 }
1157
1158
1159 \f
1160 /* Search the keyserver identified by URI for keys matching PATTERN.
1161    On success R_FP has an open stream to read the data.  */
1162 gpg_error_t
1163 ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
1164                estream_t *r_fp)
1165 {
1166   gpg_error_t err;
1167   KEYDB_SEARCH_DESC desc;
1168   char fprbuf[2+40+1];
1169   char *hostport = NULL;
1170   char *request = NULL;
1171   estream_t fp = NULL;
1172   int reselect;
1173   unsigned int httpflags;
1174   char *httphost = NULL;
1175   unsigned int tries = SEND_REQUEST_RETRIES;
1176
1177   *r_fp = NULL;
1178
1179   /* Remove search type indicator and adjust PATTERN accordingly.
1180      Note that HKP keyservers like the 0x to be present when searching
1181      by keyid.  We need to re-format the fingerprint and keyids so to
1182      remove the gpg specific force-use-of-this-key flag ("!").  */
1183   err = classify_user_id (pattern, &desc, 1);
1184   if (err)
1185     return err;
1186   switch (desc.mode)
1187     {
1188     case KEYDB_SEARCH_MODE_EXACT:
1189     case KEYDB_SEARCH_MODE_SUBSTR:
1190     case KEYDB_SEARCH_MODE_MAIL:
1191     case KEYDB_SEARCH_MODE_MAILSUB:
1192       pattern = desc.u.name;
1193       break;
1194     case KEYDB_SEARCH_MODE_SHORT_KID:
1195       snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]);
1196       pattern = fprbuf;
1197       break;
1198     case KEYDB_SEARCH_MODE_LONG_KID:
1199       snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX",
1200                 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1201       pattern = fprbuf;
1202       break;
1203     case KEYDB_SEARCH_MODE_FPR16:
1204       bin2hex (desc.u.fpr, 16, fprbuf);
1205       pattern = fprbuf;
1206       break;
1207     case KEYDB_SEARCH_MODE_FPR20:
1208     case KEYDB_SEARCH_MODE_FPR:
1209       bin2hex (desc.u.fpr, 20, fprbuf);
1210       pattern = fprbuf;
1211       break;
1212     default:
1213       return gpg_error (GPG_ERR_INV_USER_ID);
1214     }
1215
1216   /* Build the request string.  */
1217   reselect = 0;
1218  again:
1219   {
1220     char *searchkey;
1221
1222     xfree (hostport); hostport = NULL;
1223     xfree (httphost); httphost = NULL;
1224     err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect,
1225                           &hostport, &httpflags, &httphost);
1226     if (err)
1227       goto leave;
1228
1229     searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS);
1230     if (!searchkey)
1231       {
1232         err = gpg_error_from_syserror ();
1233         goto leave;
1234       }
1235
1236     xfree (request);
1237     request = strconcat (hostport,
1238                          "/pks/lookup?op=index&options=mr&search=",
1239                          searchkey,
1240                          NULL);
1241     xfree (searchkey);
1242     if (!request)
1243       {
1244         err = gpg_error_from_syserror ();
1245         goto leave;
1246       }
1247   }
1248
1249   /* Send the request.  */
1250   err = send_request (ctrl, request, hostport, httphost, httpflags,
1251                       NULL, NULL, &fp);
1252   if (handle_send_request_error (err, request, &tries))
1253     {
1254       reselect = 1;
1255       goto again;
1256     }
1257   if (err)
1258     goto leave;
1259
1260   err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1261   if (err)
1262     goto leave;
1263
1264   /* Peek at the response.  */
1265   {
1266     int c = es_getc (fp);
1267     if (c == -1)
1268       {
1269         err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF);
1270         log_error ("error reading response: %s\n", gpg_strerror (err));
1271         goto leave;
1272       }
1273     if (c == '<')
1274       {
1275         /* The document begins with a '<': Assume a HTML response,
1276            which we don't support.  */
1277         err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
1278         goto leave;
1279       }
1280     es_ungetc (c, fp);
1281   }
1282
1283   /* Return the read stream.  */
1284   *r_fp = fp;
1285   fp = NULL;
1286
1287  leave:
1288   es_fclose (fp);
1289   xfree (request);
1290   xfree (hostport);
1291   xfree (httphost);
1292   return err;
1293 }
1294
1295
1296 /* Get the key described key the KEYSPEC string from the keyserver
1297    identified by URI.  On success R_FP has an open stream to read the
1298    data.  */
1299 gpg_error_t
1300 ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
1301 {
1302   gpg_error_t err;
1303   KEYDB_SEARCH_DESC desc;
1304   char kidbuf[2+40+1];
1305   const char *exactname = NULL;
1306   char *searchkey = NULL;
1307   char *hostport = NULL;
1308   char *request = NULL;
1309   estream_t fp = NULL;
1310   int reselect;
1311   char *httphost = NULL;
1312   unsigned int httpflags;
1313   unsigned int tries = SEND_REQUEST_RETRIES;
1314
1315   *r_fp = NULL;
1316
1317   /* Remove search type indicator and adjust PATTERN accordingly.
1318      Note that HKP keyservers like the 0x to be present when searching
1319      by keyid.  We need to re-format the fingerprint and keyids so to
1320      remove the gpg specific force-use-of-this-key flag ("!").  */
1321   err = classify_user_id (keyspec, &desc, 1);
1322   if (err)
1323     return err;
1324   switch (desc.mode)
1325     {
1326     case KEYDB_SEARCH_MODE_SHORT_KID:
1327       snprintf (kidbuf, sizeof kidbuf, "0x%08lX", (ulong)desc.u.kid[1]);
1328       break;
1329     case KEYDB_SEARCH_MODE_LONG_KID:
1330       snprintf (kidbuf, sizeof kidbuf, "0x%08lX%08lX",
1331                 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1332       break;
1333     case KEYDB_SEARCH_MODE_FPR20:
1334     case KEYDB_SEARCH_MODE_FPR:
1335       /* This is a v4 fingerprint. */
1336       kidbuf[0] = '0';
1337       kidbuf[1] = 'x';
1338       bin2hex (desc.u.fpr, 20, kidbuf+2);
1339       break;
1340
1341     case KEYDB_SEARCH_MODE_EXACT:
1342       exactname = desc.u.name;
1343       break;
1344
1345     case KEYDB_SEARCH_MODE_FPR16:
1346       log_error ("HKP keyservers do not support v3 fingerprints\n");
1347     default:
1348       return gpg_error (GPG_ERR_INV_USER_ID);
1349     }
1350
1351   searchkey = http_escape_string (exactname? exactname : kidbuf,
1352                                   EXTRA_ESCAPE_CHARS);
1353   if (!searchkey)
1354     {
1355       err = gpg_error_from_syserror ();
1356       goto leave;
1357     }
1358
1359   reselect = 0;
1360  again:
1361   /* Build the request string.  */
1362   xfree (hostport); hostport = NULL;
1363   xfree (httphost); httphost = NULL;
1364   err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect,
1365                         &hostport, &httpflags, &httphost);
1366   if (err)
1367     goto leave;
1368
1369   xfree (request);
1370   request = strconcat (hostport,
1371                        "/pks/lookup?op=get&options=mr&search=",
1372                        searchkey,
1373                        exactname? "&exact=on":"",
1374                        NULL);
1375   if (!request)
1376     {
1377       err = gpg_error_from_syserror ();
1378       goto leave;
1379     }
1380
1381   /* Send the request.  */
1382   err = send_request (ctrl, request, hostport, httphost, httpflags,
1383                       NULL, NULL, &fp);
1384   if (handle_send_request_error (err, request, &tries))
1385     {
1386       reselect = 1;
1387       goto again;
1388     }
1389   if (err)
1390     goto leave;
1391
1392   err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1393   if (err)
1394     goto leave;
1395
1396   /* Return the read stream and close the HTTP context.  */
1397   *r_fp = fp;
1398   fp = NULL;
1399
1400  leave:
1401   es_fclose (fp);
1402   xfree (request);
1403   xfree (hostport);
1404   xfree (httphost);
1405   xfree (searchkey);
1406   return err;
1407 }
1408
1409
1410
1411 \f
1412 /* Callback parameters for put_post_cb.  */
1413 struct put_post_parm_s
1414 {
1415   char *datastring;
1416 };
1417
1418
1419 /* Helper for ks_hkp_put.  */
1420 static gpg_error_t
1421 put_post_cb (void *opaque, http_t http)
1422 {
1423   struct put_post_parm_s *parm = opaque;
1424   gpg_error_t err = 0;
1425   estream_t fp;
1426   size_t len;
1427
1428   fp = http_get_write_ptr (http);
1429   len = strlen (parm->datastring);
1430
1431   es_fprintf (fp,
1432               "Content-Type: application/x-www-form-urlencoded\r\n"
1433               "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */);
1434   http_start_data (http);
1435   if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL))
1436     err = gpg_error_from_syserror ();
1437   return err;
1438 }
1439
1440
1441 /* Send the key in {DATA,DATALEN} to the keyserver identified by  URI.  */
1442 gpg_error_t
1443 ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
1444 {
1445   gpg_error_t err;
1446   char *hostport = NULL;
1447   char *request = NULL;
1448   estream_t fp = NULL;
1449   struct put_post_parm_s parm;
1450   char *armored = NULL;
1451   int reselect;
1452   char *httphost = NULL;
1453   unsigned int httpflags;
1454   unsigned int tries = SEND_REQUEST_RETRIES;
1455
1456   parm.datastring = NULL;
1457
1458   err = armor_data (&armored, data, datalen);
1459   if (err)
1460     goto leave;
1461
1462   parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
1463   if (!parm.datastring)
1464     {
1465       err = gpg_error_from_syserror ();
1466       goto leave;
1467     }
1468   xfree (armored);
1469   armored = NULL;
1470
1471   /* Build the request string.  */
1472   reselect = 0;
1473  again:
1474   xfree (hostport); hostport = NULL;
1475   xfree (httphost); httphost = NULL;
1476   err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect,
1477                         &hostport, &httpflags, &httphost);
1478   if (err)
1479     goto leave;
1480
1481   xfree (request);
1482   request = strconcat (hostport, "/pks/add", NULL);
1483   if (!request)
1484     {
1485       err = gpg_error_from_syserror ();
1486       goto leave;
1487     }
1488
1489   /* Send the request.  */
1490   err = send_request (ctrl, request, hostport, httphost, 0,
1491                       put_post_cb, &parm, &fp);
1492   if (handle_send_request_error (err, request, &tries))
1493     {
1494       reselect = 1;
1495       goto again;
1496     }
1497   if (err)
1498     goto leave;
1499
1500  leave:
1501   es_fclose (fp);
1502   xfree (parm.datastring);
1503   xfree (armored);
1504   xfree (request);
1505   xfree (hostport);
1506   xfree (httphost);
1507   return err;
1508 }