dirmngr: Make use of IPv4 and IPV6 more explicit.
[gnupg.git] / dirmngr / ks-engine-hkp.c
1 /* ks-engine-hkp.c - HKP keyserver engine
2  * Copyright (C) 2011, 2012 Free Software Foundation, Inc.
3  * Copyright (C) 2011, 2012, 2014 Werner Koch
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://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 "ks-engine.h"
42
43 /* Substitute a missing Mingw macro.  */
44 #ifndef EAI_OVERFLOW
45 # define EAI_OVERFLOW EAI_FAIL
46 #endif
47
48
49 /* To match the behaviour of our old gpgkeys helper code we escape
50    more characters than actually needed. */
51 #define EXTRA_ESCAPE_CHARS "@!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
52
53 /* How many redirections do we allow.  */
54 #define MAX_REDIRECTS 2
55
56 /* Number of retries done for a dead host etc.  */
57 #define SEND_REQUEST_RETRIES 3
58
59 /* Objects used to maintain information about hosts.  */
60 struct hostinfo_s;
61 typedef struct hostinfo_s *hostinfo_t;
62 struct hostinfo_s
63 {
64   time_t lastfail;   /* Time we tried to connect and failed.  */
65   time_t lastused;   /* Time of last use.  */
66   int *pool;         /* A -1 terminated array with indices into
67                         HOSTTABLE or NULL if NAME is not a pool
68                         name.  */
69   int poolidx;       /* Index into POOL with the used host.  */
70   unsigned int v4:1; /* Host supports AF_INET.  */
71   unsigned int v6:1; /* Host supports AF_INET6.  */
72   unsigned int dead:1; /* Host is currently unresponsive.  */
73   char name[1];      /* The hostname.  */
74 };
75
76
77 /* An array of hostinfo_t for all hosts requested by the caller or
78    resolved from a pool name and its allocated size.*/
79 static hostinfo_t *hosttable;
80 static int hosttable_size;
81
82 /* The number of host slots we initally allocate for HOSTTABLE.  */
83 #define INITIAL_HOSTTABLE_SIZE 10
84
85
86 /* Create a new hostinfo object, fill in NAME and put it into
87    HOSTTABLE.  Return the index into hosttable on success or -1 on
88    error. */
89 static int
90 create_new_hostinfo (const char *name)
91 {
92   hostinfo_t hi, *newtable;
93   int newsize;
94   int idx, rc;
95
96   hi = xtrymalloc (sizeof *hi + strlen (name));
97   if (!hi)
98     return -1;
99   strcpy (hi->name, name);
100   hi->pool = NULL;
101   hi->poolidx = -1;
102   hi->lastused = (time_t)(-1);
103   hi->lastfail = (time_t)(-1);
104   hi->v4 = 0;
105   hi->v6 = 0;
106   hi->dead = 0;
107
108   /* Add it to the hosttable. */
109   for (idx=0; idx < hosttable_size; idx++)
110     if (!hosttable[idx])
111       {
112         hosttable[idx] = hi;
113         return idx;
114       }
115   /* Need to extend the hosttable.  */
116   newsize = hosttable_size + INITIAL_HOSTTABLE_SIZE;
117   newtable = xtryrealloc (hosttable, newsize * sizeof *hosttable);
118   if (!newtable)
119     {
120       xfree (hi);
121       return -1;
122     }
123   hosttable = newtable;
124   idx = hosttable_size;
125   hosttable_size = newsize;
126   rc = idx;
127   hosttable[idx++] = hi;
128   while (idx < hosttable_size)
129     hosttable[idx++] = NULL;
130
131   return rc;
132 }
133
134
135 /* Find the host NAME in our table.  Return the index into the
136    hosttable or -1 if not found.  */
137 static int
138 find_hostinfo (const char *name)
139 {
140   int idx;
141
142   for (idx=0; idx < hosttable_size; idx++)
143     if (hosttable[idx] && !ascii_strcasecmp (hosttable[idx]->name, name))
144       return idx;
145   return -1;
146 }
147
148
149 static int
150 sort_hostpool (const void *xa, const void *xb)
151 {
152   int a = *(int *)xa;
153   int b = *(int *)xb;
154
155   assert (a >= 0 && a < hosttable_size);
156   assert (b >= 0 && b < hosttable_size);
157   assert (hosttable[a]);
158   assert (hosttable[b]);
159
160   return ascii_strcasecmp (hosttable[a]->name, hosttable[b]->name);
161 }
162
163
164 /* Return true if the host with the hosttable index TBLIDX is in POOL.  */
165 static int
166 host_in_pool_p (int *pool, int tblidx)
167 {
168   int i, pidx;
169
170   for (i=0; (pidx = pool[i]) != -1; i++)
171     if (pidx == tblidx && hosttable[pidx])
172       return 1;
173   return 0;
174 }
175
176
177 /* Select a random host.  Consult TABLE which indices into the global
178    hosttable.  Returns index into TABLE or -1 if no host could be
179    selected.  */
180 static int
181 select_random_host (int *table)
182 {
183   int *tbl;
184   size_t tblsize;
185   int pidx, idx;
186
187   /* We create a new table so that we randomly select only from
188      currently alive hosts.  */
189   for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++)
190     if (hosttable[pidx] && !hosttable[pidx]->dead)
191       tblsize++;
192   if (!tblsize)
193     return -1; /* No hosts.  */
194
195   tbl = xtrymalloc (tblsize * sizeof *tbl);
196   if (!tbl)
197     return -1;
198   for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++)
199     if (hosttable[pidx] && !hosttable[pidx]->dead)
200       tbl[tblsize++] = pidx;
201
202   if (tblsize == 1)  /* Save a get_uint_nonce.  */
203     pidx = tbl[0];
204   else
205     pidx = tbl[get_uint_nonce () % tblsize];
206
207   xfree (tbl);
208   return pidx;
209 }
210
211
212 /* Simplified version of getnameinfo which also returns a numeric
213    hostname inside of brackets.  The caller should provide a buffer
214    for TMPHOST which is 2 bytes larger than the the largest hostname.
215    returns 0 on success or an EAI error code.  */
216 static int
217 my_getnameinfo (struct addrinfo *ai, char *host, size_t hostlen)
218 {
219   int ec;
220   char *p;
221
222   if (hostlen < 5)
223     return EAI_OVERFLOW;
224
225   ec = getnameinfo (ai->ai_addr, ai->ai_addrlen,
226                     host, hostlen, NULL, 0, NI_NAMEREQD);
227   if (!ec && *host == '[')
228     ec = EAI_FAIL;  /* A name may never start with a bracket.  */
229   else if (ec == EAI_NONAME)
230     {
231       p = host;
232       if (ai->ai_family == AF_INET6)
233         {
234           *p++ = '[';
235           hostlen -= 2;
236         }
237       ec = getnameinfo (ai->ai_addr, ai->ai_addrlen,
238                         p, hostlen, NULL, 0, NI_NUMERICHOST);
239       if (!ec && ai->ai_family == AF_INET6)
240         strcat (host, "]");
241     }
242
243   return ec;
244 }
245
246
247 /* Map the host name NAME to the actual to be used host name.  This
248    allows us to manage round robin DNS names.  We use our own strategy
249    to choose one of the hosts.  For example we skip those hosts which
250    failed for some time and we stick to one host for a time
251    independent of DNS retry times.  If FORCE_RESELECT is true a new
252    host is always selected.  If R_HTTPFLAGS is not NULL if will
253    received flags which are to be passed to http_open. */
254 static char *
255 map_host (ctrl_t ctrl, const char *name, int force_reselect,
256           unsigned int *r_httpflags)
257 {
258   hostinfo_t hi;
259   int idx;
260
261   if (r_httpflags)
262     *r_httpflags = 0;
263
264   /* No hostname means localhost.  */
265   if (!name || !*name)
266     return xtrystrdup ("localhost");
267
268   /* See whether the host is in our table.  */
269   idx = find_hostinfo (name);
270   if (idx == -1)
271     {
272       /* We never saw this host.  Allocate a new entry.  */
273       struct addrinfo hints, *aibuf, *ai;
274       int *reftbl;
275       size_t reftblsize;
276       int refidx;
277
278       reftblsize = 100;
279       reftbl = xtrymalloc (reftblsize * sizeof *reftbl);
280       if (!reftbl)
281         return NULL;
282       refidx = 0;
283
284       idx = create_new_hostinfo (name);
285       if (idx == -1)
286         {
287           xfree (reftbl);
288           return NULL;
289         }
290       hi = hosttable[idx];
291
292       /* Find all A records for this entry and put them into the pool
293          list - if any.  */
294       memset (&hints, 0, sizeof (hints));
295       hints.ai_socktype = SOCK_STREAM;
296       if (!getaddrinfo (name, NULL, &hints, &aibuf))
297         {
298           for (ai = aibuf; ai; ai = ai->ai_next)
299             {
300               char tmphost[NI_MAXHOST];
301               int tmpidx;
302               int ec;
303               int i;
304
305               if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
306                 continue;
307
308               dirmngr_tick (ctrl);
309               if ((ec = my_getnameinfo (ai, tmphost, sizeof tmphost)))
310                 {
311                   log_info ("getnameinfo failed while checking '%s': %s\n",
312                             name, gai_strerror (ec));
313                 }
314               else if (refidx+1 >= reftblsize)
315                 {
316                   log_error ("getnameinfo returned for '%s': '%s'"
317                             " [index table full - ignored]\n", name, tmphost);
318                 }
319               else
320                 {
321
322                   if ((tmpidx = find_hostinfo (tmphost)) != -1)
323                     {
324                       log_info ("getnameinfo returned for '%s': '%s'"
325                                 " [already known]\n", name, tmphost);
326                       if (ai->ai_family == AF_INET)
327                         hosttable[tmpidx]->v4 = 1;
328                       if (ai->ai_family == AF_INET6)
329                         hosttable[tmpidx]->v6 = 1;
330
331                       for (i=0; i < refidx; i++)
332                         if (reftbl[i] == tmpidx)
333                           break;
334                       if (!(i < refidx) && tmpidx != idx)
335                         reftbl[refidx++] = tmpidx;
336                     }
337                   else
338                     {
339                       log_info ("getnameinfo returned for '%s': '%s'\n",
340                                 name, tmphost);
341                       /* Create a new entry.  */
342                       tmpidx = create_new_hostinfo (tmphost);
343                       if (tmpidx == -1)
344                         log_error ("map_host for '%s' problem: %s - '%s'"
345                                    " [ignored]\n",
346                                    name, strerror (errno), tmphost);
347                       else
348                         {
349                           if (ai->ai_family == AF_INET)
350                             hosttable[tmpidx]->v4 = 1;
351                           if (ai->ai_family == AF_INET6)
352                             hosttable[tmpidx]->v6 = 1;
353
354                           for (i=0; i < refidx; i++)
355                             if (reftbl[i] == tmpidx)
356                               break;
357                           if (!(i < refidx) && tmpidx != idx)
358                             reftbl[refidx++] = tmpidx;
359                         }
360                     }
361                 }
362             }
363         }
364       reftbl[refidx] = -1;
365       if (refidx)
366         {
367           assert (!hi->pool);
368           hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl);
369           if (!hi->pool)
370             {
371               log_error ("shrinking index table in map_host failed: %s\n",
372                          strerror (errno));
373               xfree (reftbl);
374             }
375           qsort (reftbl, refidx, sizeof *reftbl, sort_hostpool);
376         }
377       else
378         xfree (reftbl);
379     }
380
381   hi = hosttable[idx];
382   if (hi->pool)
383     {
384       /* If the currently selected host is now marked dead, force a
385          re-selection .  */
386       if (force_reselect)
387         hi->poolidx = -1;
388       else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size
389                && hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead)
390         hi->poolidx = -1;
391
392       /* Select a host if needed.  */
393       if (hi->poolidx == -1)
394         {
395           hi->poolidx = select_random_host (hi->pool);
396           if (hi->poolidx == -1)
397             {
398               log_error ("no alive host found in pool '%s'\n", name);
399               return NULL;
400             }
401         }
402
403       assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size);
404       hi = hosttable[hi->poolidx];
405       assert (hi);
406     }
407
408   if (hi->dead)
409     {
410       log_error ("host '%s' marked as dead\n", hi->name);
411       return NULL;
412     }
413
414   if (r_httpflags)
415     {
416       /* If the hosttable does not indicate that a certain host
417          supports IPv<N>, we explicit set the corresponding http
418          flags.  The reason for this is that a host might be listed in
419          a pool as not v6 only but actually support v6 when later
420          resolved the name is resolved by our http layer.  */
421       if (!hi->v4)
422         *r_httpflags |= HTTP_FLAG_IGNORE_IPv4;
423       if (!hi->v6)
424         *r_httpflags |= HTTP_FLAG_IGNORE_IPv6;
425     }
426   return xtrystrdup (hi->name);
427 }
428
429
430 /* Mark the host NAME as dead.  NAME may be given as an URL.  Returns
431    true if a host was really marked as dead or was already marked dead
432    (e.g. by a concurrent session).  */
433 static int
434 mark_host_dead (const char *name)
435 {
436   const char *host;
437   char *host_buffer = NULL;
438   parsed_uri_t parsed_uri = NULL;
439   int done = 0;
440
441   if (name && *name && !http_parse_uri (&parsed_uri, name, 1))
442     {
443       if (parsed_uri->v6lit)
444         {
445           host_buffer = strconcat ("[", parsed_uri->host, "]", NULL);
446           if (!host_buffer)
447             log_error ("out of core in mark_host_dead");
448           host = host_buffer;
449         }
450       else
451         host = parsed_uri->host;
452     }
453   else
454     host = name;
455
456   if (host && *host && strcmp (host, "localhost"))
457     {
458       hostinfo_t hi;
459       int idx;
460
461       idx = find_hostinfo (host);
462       if (idx != -1)
463         {
464           hi = hosttable[idx];
465           log_info ("marking host '%s' as dead%s\n",
466                     hi->name, hi->dead? " (again)":"");
467           hi->dead = 1;
468           done = 1;
469         }
470     }
471
472   http_release_parsed_uri (parsed_uri);
473   xfree (host_buffer);
474   return done;
475 }
476
477
478 /* Mark a host in the hosttable as dead or - if ALIVE is true - as
479    alive.  */
480 gpg_error_t
481 ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive)
482 {
483   gpg_error_t err = 0;
484   hostinfo_t hi, hi2;
485   int idx, idx2, idx3, n;
486
487   if (!name || !*name || !strcmp (name, "localhost"))
488     return 0;
489
490   idx = find_hostinfo (name);
491   if (idx == -1)
492     return gpg_error (GPG_ERR_NOT_FOUND);
493
494   hi = hosttable[idx];
495   if (alive && hi->dead)
496     {
497       hi->dead = 0;
498       err = ks_printf_help (ctrl, "marking '%s' as alive", name);
499     }
500   else if (!alive && !hi->dead)
501     {
502       hi->dead = 1;
503       err = ks_printf_help (ctrl, "marking '%s' as dead", name);
504     }
505
506   /* If the host is a pool mark all member hosts. */
507   if (!err && hi->pool)
508     {
509       for (idx2=0; !err && (n=hi->pool[idx2]) != -1; idx2++)
510         {
511           assert (n >= 0 && n < hosttable_size);
512
513           if (!alive)
514             {
515               /* Do not mark a host from a pool dead if it is also a
516                  member in another pool.  */
517               for (idx3=0; idx3 < hosttable_size; idx3++)
518                 {
519                   if (hosttable[idx3] && hosttable[idx3]
520                       && hosttable[idx3]->pool
521                       && idx3 != idx
522                       && host_in_pool_p (hosttable[idx3]->pool, n))
523                     break;
524                 }
525               if (idx3 < hosttable_size)
526                 continue;  /* Host is also a member of another pool.  */
527             }
528
529           hi2 = hosttable[n];
530           if (!hi2)
531             ;
532           else if (alive && hi2->dead)
533             {
534               hi2->dead = 0;
535               err = ks_printf_help (ctrl, "marking '%s' as alive",
536                                     hi2->name);
537             }
538           else if (!alive && !hi2->dead)
539             {
540               hi2->dead = 1;
541               err = ks_printf_help (ctrl, "marking '%s' as dead",
542                                     hi2->name);
543             }
544         }
545     }
546
547   return err;
548 }
549
550
551 /* Debug function to print the entire hosttable.  */
552 gpg_error_t
553 ks_hkp_print_hosttable (ctrl_t ctrl)
554 {
555   gpg_error_t err;
556   int idx, idx2;
557   hostinfo_t hi;
558   membuf_t mb;
559   char *p;
560
561   err = ks_print_help (ctrl, "hosttable (idx, ipv4, ipv6, dead, name):");
562   if (err)
563     return err;
564
565   for (idx=0; idx < hosttable_size; idx++)
566     if ((hi=hosttable[idx]))
567       {
568         err = ks_printf_help (ctrl, "%3d %s %s %s %s\n",
569                               idx, hi->v4? "4":" ", hi->v6? "6":" ",
570                               hi->dead? "d":" ", hi->name);
571         if (err)
572           return err;
573         if (hi->pool)
574           {
575             init_membuf (&mb, 256);
576             put_membuf_printf (&mb, "  .   -->");
577             for (idx2=0; hi->pool[idx2] != -1; idx2++)
578               {
579                 put_membuf_printf (&mb, " %d", hi->pool[idx2]);
580                 if (hi->poolidx == hi->pool[idx2])
581                   put_membuf_printf (&mb, "*");
582               }
583             put_membuf( &mb, "", 1);
584             p = get_membuf (&mb, NULL);
585             if (!p)
586               return gpg_error_from_syserror ();
587             err = ks_print_help (ctrl, p);
588             xfree (p);
589             if (err)
590               return err;
591           }
592       }
593   return 0;
594 }
595
596
597
598 /* Print a help output for the schemata supported by this module. */
599 gpg_error_t
600 ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri)
601 {
602   const char const data[] =
603     "Handler for HKP URLs:\n"
604     "  hkp://\n"
605     "Supported methods: search, get, put\n";
606   gpg_error_t err;
607
608   if (!uri)
609     err = ks_print_help (ctrl, "  hkp");
610   else if (uri->is_http && !strcmp (uri->scheme, "hkp"))
611     err = ks_print_help (ctrl, data);
612   else
613     err = 0;
614
615   return err;
616 }
617
618
619 /* Build the remote part or the URL from SCHEME, HOST and an optional
620    PORT.  Returns an allocated string or NULL on failure and sets
621    ERRNO.  */
622 static char *
623 make_host_part (ctrl_t ctrl,
624                 const char *scheme, const char *host, unsigned short port,
625                 int force_reselect, unsigned int *r_httpflags)
626 {
627   char portstr[10];
628   char *hostname;
629   char *hostport;
630
631   /* Map scheme and port.  */
632   if (!strcmp (scheme, "hkps") || !strcmp (scheme,"https"))
633     {
634       scheme = "https";
635       strcpy (portstr, "443");
636     }
637   else /* HKP or HTTP.  */
638     {
639       scheme = "http";
640       strcpy (portstr, "11371");
641     }
642   if (port)
643     snprintf (portstr, sizeof portstr, "%hu", port);
644   else
645     {
646       /*fixme_do_srv_lookup ()*/
647     }
648
649   hostname = map_host (ctrl, host, force_reselect, r_httpflags);
650   if (!hostname)
651     return NULL;
652
653   hostport = strconcat (scheme, "://", hostname, ":", portstr, NULL);
654   xfree (hostname);
655   return hostport;
656 }
657
658
659 /* Resolve all known keyserver names and update the hosttable.  This
660    is mainly useful for debugging because the resolving is anyway done
661    on demand.  */
662 gpg_error_t
663 ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri)
664 {
665   gpg_error_t err;
666   char *hostport = NULL;
667
668   hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port, 1, NULL);
669   if (!hostport)
670     {
671       err = gpg_error_from_syserror ();
672       err = ks_printf_help (ctrl, "%s://%s:%hu: resolve failed: %s",
673                             uri->scheme, uri->host, uri->port,
674                             gpg_strerror (err));
675     }
676   else
677     {
678       err = ks_printf_help (ctrl, "%s", hostport);
679       xfree (hostport);
680     }
681   return err;
682 }
683
684
685 /* Send an HTTP request.  On success returns an estream object at
686    R_FP.  HOSTPORTSTR is only used for diagnostics.  If POST_CB is not
687    NULL a post request is used and that callback is called to allow
688    writing the post data.  */
689 static gpg_error_t
690 send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
691               unsigned int httpflags,
692               gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value,
693               estream_t *r_fp)
694 {
695   gpg_error_t err;
696   http_t http = NULL;
697   int redirects_left = MAX_REDIRECTS;
698   estream_t fp = NULL;
699   char *request_buffer = NULL;
700
701   *r_fp = NULL;
702
703  once_more:
704   err = http_open (&http,
705                    post_cb? HTTP_REQ_POST : HTTP_REQ_GET,
706                    request,
707                    /* fixme: AUTH */ NULL,
708                    httpflags,
709                    /* fixme: proxy*/ NULL,
710                    NULL, NULL,
711                    /*FIXME curl->srvtag*/NULL);
712   if (!err)
713     {
714       fp = http_get_write_ptr (http);
715       /* Avoid caches to get the most recent copy of the key.  We set
716          both the Pragma and Cache-Control versions of the header, so
717          we're good with both HTTP 1.0 and 1.1.  */
718       es_fputs ("Pragma: no-cache\r\n"
719                 "Cache-Control: no-cache\r\n", fp);
720       if (post_cb)
721         err = post_cb (post_cb_value, http);
722       if (!err)
723         {
724           http_start_data (http);
725           if (es_ferror (fp))
726             err = gpg_error_from_syserror ();
727         }
728     }
729   if (err)
730     {
731       /* Fixme: After a redirection we show the old host name.  */
732       log_error (_("error connecting to '%s': %s\n"),
733                  hostportstr, gpg_strerror (err));
734       goto leave;
735     }
736
737   /* Wait for the response.  */
738   dirmngr_tick (ctrl);
739   err = http_wait_response (http);
740   if (err)
741     {
742       log_error (_("error reading HTTP response for '%s': %s\n"),
743                  hostportstr, gpg_strerror (err));
744       goto leave;
745     }
746
747   switch (http_get_status_code (http))
748     {
749     case 200:
750       err = 0;
751       break; /* Success.  */
752
753     case 301:
754     case 302:
755       {
756         const char *s = http_get_header (http, "Location");
757
758         log_info (_("URL '%s' redirected to '%s' (%u)\n"),
759                   request, s?s:"[none]", http_get_status_code (http));
760         if (s && *s && redirects_left-- )
761           {
762             xfree (request_buffer);
763             request_buffer = xtrystrdup (s);
764             if (request_buffer)
765               {
766                 request = request_buffer;
767                 http_close (http, 0);
768                 http = NULL;
769                 goto once_more;
770               }
771             err = gpg_error_from_syserror ();
772           }
773         else
774           err = gpg_error (GPG_ERR_NO_DATA);
775         log_error (_("too many redirections\n"));
776       }
777       goto leave;
778
779     default:
780       log_error (_("error accessing '%s': http status %u\n"),
781                  request, http_get_status_code (http));
782       err = gpg_error (GPG_ERR_NO_DATA);
783       goto leave;
784     }
785
786   fp = http_get_read_ptr (http);
787   if (!fp)
788     {
789       err = gpg_error (GPG_ERR_BUG);
790       goto leave;
791     }
792
793   /* Return the read stream and close the HTTP context.  */
794   *r_fp = fp;
795   http_close (http, 1);
796   http = NULL;
797
798  leave:
799   http_close (http, 0);
800   xfree (request_buffer);
801   return err;
802 }
803
804
805 /* Helper to evaluate the error code ERR form a send_request() call
806    with REQUEST.  The function returns true if the caller shall try
807    again.  TRIES_LEFT points to a variable to track the number of
808    retries; this function decrements it and won't return true if it is
809    down to zero. */
810 static int
811 handle_send_request_error (gpg_error_t err, const char *request,
812                            unsigned int *tries_left)
813 {
814   int retry = 0;
815
816   switch (gpg_err_code (err))
817     {
818     case GPG_ERR_ECONNREFUSED:
819     case GPG_ERR_ENETUNREACH:
820       if (mark_host_dead (request) && *tries_left)
821         retry = 1;
822       break;
823
824     case GPG_ERR_ETIMEDOUT:
825       if (*tries_left)
826         {
827           log_info ("selecting a different host due to a timeout\n");
828           retry = 1;
829         }
830
831     default:
832       break;
833     }
834
835   if (*tries_left)
836     --*tries_left;
837
838   return retry;
839 }
840
841 static gpg_error_t
842 armor_data (char **r_string, const void *data, size_t datalen)
843 {
844   gpg_error_t err;
845   struct b64state b64state;
846   estream_t fp;
847   long length;
848   char *buffer;
849   size_t nread;
850
851   *r_string = NULL;
852
853   fp = es_fopenmem (0, "rw");
854   if (!fp)
855     return gpg_error_from_syserror ();
856
857   if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK"))
858       || (err=b64enc_write (&b64state, data, datalen))
859       || (err = b64enc_finish (&b64state)))
860     {
861       es_fclose (fp);
862       return err;
863     }
864
865   /* FIXME: To avoid the extra buffer allocation estream should
866      provide a function to snatch the internal allocated memory from
867      such a memory stream.  */
868   length = es_ftell (fp);
869   if (length < 0)
870     {
871       err = gpg_error_from_syserror ();
872       es_fclose (fp);
873       return err;
874     }
875
876   buffer = xtrymalloc (length+1);
877   if (!buffer)
878     {
879       err = gpg_error_from_syserror ();
880       es_fclose (fp);
881       return err;
882     }
883
884   es_rewind (fp);
885   if (es_read (fp, buffer, length, &nread))
886     {
887       err = gpg_error_from_syserror ();
888       es_fclose (fp);
889       return err;
890     }
891   buffer[nread] = 0;
892   es_fclose (fp);
893
894   *r_string = buffer;
895   return 0;
896 }
897
898
899 \f
900 /* Search the keyserver identified by URI for keys matching PATTERN.
901    On success R_FP has an open stream to read the data.  */
902 gpg_error_t
903 ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
904                estream_t *r_fp)
905 {
906   gpg_error_t err;
907   KEYDB_SEARCH_DESC desc;
908   char fprbuf[2+40+1];
909   char *hostport = NULL;
910   char *request = NULL;
911   estream_t fp = NULL;
912   int reselect;
913   unsigned int httpflags;
914   unsigned int tries = SEND_REQUEST_RETRIES;
915
916   *r_fp = NULL;
917
918   /* Remove search type indicator and adjust PATTERN accordingly.
919      Note that HKP keyservers like the 0x to be present when searching
920      by keyid.  We need to re-format the fingerprint and keyids so to
921      remove the gpg specific force-use-of-this-key flag ("!").  */
922   err = classify_user_id (pattern, &desc, 1);
923   if (err)
924     return err;
925   switch (desc.mode)
926     {
927     case KEYDB_SEARCH_MODE_EXACT:
928     case KEYDB_SEARCH_MODE_SUBSTR:
929     case KEYDB_SEARCH_MODE_MAIL:
930     case KEYDB_SEARCH_MODE_MAILSUB:
931       pattern = desc.u.name;
932       break;
933     case KEYDB_SEARCH_MODE_SHORT_KID:
934       snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]);
935       pattern = fprbuf;
936       break;
937     case KEYDB_SEARCH_MODE_LONG_KID:
938       snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX",
939                 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
940       pattern = fprbuf;
941       break;
942     case KEYDB_SEARCH_MODE_FPR16:
943       bin2hex (desc.u.fpr, 16, fprbuf);
944       pattern = fprbuf;
945       break;
946     case KEYDB_SEARCH_MODE_FPR20:
947     case KEYDB_SEARCH_MODE_FPR:
948       bin2hex (desc.u.fpr, 20, fprbuf);
949       pattern = fprbuf;
950       break;
951     default:
952       return gpg_error (GPG_ERR_INV_USER_ID);
953     }
954
955   /* Build the request string.  */
956   reselect = 0;
957  again:
958   {
959     char *searchkey;
960
961     xfree (hostport);
962     hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
963                                reselect, &httpflags);
964     if (!hostport)
965       {
966         err = gpg_error_from_syserror ();
967         goto leave;
968       }
969
970     searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS);
971     if (!searchkey)
972       {
973         err = gpg_error_from_syserror ();
974         goto leave;
975       }
976
977     xfree (request);
978     request = strconcat (hostport,
979                          "/pks/lookup?op=index&options=mr&search=",
980                          searchkey,
981                          NULL);
982     xfree (searchkey);
983     if (!request)
984       {
985         err = gpg_error_from_syserror ();
986         goto leave;
987       }
988   }
989
990   /* Send the request.  */
991   err = send_request (ctrl, request, hostport, httpflags, NULL, NULL, &fp);
992   if (handle_send_request_error (err, request, &tries))
993     {
994       reselect = 1;
995       goto again;
996     }
997   if (err)
998     goto leave;
999
1000   err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1001   if (err)
1002     goto leave;
1003
1004   /* Peek at the response.  */
1005   {
1006     int c = es_getc (fp);
1007     if (c == -1)
1008       {
1009         err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF);
1010         log_error ("error reading response: %s\n", gpg_strerror (err));
1011         goto leave;
1012       }
1013     if (c == '<')
1014       {
1015         /* The document begins with a '<': Assume a HTML response,
1016            which we don't support.  */
1017         err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
1018         goto leave;
1019       }
1020     es_ungetc (c, fp);
1021   }
1022
1023   /* Return the read stream.  */
1024   *r_fp = fp;
1025   fp = NULL;
1026
1027  leave:
1028   es_fclose (fp);
1029   xfree (request);
1030   xfree (hostport);
1031   return err;
1032 }
1033
1034
1035 /* Get the key described key the KEYSPEC string from the keyserver
1036    identified by URI.  On success R_FP has an open stream to read the
1037    data.  */
1038 gpg_error_t
1039 ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
1040 {
1041   gpg_error_t err;
1042   KEYDB_SEARCH_DESC desc;
1043   char kidbuf[40+1];
1044   char *hostport = NULL;
1045   char *request = NULL;
1046   estream_t fp = NULL;
1047   int reselect;
1048   unsigned int httpflags;
1049   unsigned int tries = SEND_REQUEST_RETRIES;
1050
1051   *r_fp = NULL;
1052
1053   /* Remove search type indicator and adjust PATTERN accordingly.
1054      Note that HKP keyservers like the 0x to be present when searching
1055      by keyid.  We need to re-format the fingerprint and keyids so to
1056      remove the gpg specific force-use-of-this-key flag ("!").  */
1057   err = classify_user_id (keyspec, &desc, 1);
1058   if (err)
1059     return err;
1060   switch (desc.mode)
1061     {
1062     case KEYDB_SEARCH_MODE_SHORT_KID:
1063       snprintf (kidbuf, sizeof kidbuf, "%08lX", (ulong)desc.u.kid[1]);
1064       break;
1065     case KEYDB_SEARCH_MODE_LONG_KID:
1066       snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX",
1067                 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1068       break;
1069     case KEYDB_SEARCH_MODE_FPR20:
1070     case KEYDB_SEARCH_MODE_FPR:
1071       /* This is a v4 fingerprint. */
1072       bin2hex (desc.u.fpr, 20, kidbuf);
1073       break;
1074
1075     case KEYDB_SEARCH_MODE_FPR16:
1076       log_error ("HKP keyservers do not support v3 fingerprints\n");
1077     default:
1078       return gpg_error (GPG_ERR_INV_USER_ID);
1079     }
1080
1081   reselect = 0;
1082  again:
1083   /* Build the request string.  */
1084   xfree (hostport);
1085   hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
1086                              reselect, &httpflags);
1087   if (!hostport)
1088     {
1089       err = gpg_error_from_syserror ();
1090       goto leave;
1091     }
1092
1093   xfree (request);
1094   request = strconcat (hostport,
1095                        "/pks/lookup?op=get&options=mr&search=0x",
1096                        kidbuf,
1097                        NULL);
1098   if (!request)
1099     {
1100       err = gpg_error_from_syserror ();
1101       goto leave;
1102     }
1103
1104   /* Send the request.  */
1105   err = send_request (ctrl, request, hostport, httpflags, NULL, NULL, &fp);
1106   if (handle_send_request_error (err, request, &tries))
1107     {
1108       reselect = 1;
1109       goto again;
1110     }
1111   if (err)
1112     goto leave;
1113
1114   err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1115   if (err)
1116     goto leave;
1117
1118   /* Return the read stream and close the HTTP context.  */
1119   *r_fp = fp;
1120   fp = NULL;
1121
1122  leave:
1123   es_fclose (fp);
1124   xfree (request);
1125   xfree (hostport);
1126   return err;
1127 }
1128
1129
1130
1131 \f
1132 /* Callback parameters for put_post_cb.  */
1133 struct put_post_parm_s
1134 {
1135   char *datastring;
1136 };
1137
1138
1139 /* Helper for ks_hkp_put.  */
1140 static gpg_error_t
1141 put_post_cb (void *opaque, http_t http)
1142 {
1143   struct put_post_parm_s *parm = opaque;
1144   gpg_error_t err = 0;
1145   estream_t fp;
1146   size_t len;
1147
1148   fp = http_get_write_ptr (http);
1149   len = strlen (parm->datastring);
1150
1151   es_fprintf (fp,
1152               "Content-Type: application/x-www-form-urlencoded\r\n"
1153               "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */);
1154   http_start_data (http);
1155   if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL))
1156     err = gpg_error_from_syserror ();
1157   return err;
1158 }
1159
1160
1161 /* Send the key in {DATA,DATALEN} to the keyserver identified by  URI.  */
1162 gpg_error_t
1163 ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
1164 {
1165   gpg_error_t err;
1166   char *hostport = NULL;
1167   char *request = NULL;
1168   estream_t fp = NULL;
1169   struct put_post_parm_s parm;
1170   char *armored = NULL;
1171   int reselect;
1172   unsigned int httpflags;
1173   unsigned int tries = SEND_REQUEST_RETRIES;
1174
1175   parm.datastring = NULL;
1176
1177   err = armor_data (&armored, data, datalen);
1178   if (err)
1179     goto leave;
1180
1181   parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
1182   if (!parm.datastring)
1183     {
1184       err = gpg_error_from_syserror ();
1185       goto leave;
1186     }
1187   xfree (armored);
1188   armored = NULL;
1189
1190   /* Build the request string.  */
1191   reselect = 0;
1192  again:
1193   xfree (hostport);
1194   hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
1195                              reselect, &httpflags);
1196   if (!hostport)
1197     {
1198       err = gpg_error_from_syserror ();
1199       goto leave;
1200     }
1201
1202   xfree (request);
1203   request = strconcat (hostport, "/pks/add", NULL);
1204   if (!request)
1205     {
1206       err = gpg_error_from_syserror ();
1207       goto leave;
1208     }
1209
1210   /* Send the request.  */
1211   err = send_request (ctrl, request, hostport, 0, put_post_cb, &parm, &fp);
1212   if (handle_send_request_error (err, request, &tries))
1213     {
1214       reselect = 1;
1215       goto again;
1216     }
1217   if (err)
1218     goto leave;
1219
1220  leave:
1221   es_fclose (fp);
1222   xfree (parm.datastring);
1223   xfree (armored);
1224   xfree (request);
1225   xfree (hostport);
1226   return err;
1227 }