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
5 * This file is part of GnuPG.
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.
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.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <https://www.gnu.org/licenses/>.
27 #ifdef HAVE_W32_SYSTEM
28 # ifdef HAVE_WINSOCK2_H
29 # include <winsock2.h>
32 #else /*!HAVE_W32_SYSTEM*/
33 # include <sys/types.h>
34 # include <sys/socket.h>
36 #endif /*!HAVE_W32_SYSTEM*/
41 #include "dns-stuff.h"
42 #include "ks-engine.h"
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. */
48 # define EAI_OVERFLOW EAI_FAIL
50 #ifdef HAVE_W32_SYSTEM
52 # define EAI_SYSTEM WSAEINVAL
57 /* Number of seconds after a host is marked as resurrected. */
58 #define RESURRECT_INTERVAL (3600*3) /* 3 hours */
60 /* To match the behaviour of our old gpgkeys helper code we escape
61 more characters than actually needed. */
62 #define EXTRA_ESCAPE_CHARS "@!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
64 /* How many redirections do we allow. */
65 #define MAX_REDIRECTS 2
67 /* Number of retries done for a dead host etc. */
68 #define SEND_REQUEST_RETRIES 3
70 /* Objects used to maintain information about hosts. */
72 typedef struct hostinfo_s *hostinfo_t;
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
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 or NAME has a numerical IP address. */
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 v6
94 address is available. */
95 unsigned short port; /* The port used by the host, 0 if unknown. */
96 char name[1]; /* The hostname. */
100 /* An array of hostinfo_t for all hosts requested by the caller or
101 resolved from a pool name and its allocated size.*/
102 static hostinfo_t *hosttable;
103 static int hosttable_size;
105 /* The number of host slots we initially allocate for HOSTTABLE. */
106 #define INITIAL_HOSTTABLE_SIZE 10
109 /* Create a new hostinfo object, fill in NAME and put it into
110 HOSTTABLE. Return the index into hosttable on success or -1 on
113 create_new_hostinfo (const char *name)
115 hostinfo_t hi, *newtable;
119 hi = xtrymalloc (sizeof *hi + strlen (name));
122 strcpy (hi->name, name);
125 hi->lastused = (time_t)(-1);
126 hi->lastfail = (time_t)(-1);
137 /* Add it to the hosttable. */
138 for (idx=0; idx < hosttable_size; idx++)
144 /* Need to extend the hosttable. */
145 newsize = hosttable_size + INITIAL_HOSTTABLE_SIZE;
146 newtable = xtryrealloc (hosttable, newsize * sizeof *hosttable);
152 hosttable = newtable;
153 idx = hosttable_size;
154 hosttable_size = newsize;
156 hosttable[idx++] = hi;
157 while (idx < hosttable_size)
158 hosttable[idx++] = NULL;
164 /* Find the host NAME in our table. Return the index into the
165 hosttable or -1 if not found. */
167 find_hostinfo (const char *name)
171 for (idx=0; idx < hosttable_size; idx++)
172 if (hosttable[idx] && !ascii_strcasecmp (hosttable[idx]->name, name))
179 sort_hostpool (const void *xa, const void *xb)
184 assert (a >= 0 && a < hosttable_size);
185 assert (b >= 0 && b < hosttable_size);
186 assert (hosttable[a]);
187 assert (hosttable[b]);
189 return ascii_strcasecmp (hosttable[a]->name, hosttable[b]->name);
193 /* Return true if the host with the hosttable index TBLIDX is in POOL. */
195 host_in_pool_p (int *pool, int tblidx)
199 for (i=0; (pidx = pool[i]) != -1; i++)
200 if (pidx == tblidx && hosttable[pidx])
206 /* Select a random host. Consult TABLE which indices into the global
207 hosttable. Returns index into TABLE or -1 if no host could be
210 select_random_host (int *table)
216 /* We create a new table so that we randomly select only from
217 currently alive hosts. */
218 for (idx=0; (pidx = table[idx]) != -1; idx++)
219 if (hosttable[pidx] && !hosttable[pidx]->dead)
222 tbl = xtryrealloc(tbl, tblsize * sizeof *tbl);
224 return -1; /* memory allocation failed! */
225 tbl[tblsize-1] = pidx;
228 return -1; /* No hosts. */
230 if (tblsize == 1) /* Save a get_uint_nonce. */
233 pidx = tbl[get_uint_nonce () % tblsize];
240 /* Figure out if a set of DNS records looks like a pool. */
242 arecords_is_pool (dns_addrinfo_t aibuf)
248 for (ai = aibuf; ai; ai = ai->next)
250 if (ai->family == AF_INET6)
252 else if (ai->family == AF_INET)
256 return n_v6 > 1 || n_v4 > 1;
260 /* Add the host AI under the NAME into the HOSTTABLE. If PORT is not
261 zero, it specifies which port to use to talk to the host. If NAME
262 specifies a pool (as indicated by IS_POOL), update the given
263 reference table accordingly. */
265 add_host (const char *name, int is_pool,
266 const dns_addrinfo_t ai, unsigned short port,
267 int *reftbl, size_t reftblsize, int *refidx)
275 idx = find_hostinfo (name);
277 if (!is_pool && !is_ip_address (name))
279 /* This is a hostname but not a pool. Use the name
280 as given without going through resolve_dns_addr. */
281 tmphost = xtrystrdup (name);
283 tmperr = gpg_error_from_syserror ();
289 tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
290 DNS_WITHBRACKET, &tmphost);
291 if (tmphost && is_ip_address (tmphost))
297 log_info ("resolve_dns_addr failed while checking '%s': %s\n",
298 name, gpg_strerror (tmperr));
300 else if ((*refidx) + 1 >= reftblsize)
302 log_error ("resolve_dns_addr for '%s': '%s'"
303 " [index table full - ignored]\n", name, tmphost);
307 if (!is_pool && is_ip_address (name))
308 /* Update the original entry. */
311 tmpidx = find_hostinfo (tmphost);
312 log_info ("resolve_dns_addr for '%s': '%s'%s\n",
314 tmpidx == -1? "" : " [already known]");
316 if (tmpidx == -1) /* Create a new entry. */
317 tmpidx = create_new_hostinfo (tmphost);
321 log_error ("map_host for '%s' problem: %s - '%s'"
323 name, strerror (errno), tmphost);
325 else /* Set or update the entry. */
330 hosttable[tmpidx]->port = port;
335 tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
340 log_info ("resolve_dns_addr failed: %s\n",
341 gpg_strerror (tmperr));
349 if (ai->family == AF_INET6)
351 hosttable[tmpidx]->v6 = 1;
352 xfree (hosttable[tmpidx]->v6addr);
353 hosttable[tmpidx]->v6addr = ipaddr;
355 else if (ai->family == AF_INET)
357 hosttable[tmpidx]->v4 = 1;
358 xfree (hosttable[tmpidx]->v4addr);
359 hosttable[tmpidx]->v4addr = ipaddr;
364 for (i=0; i < *refidx; i++)
365 if (reftbl[i] == tmpidx)
367 if (!(i < *refidx) && tmpidx != idx)
368 reftbl[(*refidx)++] = tmpidx;
375 /* Map the host name NAME to the actual to be used host name. This
376 * allows us to manage round robin DNS names. We use our own strategy
377 * to choose one of the hosts. For example we skip those hosts which
378 * failed for some time and we stick to one host for a time
379 * independent of DNS retry times. If FORCE_RESELECT is true a new
380 * host is always selected. If SRVTAG is NULL no service record
381 * lookup will be done, if it is set that service name is used. The
382 * selected host is stored as a malloced string at R_HOST; on error
383 * NULL is stored. If we know the port used by the selected host from
384 * a service record, a string representation is written to R_PORTSTR,
385 * otherwise it is left untouched. If R_HTTPFLAGS is not NULL it will
386 * receive flags which are to be passed to http_open. If R_HTTPHOST
387 * is not NULL a malloced name of the host is stored there; this might
388 * be different from R_HOST in case it has been selected from a
391 map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect,
392 char **r_host, char *r_portstr,
393 unsigned int *r_httpflags, char **r_httphost)
405 /* No hostname means localhost. */
408 *r_host = xtrystrdup ("localhost");
409 return *r_host? 0 : gpg_error_from_syserror ();
412 /* See whether the host is in our table. */
413 idx = find_hostinfo (name);
414 if (idx == -1 && is_onion_address (name))
416 idx = create_new_hostinfo (name);
418 return gpg_error_from_syserror ();
424 /* We never saw this host. Allocate a new entry. */
425 dns_addrinfo_t aibuf, ai;
431 struct srventry *srvs;
432 unsigned int srvscount;
435 reftbl = xtrymalloc (reftblsize * sizeof *reftbl);
437 return gpg_error_from_syserror ();
440 idx = create_new_hostinfo (name);
443 err = gpg_error_from_syserror ();
449 if (srvtag && !is_ip_address (name))
451 /* Check for SRV records. */
452 err = get_dns_srv (name, srvtag, NULL, &srvs, &srvscount);
462 is_pool = srvscount > 1;
464 for (i = 0; i < srvscount; i++)
466 err = resolve_dns_name (srvs[i].target, 0,
467 AF_UNSPEC, SOCK_STREAM,
472 add_host (name, is_pool, ai, srvs[i].port,
473 reftbl, reftblsize, &refidx);
480 /* Find all A records for this entry and put them into the pool
482 err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname);
485 log_error ("resolving '%s' failed: %s\n", name, gpg_strerror (err));
490 /* First figure out whether this is a pool. For a pool we
491 use a different strategy than for a plain server: We use
492 the canonical name of the pool as the virtual host along
493 with the IP addresses. If it is not a pool, we use the
496 is_pool = arecords_is_pool (aibuf);
497 if (is_pool && cname)
503 for (ai = aibuf; ai; ai = ai->next)
505 if (ai->family != AF_INET && ai->family != AF_INET6)
509 add_host (name, is_pool, ai, 0, reftbl, reftblsize, &refidx);
514 free_dns_addrinfo (aibuf);
516 if (refidx && is_pool)
519 hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl);
522 err = gpg_error_from_syserror ();
523 log_error ("shrinking index table in map_host failed: %s\n",
528 qsort (hi->pool, refidx, sizeof *reftbl, sort_hostpool);
537 /* Deal with the pool name before selecting a host. */
540 *r_httphost = xtrystrdup (hi->cname? hi->cname : hi->name);
542 return gpg_error_from_syserror ();
545 /* If the currently selected host is now marked dead, force a
549 else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size
550 && hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead)
553 /* Select a host if needed. */
554 if (hi->poolidx == -1)
556 hi->poolidx = select_random_host (hi->pool);
557 if (hi->poolidx == -1)
559 log_error ("no alive host found in pool '%s'\n", name);
565 return gpg_error (GPG_ERR_NO_KEYSERVER);
569 assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size);
570 hi = hosttable[hi->poolidx];
573 else if (r_httphost && is_ip_address (hi->name))
575 /* This is a numerical IP address and not a pool. We want to
576 * find the canonical name so that it can be used in the HTTP
577 * Host header. Fixme: We should store that name in the
579 dns_addrinfo_t aibuf, ai;
582 err = resolve_dns_name (hi->name, 0, 0, SOCK_STREAM, &aibuf, NULL);
585 for (ai = aibuf; ai; ai = ai->next)
587 if (ai->family == AF_INET6 || ai->family == AF_INET)
589 err = resolve_dns_addr (ai->addr, ai->addrlen, 0, &host);
592 /* Okay, we return the first found name. */
599 free_dns_addrinfo (aibuf);
604 log_error ("host '%s' marked as dead\n", hi->name);
610 return gpg_error (GPG_ERR_NO_KEYSERVER);
615 /* If the hosttable does not indicate that a certain host
616 supports IPv<N>, we explicit set the corresponding http
617 flags. The reason for this is that a host might be listed in
618 a pool as not v6 only but actually support v6 when later
619 the name is resolved by our http layer. */
621 *r_httpflags |= HTTP_FLAG_IGNORE_IPv4;
623 *r_httpflags |= HTTP_FLAG_IGNORE_IPv6;
625 /* Note that we do not set the HTTP_FLAG_FORCE_TOR for onion
626 addresses because the http module detects this itself. This
627 also allows us to use an onion address without Tor mode being
631 *r_host = xtrystrdup (hi->name);
634 err = gpg_error_from_syserror ();
643 snprintf (r_portstr, 6 /* five digits and the sentinel */,
649 /* Mark the host NAME as dead. NAME may be given as an URL. Returns
650 true if a host was really marked as dead or was already marked dead
651 (e.g. by a concurrent session). */
653 mark_host_dead (const char *name)
656 char *host_buffer = NULL;
657 parsed_uri_t parsed_uri = NULL;
660 if (name && *name && !http_parse_uri (&parsed_uri, name, 1))
662 if (parsed_uri->v6lit)
664 host_buffer = strconcat ("[", parsed_uri->host, "]", NULL);
666 log_error ("out of core in mark_host_dead");
670 host = parsed_uri->host;
675 if (host && *host && strcmp (host, "localhost"))
680 idx = find_hostinfo (host);
684 log_info ("marking host '%s' as dead%s\n",
685 hi->name, hi->dead? " (again)":"");
687 hi->died_at = gnupg_get_time ();
694 http_release_parsed_uri (parsed_uri);
700 /* Mark a host in the hosttable as dead or - if ALIVE is true - as
703 ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive)
707 int idx, idx2, idx3, n;
709 if (!name || !*name || !strcmp (name, "localhost"))
712 idx = find_hostinfo (name);
714 return gpg_error (GPG_ERR_NOT_FOUND);
717 if (alive && hi->dead)
720 err = ks_printf_help (ctrl, "marking '%s' as alive", name);
722 else if (!alive && !hi->dead)
725 hi->died_at = 0; /* Manually set dead. */
726 err = ks_printf_help (ctrl, "marking '%s' as dead", name);
729 /* If the host is a pool mark all member hosts. */
730 if (!err && hi->pool)
732 for (idx2=0; !err && (n=hi->pool[idx2]) != -1; idx2++)
734 assert (n >= 0 && n < hosttable_size);
738 /* Do not mark a host from a pool dead if it is also a
739 member in another pool. */
740 for (idx3=0; idx3 < hosttable_size; idx3++)
743 && hosttable[idx3]->pool
745 && host_in_pool_p (hosttable[idx3]->pool, n))
748 if (idx3 < hosttable_size)
749 continue; /* Host is also a member of another pool. */
755 else if (alive && hi2->dead)
758 err = ks_printf_help (ctrl, "marking '%s' as alive",
761 else if (!alive && !hi2->dead)
764 hi2->died_at = 0; /* Manually set dead. */
765 err = ks_printf_help (ctrl, "marking '%s' as dead",
775 /* Debug function to print the entire hosttable. */
777 ks_hkp_print_hosttable (ctrl_t ctrl)
787 err = ks_print_help (ctrl, "hosttable (idx, ipv6, ipv4, dead, name, time):");
791 curtime = gnupg_get_time ();
792 for (idx=0; idx < hosttable_size; idx++)
793 if ((hi=hosttable[idx]))
795 if (hi->dead && hi->died_at)
797 died = elapsed_time_string (hi->died_at, curtime);
798 diedstr = died? died : "error";
801 diedstr = died = NULL;
802 err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s%s\n",
804 hi->onion? "O" : hi->v6? "6":" ",
808 hi->v6addr? " v6=":"",
809 hi->v6addr? hi->v6addr:"",
810 hi->v4addr? " v4=":"",
811 hi->v4addr? hi->v4addr:"",
820 err = ks_printf_help (ctrl, " . %s", hi->cname);
826 init_membuf (&mb, 256);
827 put_membuf_printf (&mb, " . -->");
828 for (idx2=0; hi->pool[idx2] != -1; idx2++)
830 put_membuf_printf (&mb, " %d", hi->pool[idx2]);
831 if (hi->poolidx == hi->pool[idx2])
832 put_membuf_printf (&mb, "*");
834 put_membuf( &mb, "", 1);
835 p = get_membuf (&mb, NULL);
837 return gpg_error_from_syserror ();
838 err = ks_print_help (ctrl, p);
849 /* Print a help output for the schemata supported by this module. */
851 ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri)
854 "Handler for HKP URLs:\n"
856 #if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS
859 "Supported methods: search, get, put\n";
862 #if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS
863 const char data2[] = " hkp\n hkps";
865 const char data2[] = " hkp";
869 err = ks_print_help (ctrl, data2);
870 else if (uri->is_http && (!strcmp (uri->scheme, "hkp")
871 || !strcmp (uri->scheme, "hkps")))
872 err = ks_print_help (ctrl, data);
880 /* Build the remote part of the URL from SCHEME, HOST and an optional
881 * PORT. If NO_SRV is set no SRV record lookup will be done. Returns
882 * an allocated string at R_HOSTPORT or NULL on failure. If
883 * R_HTTPHOST is not NULL it receives a malloced string with the
884 * hostname; this may be different from HOST if HOST is selected from
887 make_host_part (ctrl_t ctrl,
888 const char *scheme, const char *host, unsigned short port,
889 int force_reselect, int no_srv,
890 char **r_hostport, unsigned int *r_httpflags, char **r_httphost)
899 if (!strcmp (scheme, "hkps") || !strcmp (scheme,"https"))
902 srvtag = no_srv? NULL : "pgpkey-https";
904 else /* HKP or HTTP. */
907 srvtag = no_srv? NULL : "pgpkey-http";
911 err = map_host (ctrl, host, srvtag, force_reselect,
912 &hostname, portstr, r_httpflags, r_httphost);
916 /* If map_host did not return a port (from a SRV record) but a port
917 * has been specified (implicitly or explicitly) then use that port.
918 * In the case that a port was not specified (which is probably a
919 * bug in https.c) we will set up defaults. */
922 else if (!*portstr && port)
923 snprintf (portstr, sizeof portstr, "%hu", port);
924 else if (!strcmp (scheme,"https"))
925 strcpy (portstr, "443");
927 strcpy (portstr, "11371");
929 if (*hostname != '[' && is_ip_address (hostname) == 6)
930 *r_hostport = strconcat (scheme, "://[", hostname, "]:", portstr, NULL);
932 *r_hostport = strconcat (scheme, "://", hostname, ":", portstr, NULL);
941 return gpg_error_from_syserror ();
947 /* Resolve all known keyserver names and update the hosttable. This
948 is mainly useful for debugging because the resolving is anyway done
951 ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri)
954 char *hostport = NULL;
956 /* NB: With an explicitly given port we do not want to consult a
957 * service record because that might be in conflict with the port
958 * from such a service record. */
959 err = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
960 1, uri->explicit_port,
961 &hostport, NULL, NULL);
964 err = ks_printf_help (ctrl, "%s://%s:%hu: resolve failed: %s",
965 uri->scheme, uri->host, uri->port,
970 err = ks_printf_help (ctrl, "%s", hostport);
977 /* Housekeeping function called from the housekeeping thread. It is
978 used to mark dead hosts alive so that they may be tried again after
981 ks_hkp_housekeeping (time_t curtime)
986 for (idx=0; idx < hosttable_size; idx++)
994 continue; /* Do not resurrect manually shot hosts. */
995 if (hi->died_at + RESURRECT_INTERVAL <= curtime
996 || hi->died_at > curtime)
999 log_info ("resurrected host '%s'", hi->name);
1005 /* Reload (SIGHUP) action for this module. We mark all host alive
1006 * even those which have been manually shot. */
1008 ks_hkp_reload (void)
1013 for (idx=count=0; idx < hosttable_size; idx++)
1015 hi = hosttable[idx];
1024 log_info ("number of resurrected hosts: %d", count);
1028 /* Send an HTTP request. On success returns an estream object at
1029 R_FP. HOSTPORTSTR is only used for diagnostics. If HTTPHOST is
1030 not NULL it will be used as HTTP "Host" header. If POST_CB is not
1031 NULL a post request is used and that callback is called to allow
1032 writing the post data. If R_HTTP_STATUS is not NULL, the http
1033 status code will be stored there. */
1035 send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
1036 const char *httphost, unsigned int httpflags,
1037 gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value,
1038 estream_t *r_fp, unsigned int *r_http_status)
1041 http_session_t session = NULL;
1043 int redirects_left = MAX_REDIRECTS;
1044 estream_t fp = NULL;
1045 char *request_buffer = NULL;
1049 err = http_session_new (&session, NULL, httphost, HTTP_FLAG_TRUST_DEF);
1052 http_session_set_log_cb (session, cert_log_cb);
1055 err = http_open (&http,
1056 post_cb? HTTP_REQ_POST : HTTP_REQ_GET,
1059 /* fixme: AUTH */ NULL,
1061 |(opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0)
1062 |(opt.use_tor? HTTP_FLAG_FORCE_TOR:0)),
1066 /*FIXME curl->srvtag*/NULL);
1069 fp = http_get_write_ptr (http);
1070 /* Avoid caches to get the most recent copy of the key. We set
1071 both the Pragma and Cache-Control versions of the header, so
1072 we're good with both HTTP 1.0 and 1.1. */
1073 es_fputs ("Pragma: no-cache\r\n"
1074 "Cache-Control: no-cache\r\n", fp);
1076 err = post_cb (post_cb_value, http);
1079 http_start_data (http);
1081 err = gpg_error_from_syserror ();
1086 /* Fixme: After a redirection we show the old host name. */
1087 log_error (_("error connecting to '%s': %s\n"),
1088 hostportstr, gpg_strerror (err));
1092 /* Wait for the response. */
1093 dirmngr_tick (ctrl);
1094 err = http_wait_response (http);
1097 log_error (_("error reading HTTP response for '%s': %s\n"),
1098 hostportstr, gpg_strerror (err));
1102 if (http_get_tls_info (http, NULL))
1104 /* Update the httpflags so that a redirect won't fallback to an
1105 unencrypted connection. */
1106 httpflags |= HTTP_FLAG_FORCE_TLS;
1110 *r_http_status = http_get_status_code (http);
1112 switch (http_get_status_code (http))
1116 break; /* Success. */
1122 const char *s = http_get_header (http, "Location");
1124 log_info (_("URL '%s' redirected to '%s' (%u)\n"),
1125 request, s?s:"[none]", http_get_status_code (http));
1126 if (s && *s && redirects_left-- )
1128 xfree (request_buffer);
1129 request_buffer = xtrystrdup (s);
1132 request = request_buffer;
1133 http_close (http, 0);
1137 err = gpg_error_from_syserror ();
1140 err = gpg_error (GPG_ERR_NO_DATA);
1141 log_error (_("too many redirections\n"));
1146 err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1150 log_error (_("error accessing '%s': http status %u\n"),
1151 request, http_get_status_code (http));
1152 err = gpg_error (GPG_ERR_NO_DATA);
1156 /* FIXME: We should register a permanent redirection and whether a
1157 host has ever used TLS so that future calls will always use
1160 fp = http_get_read_ptr (http);
1163 err = gpg_error (GPG_ERR_BUG);
1167 /* Return the read stream and close the HTTP context. */
1169 http_close (http, 1);
1173 http_close (http, 0);
1174 http_session_release (session);
1175 xfree (request_buffer);
1180 /* Helper to evaluate the error code ERR form a send_request() call
1181 with REQUEST. The function returns true if the caller shall try
1182 again. TRIES_LEFT points to a variable to track the number of
1183 retries; this function decrements it and won't return true if it is
1186 handle_send_request_error (gpg_error_t err, const char *request,
1187 unsigned int *tries_left)
1191 /* Fixme: Should we disable all hosts of a protocol family if a
1192 * request for an address of that familiy returned ENETDOWN? */
1194 switch (gpg_err_code (err))
1196 case GPG_ERR_ECONNREFUSED:
1201 sock = assuan_sock_connect_byname (NULL, 0, 0, NULL, ASSUAN_SOCK_TOR);
1202 if (sock == ASSUAN_INVALID_FD)
1203 log_info ("(it seems Tor is not running)\n");
1205 assuan_sock_close (sock);
1208 case GPG_ERR_ENETUNREACH:
1209 case GPG_ERR_ENETDOWN:
1210 case GPG_ERR_UNKNOWN_HOST:
1211 case GPG_ERR_NETWORK:
1212 if (mark_host_dead (request) && *tries_left)
1216 case GPG_ERR_ETIMEDOUT:
1219 log_info ("selecting a different host due to a timeout\n");
1235 /* Search the keyserver identified by URI for keys matching PATTERN.
1236 On success R_FP has an open stream to read the data. If
1237 R_HTTP_STATUS is not NULL, the http status code will be stored
1240 ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
1241 estream_t *r_fp, unsigned int *r_http_status)
1244 KEYDB_SEARCH_DESC desc;
1245 char fprbuf[2+40+1];
1246 char *hostport = NULL;
1247 char *request = NULL;
1248 estream_t fp = NULL;
1250 unsigned int httpflags;
1251 char *httphost = NULL;
1252 unsigned int tries = SEND_REQUEST_RETRIES;
1256 /* Remove search type indicator and adjust PATTERN accordingly.
1257 Note that HKP keyservers like the 0x to be present when searching
1258 by keyid. We need to re-format the fingerprint and keyids so to
1259 remove the gpg specific force-use-of-this-key flag ("!"). */
1260 err = classify_user_id (pattern, &desc, 1);
1265 case KEYDB_SEARCH_MODE_EXACT:
1266 case KEYDB_SEARCH_MODE_SUBSTR:
1267 case KEYDB_SEARCH_MODE_MAIL:
1268 case KEYDB_SEARCH_MODE_MAILSUB:
1269 pattern = desc.u.name;
1271 case KEYDB_SEARCH_MODE_SHORT_KID:
1272 snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]);
1275 case KEYDB_SEARCH_MODE_LONG_KID:
1276 snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX",
1277 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1280 case KEYDB_SEARCH_MODE_FPR16:
1283 bin2hex (desc.u.fpr, 16, fprbuf+2);
1286 case KEYDB_SEARCH_MODE_FPR20:
1287 case KEYDB_SEARCH_MODE_FPR:
1290 bin2hex (desc.u.fpr, 20, fprbuf+2);
1294 return gpg_error (GPG_ERR_INV_USER_ID);
1297 /* Build the request string. */
1303 xfree (hostport); hostport = NULL;
1304 xfree (httphost); httphost = NULL;
1305 err = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
1306 reselect, uri->explicit_port,
1307 &hostport, &httpflags, &httphost);
1311 searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS);
1314 err = gpg_error_from_syserror ();
1319 request = strconcat (hostport,
1320 "/pks/lookup?op=index&options=mr&search=",
1326 err = gpg_error_from_syserror ();
1331 /* Send the request. */
1332 err = send_request (ctrl, request, hostport, httphost, httpflags,
1333 NULL, NULL, &fp, r_http_status);
1334 if (handle_send_request_error (err, request, &tries))
1342 err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1346 /* Peek at the response. */
1348 int c = es_getc (fp);
1351 err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF);
1352 log_error ("error reading response: %s\n", gpg_strerror (err));
1357 /* The document begins with a '<': Assume a HTML response,
1358 which we don't support. */
1359 err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
1365 /* Return the read stream. */
1378 /* Get the key described key the KEYSPEC string from the keyserver
1379 identified by URI. On success R_FP has an open stream to read the
1380 data. The data will be provided in a format GnuPG can import
1381 (either a binary OpenPGP message or an armored one). */
1383 ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
1386 KEYDB_SEARCH_DESC desc;
1387 char kidbuf[2+40+1];
1388 const char *exactname = NULL;
1389 char *searchkey = NULL;
1390 char *hostport = NULL;
1391 char *request = NULL;
1392 estream_t fp = NULL;
1394 char *httphost = NULL;
1395 unsigned int httpflags;
1396 unsigned int tries = SEND_REQUEST_RETRIES;
1400 /* Remove search type indicator and adjust PATTERN accordingly.
1401 Note that HKP keyservers like the 0x to be present when searching
1402 by keyid. We need to re-format the fingerprint and keyids so to
1403 remove the gpg specific force-use-of-this-key flag ("!"). */
1404 err = classify_user_id (keyspec, &desc, 1);
1409 case KEYDB_SEARCH_MODE_SHORT_KID:
1410 snprintf (kidbuf, sizeof kidbuf, "0x%08lX", (ulong)desc.u.kid[1]);
1412 case KEYDB_SEARCH_MODE_LONG_KID:
1413 snprintf (kidbuf, sizeof kidbuf, "0x%08lX%08lX",
1414 (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1416 case KEYDB_SEARCH_MODE_FPR20:
1417 case KEYDB_SEARCH_MODE_FPR:
1418 /* This is a v4 fingerprint. */
1421 bin2hex (desc.u.fpr, 20, kidbuf+2);
1424 case KEYDB_SEARCH_MODE_EXACT:
1425 exactname = desc.u.name;
1428 case KEYDB_SEARCH_MODE_FPR16:
1429 log_error ("HKP keyservers do not support v3 fingerprints\n");
1431 return gpg_error (GPG_ERR_INV_USER_ID);
1434 searchkey = http_escape_string (exactname? exactname : kidbuf,
1435 EXTRA_ESCAPE_CHARS);
1438 err = gpg_error_from_syserror ();
1444 /* Build the request string. */
1445 xfree (hostport); hostport = NULL;
1446 xfree (httphost); httphost = NULL;
1447 err = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
1448 reselect, uri->explicit_port,
1449 &hostport, &httpflags, &httphost);
1454 request = strconcat (hostport,
1455 "/pks/lookup?op=get&options=mr&search=",
1457 exactname? "&exact=on":"",
1461 err = gpg_error_from_syserror ();
1465 /* Send the request. */
1466 err = send_request (ctrl, request, hostport, httphost, httpflags,
1467 NULL, NULL, &fp, NULL);
1468 if (handle_send_request_error (err, request, &tries))
1476 err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1480 /* Return the read stream and close the HTTP context. */
1496 /* Callback parameters for put_post_cb. */
1497 struct put_post_parm_s
1503 /* Helper for ks_hkp_put. */
1505 put_post_cb (void *opaque, http_t http)
1507 struct put_post_parm_s *parm = opaque;
1508 gpg_error_t err = 0;
1512 fp = http_get_write_ptr (http);
1513 len = strlen (parm->datastring);
1516 "Content-Type: application/x-www-form-urlencoded\r\n"
1517 "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */);
1518 http_start_data (http);
1519 if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL))
1520 err = gpg_error_from_syserror ();
1525 /* Send the key in {DATA,DATALEN} to the keyserver identified by URI. */
1527 ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
1530 char *hostport = NULL;
1531 char *request = NULL;
1532 estream_t fp = NULL;
1533 struct put_post_parm_s parm;
1534 char *armored = NULL;
1536 char *httphost = NULL;
1537 unsigned int httpflags;
1538 unsigned int tries = SEND_REQUEST_RETRIES;
1540 parm.datastring = NULL;
1542 err = armor_data (&armored, data, datalen);
1546 parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
1547 if (!parm.datastring)
1549 err = gpg_error_from_syserror ();
1555 /* Build the request string. */
1558 xfree (hostport); hostport = NULL;
1559 xfree (httphost); httphost = NULL;
1560 err = make_host_part (ctrl, uri->scheme, uri->host, uri->port,
1561 reselect, uri->explicit_port,
1562 &hostport, &httpflags, &httphost);
1567 request = strconcat (hostport, "/pks/add", NULL);
1570 err = gpg_error_from_syserror ();
1574 /* Send the request. */
1575 err = send_request (ctrl, request, hostport, httphost, 0,
1576 put_post_cb, &parm, &fp, NULL);
1577 if (handle_send_request_error (err, request, &tries))
1587 xfree (parm.datastring);