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