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