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