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