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