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