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