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