gpg: Make --auto-key-locate work again with keyservers.
[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[2+40+1];
1044   const char *exactname = NULL;
1045   char *searchkey = NULL;
1046   char *hostport = NULL;
1047   char *request = NULL;
1048   estream_t fp = NULL;
1049   int reselect;
1050   unsigned int httpflags;
1051   unsigned int tries = SEND_REQUEST_RETRIES;
1052
1053   *r_fp = NULL;
1054
1055   /* Remove search type indicator and adjust PATTERN accordingly.
1056      Note that HKP keyservers like the 0x to be present when searching
1057      by keyid.  We need to re-format the fingerprint and keyids so to
1058      remove the gpg specific force-use-of-this-key flag ("!").  */
1059   err = classify_user_id (keyspec, &desc, 1);
1060   if (err)
1061     return err;
1062   switch (desc.mode)
1063     {
1064     case KEYDB_SEARCH_MODE_SHORT_KID:
1065       snprintf (kidbuf, sizeof kidbuf, "0x%08lX", (ulong)desc.u.kid[1]);
1066       break;
1067     case KEYDB_SEARCH_MODE_LONG_KID:
1068       snprintf (kidbuf, sizeof kidbuf, "0x%08lX%08lX",
1069                 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1070       break;
1071     case KEYDB_SEARCH_MODE_FPR20:
1072     case KEYDB_SEARCH_MODE_FPR:
1073       /* This is a v4 fingerprint. */
1074       kidbuf[0] = '0';
1075       kidbuf[1] = 'x';
1076       bin2hex (desc.u.fpr, 20, kidbuf+2);
1077       break;
1078
1079     case KEYDB_SEARCH_MODE_EXACT:
1080       exactname = desc.u.name;
1081       break;
1082
1083     case KEYDB_SEARCH_MODE_FPR16:
1084       log_error ("HKP keyservers do not support v3 fingerprints\n");
1085     default:
1086       return gpg_error (GPG_ERR_INV_USER_ID);
1087     }
1088
1089   searchkey = http_escape_string (exactname? exactname : kidbuf,
1090                                   EXTRA_ESCAPE_CHARS);
1091   if (!searchkey)
1092     {
1093       err = gpg_error_from_syserror ();
1094       goto leave;
1095     }
1096
1097   reselect = 0;
1098  again:
1099   /* Build the request string.  */
1100   xfree (hostport);
1101   hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
1102                              reselect, &httpflags);
1103   if (!hostport)
1104     {
1105       err = gpg_error_from_syserror ();
1106       goto leave;
1107     }
1108
1109   xfree (request);
1110   request = strconcat (hostport,
1111                        "/pks/lookup?op=get&options=mr&search=",
1112                        searchkey,
1113                        exactname? "&exact=on":"",
1114                        NULL);
1115   if (!request)
1116     {
1117       err = gpg_error_from_syserror ();
1118       goto leave;
1119     }
1120
1121   /* Send the request.  */
1122   err = send_request (ctrl, request, hostport, httpflags, NULL, NULL, &fp);
1123   if (handle_send_request_error (err, request, &tries))
1124     {
1125       reselect = 1;
1126       goto again;
1127     }
1128   if (err)
1129     goto leave;
1130
1131   err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1132   if (err)
1133     goto leave;
1134
1135   /* Return the read stream and close the HTTP context.  */
1136   *r_fp = fp;
1137   fp = NULL;
1138
1139  leave:
1140   es_fclose (fp);
1141   xfree (request);
1142   xfree (hostport);
1143   xfree (searchkey);
1144   return err;
1145 }
1146
1147
1148
1149 \f
1150 /* Callback parameters for put_post_cb.  */
1151 struct put_post_parm_s
1152 {
1153   char *datastring;
1154 };
1155
1156
1157 /* Helper for ks_hkp_put.  */
1158 static gpg_error_t
1159 put_post_cb (void *opaque, http_t http)
1160 {
1161   struct put_post_parm_s *parm = opaque;
1162   gpg_error_t err = 0;
1163   estream_t fp;
1164   size_t len;
1165
1166   fp = http_get_write_ptr (http);
1167   len = strlen (parm->datastring);
1168
1169   es_fprintf (fp,
1170               "Content-Type: application/x-www-form-urlencoded\r\n"
1171               "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */);
1172   http_start_data (http);
1173   if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL))
1174     err = gpg_error_from_syserror ();
1175   return err;
1176 }
1177
1178
1179 /* Send the key in {DATA,DATALEN} to the keyserver identified by  URI.  */
1180 gpg_error_t
1181 ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
1182 {
1183   gpg_error_t err;
1184   char *hostport = NULL;
1185   char *request = NULL;
1186   estream_t fp = NULL;
1187   struct put_post_parm_s parm;
1188   char *armored = NULL;
1189   int reselect;
1190   unsigned int httpflags;
1191   unsigned int tries = SEND_REQUEST_RETRIES;
1192
1193   parm.datastring = NULL;
1194
1195   err = armor_data (&armored, data, datalen);
1196   if (err)
1197     goto leave;
1198
1199   parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
1200   if (!parm.datastring)
1201     {
1202       err = gpg_error_from_syserror ();
1203       goto leave;
1204     }
1205   xfree (armored);
1206   armored = NULL;
1207
1208   /* Build the request string.  */
1209   reselect = 0;
1210  again:
1211   xfree (hostport);
1212   hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
1213                              reselect, &httpflags);
1214   if (!hostport)
1215     {
1216       err = gpg_error_from_syserror ();
1217       goto leave;
1218     }
1219
1220   xfree (request);
1221   request = strconcat (hostport, "/pks/add", NULL);
1222   if (!request)
1223     {
1224       err = gpg_error_from_syserror ();
1225       goto leave;
1226     }
1227
1228   /* Send the request.  */
1229   err = send_request (ctrl, request, hostport, 0, put_post_cb, &parm, &fp);
1230   if (handle_send_request_error (err, request, &tries))
1231     {
1232       reselect = 1;
1233       goto again;
1234     }
1235   if (err)
1236     goto leave;
1237
1238  leave:
1239   es_fclose (fp);
1240   xfree (parm.datastring);
1241   xfree (armored);
1242   xfree (request);
1243   xfree (hostport);
1244   return err;
1245 }