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