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