gpg: Print the actual used keyserver address.
[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.  */
458 gpg_error_t
459 ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive)
460 {
461   gpg_error_t err = 0;
462   hostinfo_t hi, hi2;
463   int idx, idx2, idx3, n;
464
465   if (!name || !*name || !strcmp (name, "localhost"))
466     return 0;
467
468   idx = find_hostinfo (name);
469   if (idx == -1)
470     return gpg_error (GPG_ERR_NOT_FOUND);
471
472   hi = hosttable[idx];
473   if (alive && hi->dead)
474     {
475       hi->dead = 0;
476       err = ks_printf_help (ctrl, "marking '%s' as alive", name);
477     }
478   else if (!alive && !hi->dead)
479     {
480       hi->dead = 1;
481       err = ks_printf_help (ctrl, "marking '%s' as dead", name);
482     }
483
484   /* If the host is a pool mark all member hosts. */
485   if (!err && hi->pool)
486     {
487       for (idx2=0; !err && (n=hi->pool[idx2]) != -1; idx2++)
488         {
489           assert (n >= 0 && n < hosttable_size);
490
491           if (!alive)
492             {
493               /* Do not mark a host from a pool dead if it is also a
494                  member in another pool.  */
495               for (idx3=0; idx3 < hosttable_size; idx3++)
496                 {
497                   if (hosttable[idx3] && hosttable[idx3]
498                       && hosttable[idx3]->pool
499                       && idx3 != idx
500                       && host_in_pool_p (hosttable[idx3]->pool, n))
501                     break;
502                 }
503               if (idx3 < hosttable_size)
504                 continue;  /* Host is also a member of another pool.  */
505             }
506
507           hi2 = hosttable[n];
508           if (!hi2)
509             ;
510           else if (alive && hi2->dead)
511             {
512               hi2->dead = 0;
513               err = ks_printf_help (ctrl, "marking '%s' as alive",
514                                     hi2->name);
515             }
516           else if (!alive && !hi2->dead)
517             {
518               hi2->dead = 1;
519               err = ks_printf_help (ctrl, "marking '%s' as dead",
520                                     hi2->name);
521             }
522         }
523     }
524
525   return err;
526 }
527
528
529 /* Debug function to print the entire hosttable.  */
530 gpg_error_t
531 ks_hkp_print_hosttable (ctrl_t ctrl)
532 {
533   gpg_error_t err;
534   int idx, idx2;
535   hostinfo_t hi;
536   membuf_t mb;
537   char *p;
538
539   err = ks_print_help (ctrl, "hosttable (idx, ipv4, ipv6, dead, name):");
540   if (err)
541     return err;
542
543   for (idx=0; idx < hosttable_size; idx++)
544     if ((hi=hosttable[idx]))
545       {
546         err = ks_printf_help (ctrl, "%3d %s %s %s %s\n",
547                               idx, hi->v4? "4":" ", hi->v6? "6":" ",
548                               hi->dead? "d":" ", hi->name);
549         if (err)
550           return err;
551         if (hi->pool)
552           {
553             init_membuf (&mb, 256);
554             put_membuf_printf (&mb, "  .   -->");
555             for (idx2=0; hi->pool[idx2] != -1; idx2++)
556               {
557                 put_membuf_printf (&mb, " %d", hi->pool[idx2]);
558                 if (hi->poolidx == hi->pool[idx2])
559                   put_membuf_printf (&mb, "*");
560               }
561             put_membuf( &mb, "", 1);
562             p = get_membuf (&mb, NULL);
563             if (!p)
564               return gpg_error_from_syserror ();
565             err = ks_print_help (ctrl, p);
566             xfree (p);
567             if (err)
568               return err;
569           }
570       }
571   return 0;
572 }
573
574
575
576 /* Print a help output for the schemata supported by this module. */
577 gpg_error_t
578 ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri)
579 {
580   const char const data[] =
581     "Handler for HKP URLs:\n"
582     "  hkp://\n"
583     "Supported methods: search, get, put\n";
584   gpg_error_t err;
585
586   if (!uri)
587     err = ks_print_help (ctrl, "  hkp");
588   else if (uri->is_http && !strcmp (uri->scheme, "hkp"))
589     err = ks_print_help (ctrl, data);
590   else
591     err = 0;
592
593   return err;
594 }
595
596
597 /* Build the remote part or the URL from SCHEME, HOST and an optional
598    PORT.  Returns an allocated string or NULL on failure and sets
599    ERRNO.  */
600 static char *
601 make_host_part (ctrl_t ctrl,
602                 const char *scheme, const char *host, unsigned short port,
603                 int force_reselect)
604 {
605   char portstr[10];
606   char *hostname;
607   char *hostport;
608
609   /* Map scheme and port.  */
610   if (!strcmp (scheme, "hkps") || !strcmp (scheme,"https"))
611     {
612       scheme = "https";
613       strcpy (portstr, "443");
614     }
615   else /* HKP or HTTP.  */
616     {
617       scheme = "http";
618       strcpy (portstr, "11371");
619     }
620   if (port)
621     snprintf (portstr, sizeof portstr, "%hu", port);
622   else
623     {
624       /*fixme_do_srv_lookup ()*/
625     }
626
627   hostname = map_host (ctrl, host, force_reselect);
628   if (!hostname)
629     return NULL;
630
631   hostport = strconcat (scheme, "://", hostname, ":", portstr, NULL);
632   xfree (hostname);
633   return hostport;
634 }
635
636
637 /* Resolve all known keyserver names and update the hosttable.  This
638    is mainly useful for debugging because the resolving is anyway done
639    on demand.  */
640 gpg_error_t
641 ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri)
642 {
643   gpg_error_t err;
644   char *hostport = NULL;
645
646   hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port, 1);
647   if (!hostport)
648     {
649       err = gpg_error_from_syserror ();
650       err = ks_printf_help (ctrl, "%s://%s:%hu: resolve failed: %s",
651                             uri->scheme, uri->host, uri->port,
652                             gpg_strerror (err));
653     }
654   else
655     {
656       err = ks_printf_help (ctrl, "%s", hostport);
657       xfree (hostport);
658     }
659   return err;
660 }
661
662
663 /* Send an HTTP request.  On success returns an estream object at
664    R_FP.  HOSTPORTSTR is only used for diagnostics.  If POST_CB is not
665    NULL a post request is used and that callback is called to allow
666    writing the post data.  */
667 static gpg_error_t
668 send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
669               gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value,
670               estream_t *r_fp)
671 {
672   gpg_error_t err;
673   http_t http = NULL;
674   int redirects_left = MAX_REDIRECTS;
675   estream_t fp = NULL;
676   char *request_buffer = NULL;
677
678   *r_fp = NULL;
679
680  once_more:
681   err = http_open (&http,
682                    post_cb? HTTP_REQ_POST : HTTP_REQ_GET,
683                    request,
684                    /* fixme: AUTH */ NULL,
685                    0,
686                    /* fixme: proxy*/ NULL,
687                    NULL, NULL,
688                    /*FIXME curl->srvtag*/NULL);
689   if (!err)
690     {
691       fp = http_get_write_ptr (http);
692       /* Avoid caches to get the most recent copy of the key.  We set
693          both the Pragma and Cache-Control versions of the header, so
694          we're good with both HTTP 1.0 and 1.1.  */
695       es_fputs ("Pragma: no-cache\r\n"
696                 "Cache-Control: no-cache\r\n", fp);
697       if (post_cb)
698         err = post_cb (post_cb_value, http);
699       if (!err)
700         {
701           http_start_data (http);
702           if (es_ferror (fp))
703             err = gpg_error_from_syserror ();
704         }
705     }
706   if (err)
707     {
708       /* Fixme: After a redirection we show the old host name.  */
709       log_error (_("error connecting to '%s': %s\n"),
710                  hostportstr, gpg_strerror (err));
711       goto leave;
712     }
713
714   /* Wait for the response.  */
715   dirmngr_tick (ctrl);
716   err = http_wait_response (http);
717   if (err)
718     {
719       log_error (_("error reading HTTP response for '%s': %s\n"),
720                  hostportstr, gpg_strerror (err));
721       goto leave;
722     }
723
724   switch (http_get_status_code (http))
725     {
726     case 200:
727       err = 0;
728       break; /* Success.  */
729
730     case 301:
731     case 302:
732       {
733         const char *s = http_get_header (http, "Location");
734
735         log_info (_("URL '%s' redirected to '%s' (%u)\n"),
736                   request, s?s:"[none]", http_get_status_code (http));
737         if (s && *s && redirects_left-- )
738           {
739             xfree (request_buffer);
740             request_buffer = xtrystrdup (s);
741             if (request_buffer)
742               {
743                 request = request_buffer;
744                 http_close (http, 0);
745                 http = NULL;
746                 goto once_more;
747               }
748             err = gpg_error_from_syserror ();
749           }
750         else
751           err = gpg_error (GPG_ERR_NO_DATA);
752         log_error (_("too many redirections\n"));
753       }
754       goto leave;
755
756     default:
757       log_error (_("error accessing '%s': http status %u\n"),
758                  request, http_get_status_code (http));
759       err = gpg_error (GPG_ERR_NO_DATA);
760       goto leave;
761     }
762
763   fp = http_get_read_ptr (http);
764   if (!fp)
765     {
766       err = gpg_error (GPG_ERR_BUG);
767       goto leave;
768     }
769
770   /* Return the read stream and close the HTTP context.  */
771   *r_fp = fp;
772   http_close (http, 1);
773   http = NULL;
774
775  leave:
776   http_close (http, 0);
777   xfree (request_buffer);
778   return err;
779 }
780
781
782 /* Helper to evaluate the error code ERR form a send_request() call
783    with REQUEST.  The function returns true if the caller shall try
784    again.  TRIES_LEFT points to a variable to track the number of
785    retries; this function decrements it and won't return true if it is
786    down to zero. */
787 static int
788 handle_send_request_error (gpg_error_t err, const char *request,
789                            unsigned int *tries_left)
790 {
791   int retry = 0;
792
793   switch (gpg_err_code (err))
794     {
795     case GPG_ERR_ECONNREFUSED:
796     case GPG_ERR_ENETUNREACH:
797       if (mark_host_dead (request) && *tries_left)
798         retry = 1;
799       break;
800
801     case GPG_ERR_ETIMEDOUT:
802       if (*tries_left)
803         {
804           log_info ("selecting a different host due to a timeout\n");
805           retry = 1;
806         }
807
808     default:
809       break;
810     }
811
812   if (*tries_left)
813     --*tries_left;
814
815   return retry;
816 }
817
818 static gpg_error_t
819 armor_data (char **r_string, const void *data, size_t datalen)
820 {
821   gpg_error_t err;
822   struct b64state b64state;
823   estream_t fp;
824   long length;
825   char *buffer;
826   size_t nread;
827
828   *r_string = NULL;
829
830   fp = es_fopenmem (0, "rw");
831   if (!fp)
832     return gpg_error_from_syserror ();
833
834   if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK"))
835       || (err=b64enc_write (&b64state, data, datalen))
836       || (err = b64enc_finish (&b64state)))
837     {
838       es_fclose (fp);
839       return err;
840     }
841
842   /* FIXME: To avoid the extra buffer allocation estream should
843      provide a function to snatch the internal allocated memory from
844      such a memory stream.  */
845   length = es_ftell (fp);
846   if (length < 0)
847     {
848       err = gpg_error_from_syserror ();
849       es_fclose (fp);
850       return err;
851     }
852
853   buffer = xtrymalloc (length+1);
854   if (!buffer)
855     {
856       err = gpg_error_from_syserror ();
857       es_fclose (fp);
858       return err;
859     }
860
861   es_rewind (fp);
862   if (es_read (fp, buffer, length, &nread))
863     {
864       err = gpg_error_from_syserror ();
865       es_fclose (fp);
866       return err;
867     }
868   buffer[nread] = 0;
869   es_fclose (fp);
870
871   *r_string = buffer;
872   return 0;
873 }
874
875
876 \f
877 /* Search the keyserver identified by URI for keys matching PATTERN.
878    On success R_FP has an open stream to read the data.  */
879 gpg_error_t
880 ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
881                estream_t *r_fp)
882 {
883   gpg_error_t err;
884   KEYDB_SEARCH_DESC desc;
885   char fprbuf[2+40+1];
886   char *hostport = NULL;
887   char *request = NULL;
888   estream_t fp = NULL;
889   int reselect;
890   unsigned int tries = SEND_REQUEST_RETRIES;
891
892   *r_fp = NULL;
893
894   /* Remove search type indicator and adjust PATTERN accordingly.
895      Note that HKP keyservers like the 0x to be present when searching
896      by keyid.  We need to re-format the fingerprint and keyids so to
897      remove the gpg specific force-use-of-this-key flag ("!").  */
898   err = classify_user_id (pattern, &desc, 1);
899   if (err)
900     return err;
901   switch (desc.mode)
902     {
903     case KEYDB_SEARCH_MODE_EXACT:
904     case KEYDB_SEARCH_MODE_SUBSTR:
905     case KEYDB_SEARCH_MODE_MAIL:
906     case KEYDB_SEARCH_MODE_MAILSUB:
907       pattern = desc.u.name;
908       break;
909     case KEYDB_SEARCH_MODE_SHORT_KID:
910       snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]);
911       pattern = fprbuf;
912       break;
913     case KEYDB_SEARCH_MODE_LONG_KID:
914       snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX",
915                 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
916       pattern = fprbuf;
917       break;
918     case KEYDB_SEARCH_MODE_FPR16:
919       bin2hex (desc.u.fpr, 16, fprbuf);
920       pattern = fprbuf;
921       break;
922     case KEYDB_SEARCH_MODE_FPR20:
923     case KEYDB_SEARCH_MODE_FPR:
924       bin2hex (desc.u.fpr, 20, fprbuf);
925       pattern = fprbuf;
926       break;
927     default:
928       return gpg_error (GPG_ERR_INV_USER_ID);
929     }
930
931   /* Build the request string.  */
932   reselect = 0;
933  again:
934   {
935     char *searchkey;
936
937     xfree (hostport);
938     hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
939                                reselect);
940     if (!hostport)
941       {
942         err = gpg_error_from_syserror ();
943         goto leave;
944       }
945
946     searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS);
947     if (!searchkey)
948       {
949         err = gpg_error_from_syserror ();
950         goto leave;
951       }
952
953     xfree (request);
954     request = strconcat (hostport,
955                          "/pks/lookup?op=index&options=mr&search=",
956                          searchkey,
957                          NULL);
958     xfree (searchkey);
959     if (!request)
960       {
961         err = gpg_error_from_syserror ();
962         goto leave;
963       }
964   }
965
966   /* Send the request.  */
967   err = send_request (ctrl, request, hostport, NULL, NULL, &fp);
968   if (handle_send_request_error (err, request, &tries))
969     {
970       reselect = 1;
971       goto again;
972     }
973   if (err)
974     goto leave;
975
976   err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
977   if (err)
978     goto leave;
979
980   /* Peek at the response.  */
981   {
982     int c = es_getc (fp);
983     if (c == -1)
984       {
985         err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF);
986         log_error ("error reading response: %s\n", gpg_strerror (err));
987         goto leave;
988       }
989     if (c == '<')
990       {
991         /* The document begins with a '<': Assume a HTML response,
992            which we don't support.  */
993         err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
994         goto leave;
995       }
996     es_ungetc (c, fp);
997   }
998
999   /* Return the read stream.  */
1000   *r_fp = fp;
1001   fp = NULL;
1002
1003  leave:
1004   es_fclose (fp);
1005   xfree (request);
1006   xfree (hostport);
1007   return err;
1008 }
1009
1010
1011 /* Get the key described key the KEYSPEC string from the keyserver
1012    identified by URI.  On success R_FP has an open stream to read the
1013    data.  */
1014 gpg_error_t
1015 ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
1016 {
1017   gpg_error_t err;
1018   KEYDB_SEARCH_DESC desc;
1019   char kidbuf[40+1];
1020   char *hostport = NULL;
1021   char *request = NULL;
1022   estream_t fp = NULL;
1023   int reselect;
1024   unsigned int tries = SEND_REQUEST_RETRIES;
1025
1026   *r_fp = NULL;
1027
1028   /* Remove search type indicator and adjust PATTERN accordingly.
1029      Note that HKP keyservers like the 0x to be present when searching
1030      by keyid.  We need to re-format the fingerprint and keyids so to
1031      remove the gpg specific force-use-of-this-key flag ("!").  */
1032   err = classify_user_id (keyspec, &desc, 1);
1033   if (err)
1034     return err;
1035   switch (desc.mode)
1036     {
1037     case KEYDB_SEARCH_MODE_SHORT_KID:
1038       snprintf (kidbuf, sizeof kidbuf, "%08lX", (ulong)desc.u.kid[1]);
1039       break;
1040     case KEYDB_SEARCH_MODE_LONG_KID:
1041       snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX",
1042                 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1043       break;
1044     case KEYDB_SEARCH_MODE_FPR20:
1045     case KEYDB_SEARCH_MODE_FPR:
1046       /* This is a v4 fingerprint. */
1047       bin2hex (desc.u.fpr, 20, kidbuf);
1048       break;
1049
1050     case KEYDB_SEARCH_MODE_FPR16:
1051       log_error ("HKP keyservers do not support v3 fingerprints\n");
1052     default:
1053       return gpg_error (GPG_ERR_INV_USER_ID);
1054     }
1055
1056   reselect = 0;
1057  again:
1058   /* Build the request string.  */
1059   xfree (hostport);
1060   hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect);
1061   if (!hostport)
1062     {
1063       err = gpg_error_from_syserror ();
1064       goto leave;
1065     }
1066
1067   xfree (request);
1068   request = strconcat (hostport,
1069                        "/pks/lookup?op=get&options=mr&search=0x",
1070                        kidbuf,
1071                        NULL);
1072   if (!request)
1073     {
1074       err = gpg_error_from_syserror ();
1075       goto leave;
1076     }
1077
1078   /* Send the request.  */
1079   err = send_request (ctrl, request, hostport, NULL, NULL, &fp);
1080   if (handle_send_request_error (err, request, &tries))
1081     {
1082       reselect = 1;
1083       goto again;
1084     }
1085   if (err)
1086     goto leave;
1087
1088   err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1089   if (err)
1090     goto leave;
1091
1092   /* Return the read stream and close the HTTP context.  */
1093   *r_fp = fp;
1094   fp = NULL;
1095
1096  leave:
1097   es_fclose (fp);
1098   xfree (request);
1099   xfree (hostport);
1100   return err;
1101 }
1102
1103
1104
1105 \f
1106 /* Callback parameters for put_post_cb.  */
1107 struct put_post_parm_s
1108 {
1109   char *datastring;
1110 };
1111
1112
1113 /* Helper for ks_hkp_put.  */
1114 static gpg_error_t
1115 put_post_cb (void *opaque, http_t http)
1116 {
1117   struct put_post_parm_s *parm = opaque;
1118   gpg_error_t err = 0;
1119   estream_t fp;
1120   size_t len;
1121
1122   fp = http_get_write_ptr (http);
1123   len = strlen (parm->datastring);
1124
1125   es_fprintf (fp,
1126               "Content-Type: application/x-www-form-urlencoded\r\n"
1127               "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */);
1128   http_start_data (http);
1129   if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL))
1130     err = gpg_error_from_syserror ();
1131   return err;
1132 }
1133
1134
1135 /* Send the key in {DATA,DATALEN} to the keyserver identified by  URI.  */
1136 gpg_error_t
1137 ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
1138 {
1139   gpg_error_t err;
1140   char *hostport = NULL;
1141   char *request = NULL;
1142   estream_t fp = NULL;
1143   struct put_post_parm_s parm;
1144   char *armored = NULL;
1145   int reselect;
1146   unsigned int tries = SEND_REQUEST_RETRIES;
1147
1148   parm.datastring = NULL;
1149
1150   err = armor_data (&armored, data, datalen);
1151   if (err)
1152     goto leave;
1153
1154   parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
1155   if (!parm.datastring)
1156     {
1157       err = gpg_error_from_syserror ();
1158       goto leave;
1159     }
1160   xfree (armored);
1161   armored = NULL;
1162
1163   /* Build the request string.  */
1164   reselect = 0;
1165  again:
1166   xfree (hostport);
1167   hostport = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect);
1168   if (!hostport)
1169     {
1170       err = gpg_error_from_syserror ();
1171       goto leave;
1172     }
1173
1174   xfree (request);
1175   request = strconcat (hostport, "/pks/add", NULL);
1176   if (!request)
1177     {
1178       err = gpg_error_from_syserror ();
1179       goto leave;
1180     }
1181
1182   /* Send the request.  */
1183   err = send_request (ctrl, request, hostport, put_post_cb, &parm, &fp);
1184   if (handle_send_request_error (err, request, &tries))
1185     {
1186       reselect = 1;
1187       goto again;
1188     }
1189   if (err)
1190     goto leave;
1191
1192  leave:
1193   es_fclose (fp);
1194   xfree (parm.datastring);
1195   xfree (armored);
1196   xfree (request);
1197   xfree (hostport);
1198   return err;
1199 }