summaryrefslogtreecommitdiff
path: root/dirmngr
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2021-02-09 16:00:23 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2021-02-09 16:00:23 +0900
commit5ce840383da7cf82ffa7dfaeda187f3fe3d591a7 (patch)
treef93fb33cde2a62aa414b61dca085f3fd0613aaca /dirmngr
parentd9f0d99e31569835e295b990029c6dd19554299c (diff)
downloadgpg2-5ce840383da7cf82ffa7dfaeda187f3fe3d591a7.tar.gz
gpg2-5ce840383da7cf82ffa7dfaeda187f3fe3d591a7.tar.bz2
gpg2-5ce840383da7cf82ffa7dfaeda187f3fe3d591a7.zip
Imported Upstream version 2.1.22upstream/2.1.22
Diffstat (limited to 'dirmngr')
-rw-r--r--dirmngr/Makefile.am8
-rw-r--r--dirmngr/dirmngr-client.c2
-rw-r--r--dirmngr/dirmngr.c88
-rw-r--r--dirmngr/dirmngr.h9
-rw-r--r--dirmngr/dirmngr_ldap.c2
-rw-r--r--dirmngr/dns-stuff.c49
-rw-r--r--dirmngr/dns.c288
-rw-r--r--dirmngr/http.c328
-rw-r--r--dirmngr/http.h5
-rw-r--r--dirmngr/ks-action.c16
-rw-r--r--dirmngr/ks-engine-finger.c2
-rw-r--r--dirmngr/ks-engine-hkp.c272
-rw-r--r--dirmngr/ks-engine-http.c26
-rw-r--r--dirmngr/ldap.c1
-rw-r--r--dirmngr/loadswdb.c1
-rw-r--r--dirmngr/server.c42
-rw-r--r--dirmngr/t-http.c31
17 files changed, 965 insertions, 205 deletions
diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am
index 34f2c5d..b404165 100644
--- a/dirmngr/Makefile.am
+++ b/dirmngr/Makefile.am
@@ -143,7 +143,7 @@ endif
t_http_SOURCES = $(t_common_src) t-http.c http.c dns-stuff.c http-common.c
t_http_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \
$(LIBGCRYPT_CFLAGS) $(NTBTLS_CFLAGS) $(LIBGNUTLS_CFLAGS) \
- $(GPG_ERROR_CFLAGS) $(KSBA_CFLAGS)
+ $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(KSBA_CFLAGS)
t_http_LDADD = $(t_common_ldadd) \
$(NTBTLS_LIBS) $(KSBA_LIBS) $(LIBGNUTLS_LIBS) $(DNSLIBS)
@@ -152,11 +152,13 @@ t_ldap_parse_uri_SOURCES = \
http.c http-common.c dns-stuff.c \
$(ldap_url) $(t_common_src)
t_ldap_parse_uri_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \
- $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
+ $(LIBGCRYPT_CFLAGS) \
+ $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)
t_ldap_parse_uri_LDADD = $(ldaplibs) $(t_common_ldadd) $(DNSLIBS)
t_dns_stuff_CFLAGS = -DWITHOUT_NPTH=1 $(USE_C99_CFLAGS) \
- $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
+ $(LIBGCRYPT_CFLAGS) \
+ $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)
t_dns_stuff_SOURCES = $(t_common_src) t-dns-stuff.c dns-stuff.c
t_dns_stuff_LDADD = $(t_common_ldadd) $(DNSLIBS)
diff --git a/dirmngr/dirmngr-client.c b/dirmngr/dirmngr-client.c
index 4dc64bf..53b405e 100644
--- a/dirmngr/dirmngr-client.c
+++ b/dirmngr/dirmngr-client.c
@@ -80,7 +80,7 @@ static ARGPARSE_OPTS opts[] = {
{ oPEM, "pem", 0, N_("expect certificates in PEM format")},
{ oForceDefaultResponder, "force-default-responder", 0,
N_("force the use of the default OCSP responder")},
- { 0, NULL, 0, NULL }
+ ARGPARSE_end ()
};
diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c
index 8393e0b..0d133c6 100644
--- a/dirmngr/dirmngr.c
+++ b/dirmngr/dirmngr.c
@@ -147,6 +147,8 @@ enum cmd_and_opt_values {
oStandardResolver,
oRecursiveResolver,
oResolverTimeout,
+ oConnectTimeout,
+ oConnectQuickTimeout,
aTest
};
@@ -250,6 +252,8 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_n (oStandardResolver, "standard-resolver", "@"),
ARGPARSE_s_n (oRecursiveResolver, "recursive-resolver", "@"),
ARGPARSE_s_i (oResolverTimeout, "resolver-timeout", "@"),
+ ARGPARSE_s_i (oConnectTimeout, "connect-timeout", "@"),
+ ARGPARSE_s_i (oConnectQuickTimeout, "connect-quick-timeout", "@"),
ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing "
"of all commands and options)\n")),
@@ -277,6 +281,9 @@ static struct debug_flags_s debug_flags [] =
#define DEFAULT_MAX_REPLIES 10
#define DEFAULT_LDAP_TIMEOUT 100 /* arbitrary large timeout */
+#define DEFAULT_CONNECT_TIMEOUT (15*1000) /* 15 seconds */
+#define DEFAULT_CONNECT_QUICK_TIMEOUT ( 2*1000) /* 2 seconds */
+
/* For the cleanup handler we need to keep track of the socket's name. */
static const char *socket_name;
/* If the socket has been redirected, this is the name of the
@@ -327,8 +334,9 @@ static int network_activity_seen;
static strlist_t hkp_cacert_filenames;
-/* The timer tick used for housekeeping stuff. */
-#define TIMERTICK_INTERVAL (60)
+/* The timer tick used for housekeeping stuff. The second constant is used when a shutdown is pending. */
+#define TIMERTICK_INTERVAL (60)
+#define TIMERTICK_INTERVAL_SHUTDOWN (4)
/* How oft to run the housekeeping. */
#define HOUSEKEEPING_INTERVAL (600)
@@ -524,7 +532,17 @@ dirmngr_use_tor (void)
{
if (tor_mode == TOR_MODE_AUTO)
{
- /* FIXME: Figure out whether Tor is running. */
+ /* Figure out whether Tor is running. */
+ assuan_fd_t sock;
+
+ sock = assuan_sock_connect_byname (NULL, 0, 0, NULL, ASSUAN_SOCK_TOR);
+ if (sock == ASSUAN_INVALID_FD)
+ tor_mode = TOR_MODE_NO;
+ else
+ {
+ tor_mode = TOR_MODE_YES;
+ assuan_sock_close (sock);
+ }
}
if (tor_mode == TOR_MODE_FORCE)
@@ -602,6 +620,8 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
disable_check_own_socket = 0;
enable_standard_resolver (0);
set_dns_timeout (0);
+ opt.connect_timeout = 0;
+ opt.connect_quick_timeout = 0;
return 1;
}
@@ -703,6 +723,14 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
set_dns_timeout (pargs->r.ret_int);
break;
+ case oConnectTimeout:
+ opt.connect_timeout = pargs->r.ret_ulong * 1000;
+ break;
+
+ case oConnectQuickTimeout:
+ opt.connect_quick_timeout = pargs->r.ret_ulong * 1000;
+ break;
+
default:
return 0; /* Not handled. */
}
@@ -716,6 +744,21 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
}
+/* This fucntion is called after option parsing to adjust some values
+ * and call option setup functions. */
+static void
+post_option_parsing (void)
+{
+ /* It would be too surpirsing if the quick timeout is larger than
+ * the standard value. */
+ if (opt.connect_quick_timeout > opt.connect_timeout)
+ opt.connect_quick_timeout = opt.connect_timeout;
+
+ set_debug ();
+ set_tor_mode ();
+}
+
+
#ifndef HAVE_W32_SYSTEM
static int
pid_suffix_callback (unsigned long *r_suffix)
@@ -844,6 +887,10 @@ main (int argc, char **argv)
/* Reset rereadable options to default values. */
parse_rereadable_options (NULL, 0);
+ /* Default TCP timeouts. */
+ opt.connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+ opt.connect_quick_timeout = DEFAULT_CONNECT_QUICK_TIMEOUT;
+
/* LDAP defaults. */
opt.add_new_ldapservers = 0;
opt.ldaptimeout = DEFAULT_LDAP_TIMEOUT;
@@ -1031,8 +1078,7 @@ main (int argc, char **argv)
log_printf ("\n");
}
- set_debug ();
- set_tor_mode ();
+ post_option_parsing ();
/* Get LDAP server list from file. */
#if USE_LDAP
@@ -1316,13 +1362,18 @@ main (int argc, char **argv)
log_set_prefix (NULL, oldflags | GPGRT_LOG_RUN_DETACHED);
opt.running_detached = 1;
- if (chdir("/"))
+ }
+#endif
+
+ if (!nodetach )
+ {
+ if (gnupg_chdir (gnupg_daemon_rootdir ()))
{
- log_error ("chdir to / failed: %s\n", strerror (errno));
+ log_error ("chdir to '%s' failed: %s\n",
+ gnupg_daemon_rootdir (), strerror (errno));
dirmngr_exit (1);
}
}
-#endif
thread_init ();
cert_cache_init (hkp_cacert_filenames);
@@ -1513,6 +1564,7 @@ dirmngr_init_default_ctrl (ctrl_t ctrl)
if (opt.http_proxy)
ctrl->http_proxy = xstrdup (opt.http_proxy);
ctrl->http_no_crl = 1;
+ ctrl->timeout = opt.connect_timeout;
}
@@ -1774,8 +1826,7 @@ reread_configuration (void)
}
fclose (fp);
- set_debug ();
- set_tor_mode ();
+ post_option_parsing ();
}
@@ -1927,6 +1978,8 @@ time_for_housekeeping_p (time_t curtime)
static void
handle_tick (void)
{
+ struct stat statbuf;
+
if (time_for_housekeeping_p (gnupg_get_time ()))
{
npth_t thread;
@@ -1946,6 +1999,14 @@ handle_tick (void)
npth_attr_destroy (&tattr);
}
}
+
+ /* Check whether the homedir is still available. */
+ if (!shutdown_pending
+ && stat (gnupg_homedir (), &statbuf) && errno == ENOENT)
+ {
+ shutdown_pending = 1;
+ log_info ("homedir has been removed - shutting down\n");
+ }
}
@@ -2139,10 +2200,13 @@ handle_connections (assuan_fd_t listen_fd)
npth_clock_gettime (&curtime);
if (!(npth_timercmp (&curtime, &abstime, <)))
{
- /* Timeout. */
+ /* Timeout. When a shutdown is pending we use a shorter
+ * interval to handle the shutdown more quickly. */
handle_tick ();
npth_clock_gettime (&abstime);
- abstime.tv_sec += TIMERTICK_INTERVAL;
+ abstime.tv_sec += (shutdown_pending
+ ? TIMERTICK_INTERVAL_SHUTDOWN
+ : TIMERTICK_INTERVAL);
}
npth_timersub (&abstime, &curtime, &timeout);
diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h
index e10de09..1f660de 100644
--- a/dirmngr/dirmngr.h
+++ b/dirmngr/dirmngr.h
@@ -95,6 +95,10 @@ struct
int force; /* Force loading outdated CRLs. */
+
+ unsigned int connect_timeout; /* Timeout for connect. */
+ unsigned int connect_quick_timeout; /* Shorter timeout for connect. */
+
int disable_http; /* Do not use HTTP at all. */
int disable_ldap; /* Do not use LDAP at all. */
int disable_ipv4; /* Do not use legacy IP addresses. */
@@ -194,6 +198,8 @@ struct server_control_s
int audit_events; /* Send audit events to client. */
char *http_proxy; /* The used http_proxy or NULL. */
+ unsigned int timeout; /* Timeout for connect calls in ms. */
+
unsigned int http_no_crl:1; /* Do not check CRLs for https. */
};
@@ -223,6 +229,9 @@ int dirmngr_assuan_log_monitor (assuan_context_t ctx, unsigned int cat,
void start_command_handler (gnupg_fd_t fd);
gpg_error_t dirmngr_status (ctrl_t ctrl, const char *keyword, ...);
gpg_error_t dirmngr_status_help (ctrl_t ctrl, const char *text);
+gpg_error_t dirmngr_status_printf (ctrl_t ctrl, const char *keyword,
+ const char *format,
+ ...) GPGRT_ATTR_PRINTF(3,4);
gpg_error_t dirmngr_tick (ctrl_t ctrl);
/*-- http-ntbtls.c --*/
diff --git a/dirmngr/dirmngr_ldap.c b/dirmngr/dirmngr_ldap.c
index 836ced0..5a9ae97 100644
--- a/dirmngr/dirmngr_ldap.c
+++ b/dirmngr/dirmngr_ldap.c
@@ -150,7 +150,7 @@ static ARGPARSE_OPTS opts[] = {
{ oAttr, "attr", 2, N_("|STRING|return the attribute STRING")},
{ oOnlySearchTimeout, "only-search-timeout", 0, "@"},
{ oLogWithPID,"log-with-pid", 0, "@"},
- { 0, NULL, 0, NULL }
+ ARGPARSE_end ()
};
diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c
index a6c14cd..7324aae 100644
--- a/dirmngr/dns-stuff.c
+++ b/dirmngr/dns-stuff.c
@@ -45,6 +45,9 @@
# endif
# include <netdb.h>
#endif
+#ifdef HAVE_STAT
+# include <sys/stat.h>
+#endif
#include <string.h>
#include <unistd.h>
@@ -111,6 +114,8 @@
#define DEFAULT_TIMEOUT 30
+#define RESOLV_CONF_NAME "/etc/resolv.conf"
+
/* Two flags to enable verbose and debug mode. */
static int opt_verbose;
static int opt_debug;
@@ -391,6 +396,39 @@ libdns_error_to_gpg_error (int serr)
#endif /*USE_LIBDNS*/
+/* Return true if resolve.conf changed since it was last loaded. */
+#ifdef USE_LIBDNS
+static int
+resolv_conf_changed_p (void)
+{
+#if defined(HAVE_W32_SYSTEM) || !defined(HAVE_STAT)
+ return 0;
+#else
+ static time_t last_mtime;
+ const char *fname = RESOLV_CONF_NAME;
+ struct stat statbuf;
+ int changed = 0;
+
+ if (stat (fname, &statbuf))
+ {
+ log_error ("stat'ing '%s' failed: %s\n",
+ fname, gpg_strerror (gpg_error_from_syserror ()));
+ last_mtime = 1; /* Force a "changed" result the next time stat
+ * works. */
+ }
+ else if (!last_mtime)
+ last_mtime = statbuf.st_mtime;
+ else if (last_mtime != statbuf.st_mtime)
+ {
+ changed = 1;
+ last_mtime = statbuf.st_mtime;
+ }
+
+ return changed;
+#endif
+}
+#endif /*USE_LIBDNS*/
+
#ifdef USE_LIBDNS
/* Initialize libdns. Returns 0 on success; prints a diagnostic and
* returns an error code on failure. */
@@ -496,7 +534,8 @@ libdns_init (void)
#else /* Unix */
const char *fname;
- fname = "/etc/resolv.conf";
+ fname = RESOLV_CONF_NAME;
+ resolv_conf_changed_p (); /* Reset timestamp. */
err = libdns_error_to_gpg_error
(dns_resconf_loadpath (ld.resolv_conf, fname));
if (err)
@@ -653,6 +692,14 @@ libdns_res_open (struct dns_resolver **r_res)
*r_res = NULL;
+ /* Force a reload if resolv.conf has changed. */
+ if (resolv_conf_changed_p ())
+ {
+ if (opt_debug)
+ log_debug ("dns: resolv.conf changed - forcing reload\n");
+ libdns_reinit_pending = 1;
+ }
+
if (libdns_reinit_pending)
{
libdns_reinit_pending = 0;
diff --git a/dirmngr/dns.c b/dirmngr/dns.c
index c660cf5..8e8b6db 100644
--- a/dirmngr/dns.c
+++ b/dirmngr/dns.c
@@ -205,8 +205,10 @@ typedef int socket_fd_t;
#ifndef HAVE_STATIC_ASSERT
#if DNS_GNUC_PREREQ(0,0,0) && !DNS_GNUC_PREREQ(4,6,0)
#define HAVE_STATIC_ASSERT 0 /* glibc doesn't check GCC version */
+#elif defined(static_assert)
+#define HAVE_STATIC_ASSERT 1
#else
-#define HAVE_STATIC_ASSERT (defined static_assert)
+#define HAVE_STATIC_ASSERT 0
#endif
#endif
@@ -380,7 +382,11 @@ const char *dns_strerror(int error) {
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef HAVE___ATOMIC_FETCH_ADD
-#define HAVE___ATOMIC_FETCH_ADD (defined __ATOMIC_RELAXED)
+#ifdef __ATOMIC_RELAXED
+#define HAVE___ATOMIC_FETCH_ADD 1
+#else
+#define HAVE___ATOMIC_FETCH_ADD 0
+#endif
#endif
#ifndef HAVE___ATOMIC_FETCH_SUB
@@ -787,7 +793,11 @@ DNS_NOTUSED static size_t dns_strnlcpy(char *dst, size_t lim, const char *src, s
} /* dns_strnlcpy() */
-#define DNS_HAVE_SOCKADDR_UN (defined AF_UNIX && !defined _WIN32)
+#if (defined AF_UNIX && !defined _WIN32)
+#define DNS_HAVE_SOCKADDR_UN 1
+#else
+#define DNS_HAVE_SOCKADDR_UN 0
+#endif
static size_t dns_af_len(int af) {
static const size_t table[AF_MAX] = {
@@ -7051,11 +7061,19 @@ static void dns_socketclose(int *fd, const struct dns_options *opts) {
#endif
#ifndef HAVE_SOCK_CLOEXEC
-#define HAVE_SOCK_CLOEXEC (defined SOCK_CLOEXEC)
+#ifdef SOCK_CLOEXEC
+#define HAVE_SOCK_CLOEXEC 1
+#else
+#define HAVE_SOCK_CLOEXEC 0
+#endif
#endif
#ifndef HAVE_SOCK_NONBLOCK
-#define HAVE_SOCK_NONBLOCK (defined SOCK_NONBLOCK)
+#ifdef SOCK_NONBLOCK
+#define HAVE_SOCK_NONBLOCK 1
+#else
+#define HAVE_SOCK_NONBLOCK 0
+#endif
#endif
#define DNS_SO_MAXTRY 7
@@ -7583,14 +7601,30 @@ int dns_so_check(struct dns_socket *so) {
retry:
switch (so->state) {
case DNS_SO_UDP_INIT:
- so->state++;
+ if (so->remote.ss_family != so->local.ss_family) {
+ /* Family mismatch. Reinitialize. */
+ if ((error = dns_so_closefd(so, &so->udp)))
+ goto error;
+ if ((error = dns_so_closefd(so, &so->tcp)))
+ goto error;
+
+ /* If the user supplied an interface
+ statement, that is gone now. Sorry. */
+ memset(&so->local, 0, sizeof so->local);
+ so->local.ss_family = so->remote.ss_family;
+
+ if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, &error)))
+ goto error;
+ }
+
+ so->state++; /* FALL THROUGH */
case DNS_SO_UDP_CONN:
error = dns_connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote));
dns_trace_sys_connect(so->trace, so->udp, SOCK_DGRAM, (struct sockaddr *)&so->remote, error);
if (error)
goto error;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_UDP_SEND:
n = dns_send(so->udp, (void *)so->query->data, so->query->end, 0, &error);
dns_trace_sys_send(so->trace, so->udp, SOCK_DGRAM, so->query->data, n, error);
@@ -7600,7 +7634,7 @@ retry:
so->stat.udp.sent.bytes += n;
so->stat.udp.sent.count++;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_UDP_RECV:
n = dns_recv(so->udp, (void *)so->answer->data, so->answer->size, 0, &error);
dns_trace_sys_recv(so->trace, so->udp, SOCK_DGRAM, so->answer->data, n, error);
@@ -7614,13 +7648,26 @@ retry:
if ((error = dns_so_verify(so, so->answer)))
goto trash;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_UDP_DONE:
if (!dns_header(so->answer)->tc || so->type == SOCK_DGRAM)
return 0;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_TCP_INIT:
+ if (so->remote.ss_family != so->local.ss_family) {
+ /* Family mismatch. Reinitialize. */
+ if ((error = dns_so_closefd(so, &so->udp)))
+ goto error;
+ if ((error = dns_so_closefd(so, &so->tcp)))
+ goto error;
+
+ /* If the user supplied an interface
+ statement, that is gone now. Sorry. */
+ memset(&so->local, 0, sizeof so->local);
+ so->local.ss_family = so->remote.ss_family;
+ }
+
if (dns_so_tcp_keep(so)) {
so->state = DNS_SO_TCP_SEND;
@@ -7633,24 +7680,24 @@ retry:
if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error)))
goto error;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_TCP_CONN:
error = dns_connect(so->tcp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote));
dns_trace_sys_connect(so->trace, so->tcp, SOCK_STREAM, (struct sockaddr *)&so->remote, error);
if (error && error != DNS_EISCONN)
goto error;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_TCP_SEND:
if ((error = dns_so_tcp_send(so)))
goto error;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_TCP_RECV:
if ((error = dns_so_tcp_recv(so)))
goto error;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_TCP_DONE:
/* close unless DNS_RESCONF_TCP_ONLY (see dns_res_tcp2type) */
if (so->type != SOCK_STREAM) {
@@ -7669,7 +7716,7 @@ retry:
if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error)))
goto error;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_SOCKS_CONN: {
unsigned char method;
@@ -7697,13 +7744,13 @@ retry:
buffer[2] = method;
so->state++;
- }
+ } /* FALL THROUGH */
case DNS_SO_SOCKS_HELLO_SEND:
if ((error = dns_so_tcp_send(so)))
goto error;
dns_so_tcp_recv_expect(so, 2);
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_SOCKS_HELLO_RECV: {
unsigned char method;
@@ -7751,7 +7798,7 @@ retry:
}
so->state++;
- }
+ } /* FALL THROUGH */
case DNS_SO_SOCKS_AUTH_SEND:
if ((error = dns_so_tcp_send(so)))
goto error;
@@ -7759,7 +7806,7 @@ retry:
/* Skip the two length octets, and receive two octets. */
dns_so_tcp_recv_expect(so, 2);
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_SOCKS_AUTH_RECV:
if ((error = dns_so_tcp_recv(so)))
goto error;
@@ -7776,7 +7823,7 @@ retry:
goto error;
}
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_SOCKS_REQUEST_PREPARE:
/* Send request details (rfc-1928, 4). */
buffer = dns_so_tcp_send_buffer(so, so->remote.ss_family == AF_INET6 ? 22 : 10);
@@ -7797,7 +7844,7 @@ retry:
memcpy (buffer+8, &addr_in->sin_port, 2); /* DST.PORT */
}
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_SOCKS_REQUEST_SEND:
if ((error = dns_so_tcp_send(so)))
goto error;
@@ -7805,7 +7852,7 @@ retry:
/* Expect ten octets. This is the length of the
* response assuming a IPv4 address is used. */
dns_so_tcp_recv_expect(so, 10);
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_SOCKS_REQUEST_RECV:
if ((error = dns_so_tcp_recv(so)))
goto error;
@@ -7863,12 +7910,12 @@ retry:
* the remaining bytes assuming an IPv6 address is
* used. */
dns_so_tcp_recv_expect(so, 12);
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_SOCKS_REQUEST_RECV_V6:
if ((error = dns_so_tcp_recv(so)))
goto error;
- so->state++;
+ so->state++; /* FALL THROUGH */
case DNS_SO_SOCKS_HANDSHAKE_DONE:
/* We have not way to store the actual address used by
* the server. Then again, we don't really care. */
@@ -8072,6 +8119,8 @@ enum dns_res_state {
DNS_R_RESOLV1_NS, /* Epilog: Inspect answer */
DNS_R_FOREACH_A,
DNS_R_QUERY_A,
+ DNS_R_FOREACH_AAAA,
+ DNS_R_QUERY_AAAA,
DNS_R_CNAME0_A,
DNS_R_CNAME1_A,
@@ -8494,7 +8543,7 @@ exec:
switch (F->state) {
case DNS_R_INIT:
- F->state++;
+ F->state++; /* FALL THROUGH */
case DNS_R_GLUE:
if (R->sp == 0)
dgoto(R->sp, DNS_R_SWITCH);
@@ -8608,17 +8657,17 @@ exec:
} else if (error)
goto error;
- F->state++;
+ F->state++; /* FALL THROUGH */
case DNS_R_SUBMIT:
if ((error = R->cache->submit(F->query, R->cache)))
goto error;
- F->state++;
+ F->state++; /* FALL THROUGH */
case DNS_R_CHECK:
if ((error = R->cache->check(R->cache)))
goto error;
- F->state++;
+ F->state++; /* FALL THROUGH */
case DNS_R_FETCH:
error = 0;
@@ -8643,7 +8692,7 @@ exec:
R->search = 0;
- F->state++;
+ F->state++; /* FALL THROUGH */
case DNS_R_SEARCH:
/*
* XXX: We probably should only apply the domain search
@@ -8655,12 +8704,12 @@ exec:
if ((error = dns_q_make2(&F->query, u.name, len, R->qtype, R->qclass, F->qflags)))
goto error;
- F->state++;
+ F->state++; /* FALL THROUGH */
case DNS_R_HINTS:
if (!dns_p_setptr(&F->hints, dns_hints_query(R->hints, F->query, &error)))
goto error;
- F->state++;
+ F->state++; /* FALL THROUGH */
case DNS_R_ITERATE:
dns_rr_i_init(&F->hints_i, F->hints);
@@ -8669,7 +8718,7 @@ exec:
F->hints_i.sort = &dns_res_nameserv_cmp;
F->hints_i.args[0] = F->hints->end;
- F->state++;
+ F->state++; /* FALL THROUGH */
case DNS_R_FOREACH_NS:
dns_rr_i_save(&F->hints_i);
@@ -8731,8 +8780,22 @@ exec:
F->hints_j.section = DNS_S_ALL & ~DNS_S_QD;
if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
- if (!dns_rr_i_count(&F->hints_j))
+ if (!dns_rr_i_count(&F->hints_j)) {
+ /* Check if we have in fact servers
+ with an IPv6 address. */
+ dns_rr_i_init(&F->hints_j, F->hints);
+ F->hints_j.name = u.ns.host;
+ F->hints_j.type = DNS_T_AAAA;
+ F->hints_j.section = DNS_S_ALL & ~DNS_S_QD;
+ if (dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
+ /* We do. Reinitialize
+ iterator and handle it. */
+ dns_rr_i_init(&F->hints_j, F->hints);
+ dgoto(R->sp, DNS_R_FOREACH_AAAA);
+ }
+
dgoto(R->sp, DNS_R_RESOLV0_NS);
+ }
dgoto(R->sp, DNS_R_FOREACH_NS);
}
@@ -8761,7 +8824,7 @@ exec:
goto error;
F->state++;
- }
+ } /* FALL THROUGH */
case DNS_R_QUERY_A:
if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf))
dgoto(R->sp, DNS_R_FOREACH_A);
@@ -8833,6 +8896,139 @@ exec:
/* XXX: Should we copy F->answer to R->nodata? */
dgoto(R->sp, DNS_R_FOREACH_A);
+ case DNS_R_FOREACH_AAAA: {
+ struct dns_aaaa aaaa;
+ struct sockaddr_in6 sin6;
+
+ /*
+ * NOTE: Iterator initialized in DNS_R_FOREACH_NS because
+ * this state is re-entrant, but we need to reset
+ * .name to a valid pointer each time.
+ */
+ if ((error = dns_ns_parse(&u.ns, &F->hints_ns, F->hints)))
+ goto error;
+
+ F->hints_j.name = u.ns.host;
+ F->hints_j.type = DNS_T_AAAA;
+ F->hints_j.section = DNS_S_ALL & ~DNS_S_QD;
+
+ if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
+ if (!dns_rr_i_count(&F->hints_j)) {
+ /* Check if we have in fact servers
+ with an IPv4 address. */
+ dns_rr_i_init(&F->hints_j, F->hints);
+ F->hints_j.name = u.ns.host;
+ F->hints_j.type = DNS_T_A;
+ F->hints_j.section = DNS_S_ALL & ~DNS_S_QD;
+ if (dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
+ /* We do. Reinitialize
+ iterator and handle it. */
+ dns_rr_i_init(&F->hints_j, F->hints);
+ dgoto(R->sp, DNS_R_FOREACH_A);
+ }
+
+ dgoto(R->sp, DNS_R_RESOLV0_NS);
+ }
+
+ dgoto(R->sp, DNS_R_FOREACH_NS);
+ }
+
+ if ((error = dns_aaaa_parse(&aaaa, &rr, F->hints)))
+ goto error;
+
+ memset(&sin6, '\0', sizeof sin6); /* NB: silence valgrind */
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = aaaa.addr;
+ if (R->sp == 0)
+ sin6.sin6_port = dns_hints_port(R->hints, AF_INET, &sin6.sin6_addr);
+ else
+ sin6.sin6_port = htons(53);
+
+ if (DNS_DEBUG) {
+ char addr[INET6_ADDRSTRLEN + 1];
+ dns_aaaa_print(addr, sizeof addr, &aaaa);
+ dns_header(F->query)->qid = dns_so_mkqid(&R->so);
+ DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", u.ns.host, addr, R->sp);
+ }
+
+ dns_trace_setcname(R->trace, u.ns.host, (struct sockaddr *)&sin6);
+
+ if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin6)))
+ goto error;
+
+ F->state++;
+ } /* FALL THROUGH */
+ case DNS_R_QUERY_AAAA:
+ if (dns_so_elapsed(&R->so) >= dns_resconf_timeout(R->resconf))
+ dgoto(R->sp, DNS_R_FOREACH_AAAA);
+
+ if ((error = dns_so_check(&R->so)))
+ goto error;
+
+ if (!dns_p_setptr(&F->answer, dns_so_fetch(&R->so, &error)))
+ goto error;
+
+ if (DNS_DEBUG) {
+ DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp);
+ }
+
+ if (dns_p_rcode(F->answer) == DNS_RC_FORMERR ||
+ dns_p_rcode(F->answer) == DNS_RC_NOTIMP ||
+ dns_p_rcode(F->answer) == DNS_RC_BADVERS) {
+ /* Temporarily disable EDNS0 and try again. */
+ if (F->qflags & DNS_Q_EDNS0) {
+ F->qflags &= ~DNS_Q_EDNS0;
+ if ((error = dns_q_remake(&F->query, F->qflags)))
+ goto error;
+
+ dgoto(R->sp, DNS_R_FOREACH_AAAA);
+ }
+ }
+
+ if ((error = dns_rr_parse(&rr, 12, F->query)))
+ goto error;
+
+ if (!(len = dns_d_expand(u.name, sizeof u.name, rr.dn.p, F->query, &error)))
+ goto error;
+ else if (len >= sizeof u.name)
+ goto toolong;
+
+ dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = rr.type) {
+ dgoto(R->sp, DNS_R_FINISH); /* Found */
+ }
+
+ dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = u.name, .type = DNS_T_CNAME) {
+ F->ans_cname = rr;
+
+ dgoto(R->sp, DNS_R_CNAME0_A);
+ }
+
+ /*
+ * XXX: The condition here should probably check whether
+ * R->sp == 0, because DNS_R_SEARCH runs regardless of
+ * options.recurse. See DNS_R_BIND.
+ */
+ if (!R->resconf->options.recurse) {
+ /* Make first answer our tentative answer */
+ if (!R->nodata)
+ dns_p_movptr(&R->nodata, &F->answer);
+
+ dgoto(R->sp, DNS_R_SEARCH);
+ }
+
+ dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) {
+ dns_p_movptr(&F->hints, &F->answer);
+
+ dgoto(R->sp, DNS_R_ITERATE);
+ }
+
+ /* XXX: Should this go further up? */
+ if (dns_header(F->answer)->aa)
+ dgoto(R->sp, DNS_R_FINISH);
+
+ /* XXX: Should we copy F->answer to R->nodata? */
+
+ dgoto(R->sp, DNS_R_FOREACH_AAAA);
case DNS_R_CNAME0_A:
if (&F[1] >= endof(R->stack))
dgoto(R->sp, DNS_R_FINISH);
@@ -8864,7 +9060,7 @@ exec:
dns_rr_i_init(&R->smart, F->answer);
- F->state++;
+ F->state++; /* FALL THROUGH */
case DNS_R_SMART0_A:
if (&F[1] >= endof(R->stack))
dgoto(R->sp, DNS_R_DONE);
@@ -9549,12 +9745,12 @@ exec:
switch (ai->state) {
case DNS_AI_S_INIT:
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_NEXTAF:
if (!dns_ai_nextaf(ai))
dns_ai_goto(DNS_AI_S_DONE);
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_NUMERIC:
if (1 == dns_inet_pton(AF_INET, ai->qname, &any.a)) {
if (ai->af.atype == AF_INET) {
@@ -9577,19 +9773,19 @@ exec:
if (ai->hints.ai_flags & AI_NUMERICHOST)
dns_ai_goto(DNS_AI_S_NEXTAF);
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_SUBMIT:
assert(ai->res);
if ((error = dns_res_submit(ai->res, ai->qname, dns_ai_qtype(ai), DNS_C_IN)))
return error;
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_CHECK:
if ((error = dns_res_check(ai->res)))
return error;
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_FETCH:
if (!(ans = dns_res_fetch_and_study(ai->res, &error)))
return error;
@@ -9612,7 +9808,7 @@ exec:
ai->i.type = dns_ai_qtype(ai);
ai->i.sort = &dns_rr_i_order;
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_FOREACH_I:
if (!dns_rr_grep(&rr, 1, &ai->i, ai->answer, &error))
dns_ai_goto(DNS_AI_S_NEXTAF);
@@ -9646,11 +9842,11 @@ exec:
break;
} /* switch() */
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_INIT_G:
ai->g_depth = 0;
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_ITERATE_G:
dns_strlcpy(ai->g_cname, ai->cname, sizeof ai->g_cname);
dns_rr_i_init(&ai->g, ai->glue);
@@ -9658,7 +9854,7 @@ exec:
ai->g.name = ai->g_cname;
ai->g.type = ai->af.qtype;
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_FOREACH_G:
if (!dns_rr_grep(&rr, 1, &ai->g, ai->glue, &error)) {
if (dns_rr_i_count(&ai->g) > 0)
@@ -9682,12 +9878,12 @@ exec:
if ((error = dns_res_submit(ai->res, ai->g.name, ai->g.type, DNS_C_IN)))
return error;
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_CHECK_G:
if ((error = dns_res_check(ai->res)))
return error;
- ai->state++;
+ ai->state++; /* FALL THROUGH */
case DNS_AI_S_FETCH_G:
if (!(ans = dns_res_fetch_and_study(ai->res, &error)))
return error;
diff --git a/dirmngr/http.c b/dirmngr/http.c
index f461e5d..0bedba0 100644
--- a/dirmngr/http.c
+++ b/dirmngr/http.c
@@ -70,6 +70,7 @@
# include <sys/socket.h>
# include <sys/time.h>
# include <time.h>
+# include <fcntl.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
@@ -153,13 +154,14 @@ static int insert_escapes (char *buffer, const char *string,
static uri_tuple_t parse_tuple (char *string);
static gpg_error_t send_request (http_t hd, const char *httphost,
const char *auth,const char *proxy,
- const char *srvtag,strlist_t headers);
+ const char *srvtag, unsigned int timeout,
+ strlist_t headers);
static char *build_rel_path (parsed_uri_t uri);
static gpg_error_t parse_response (http_t hd);
static gpg_error_t connect_server (const char *server, unsigned short port,
unsigned int flags, const char *srvtag,
- assuan_fd_t *r_sock);
+ unsigned int timeout, assuan_fd_t *r_sock);
static gpgrt_ssize_t read_server (assuan_fd_t sock, void *buffer, size_t size);
static gpg_error_t write_server (assuan_fd_t sock, const char *data, size_t length);
@@ -259,6 +261,9 @@ struct http_session_s
/* A per-session TLS verification callback. */
http_verify_cb_t verify_cb;
void *verify_cb_value;
+
+ /* The connect timeout */
+ unsigned int connect_timeout;
};
@@ -695,6 +700,7 @@ http_session_new (http_session_t *r_session,
sess->flags = flags;
sess->verify_cb = verify_cb;
sess->verify_cb_value = verify_cb_value;
+ sess->connect_timeout = 0;
#if HTTP_USE_NTBTLS
{
@@ -867,6 +873,15 @@ http_session_set_log_cb (http_session_t sess,
}
+/* Set the TIMEOUT in milliseconds for the connection's connect
+ * calls. Using 0 disables the timeout. */
+void
+http_session_set_timeout (http_session_t sess, unsigned int timeout)
+{
+ sess->connect_timeout = timeout;
+}
+
+
/* Start a HTTP retrieval and on success store at R_HD a context
@@ -898,7 +913,9 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url,
err = parse_uri (&hd->uri, url, 0, !!(flags & HTTP_FLAG_FORCE_TLS));
if (!err)
- err = send_request (hd, httphost, auth, proxy, srvtag, headers);
+ err = send_request (hd, httphost, auth, proxy, srvtag,
+ hd->session? hd->session->connect_timeout : 0,
+ headers);
if (err)
{
@@ -918,10 +935,10 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url,
/* This function is useful to connect to a generic TCP service using
this http abstraction layer. This has the advantage of providing
- service tags and an estream interface. */
+ service tags and an estream interface. TIMEOUT is in milliseconds. */
gpg_error_t
http_raw_connect (http_t *r_hd, const char *server, unsigned short port,
- unsigned int flags, const char *srvtag)
+ unsigned int flags, const char *srvtag, unsigned int timeout)
{
gpg_error_t err = 0;
http_t hd;
@@ -938,6 +955,10 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port,
log_error ("Tor support is not available\n");
return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
}
+ /* Non-blocking connects do not work with our Tor proxy because
+ * we can't continue the Socks protocol after the EINPROGRESS.
+ * Disable the timeout to use a blocking connect. */
+ timeout = 0;
}
/* Create the handle. */
@@ -952,7 +973,7 @@ http_raw_connect (http_t *r_hd, const char *server, unsigned short port,
{
assuan_fd_t sock;
- err = connect_server (server, port, hd->flags, srvtag, &sock);
+ err = connect_server (server, port, hd->flags, srvtag, timeout, &sock);
if (err)
{
xfree (hd);
@@ -1047,6 +1068,7 @@ http_wait_response (http_t hd)
{
gpg_error_t err;
cookie_t cookie;
+ int use_tls;
/* Make sure that we are in the data. */
http_start_data (hd);
@@ -1057,6 +1079,7 @@ http_wait_response (http_t hd)
if (!cookie)
return gpg_err_make (default_errsource, GPG_ERR_INTERNAL);
+ use_tls = cookie->use_tls;
es_fclose (hd->fp_write);
hd->fp_write = NULL;
/* The close has released the cookie and thus we better set it to NULL. */
@@ -1075,7 +1098,7 @@ http_wait_response (http_t hd)
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
cookie->sock = my_socket_ref (hd->sock);
cookie->session = http_session_ref (hd->session);
- cookie->use_tls = hd->uri->use_tls;
+ cookie->use_tls = use_tls;
hd->read_cookie = cookie;
hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
@@ -1202,14 +1225,16 @@ parse_uri (parsed_uri_t *ret_uri, const char *uri,
{
gpg_err_code_t ec;
- *ret_uri = xtrycalloc (1, sizeof **ret_uri + strlen (uri));
+ *ret_uri = xtrycalloc (1, sizeof **ret_uri + 2 * strlen (uri) + 1);
if (!*ret_uri)
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
strcpy ((*ret_uri)->buffer, uri);
+ strcpy ((*ret_uri)->buffer + strlen (uri) + 1, uri);
+ (*ret_uri)->original = (*ret_uri)->buffer + strlen (uri) + 1;
ec = do_parse_uri (*ret_uri, 0, no_scheme_check, force_tls);
if (ec)
{
- xfree (*ret_uri);
+ http_release_parsed_uri (*ret_uri);
*ret_uri = NULL;
}
return gpg_err_make (default_errsource, ec);
@@ -1238,6 +1263,11 @@ http_release_parsed_uri (parsed_uri_t uri)
{
uri_tuple_t r, r2;
+ for (r = uri->params; r; r = r2)
+ {
+ r2 = r->next;
+ xfree (r);
+ }
for (r = uri->query; r; r = r2)
{
r2 = r->next;
@@ -1635,7 +1665,8 @@ is_hostname_port (const char *string)
*/
static gpg_error_t
send_request (http_t hd, const char *httphost, const char *auth,
- const char *proxy, const char *srvtag, strlist_t headers)
+ const char *proxy, const char *srvtag, unsigned int timeout,
+ strlist_t headers)
{
gpg_error_t err;
const char *server;
@@ -1645,6 +1676,9 @@ send_request (http_t hd, const char *httphost, const char *auth,
char *proxy_authstr = NULL;
char *authstr = NULL;
assuan_fd_t sock;
+#ifdef USE_TLS
+ int have_http_proxy = 0;
+#endif
if (hd->uri->use_tls && !hd->session)
{
@@ -1668,6 +1702,10 @@ send_request (http_t hd, const char *httphost, const char *auth,
log_error ("Tor support is not available\n");
return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
}
+ /* Non-blocking connects do not work with our Tor proxy because
+ * we can't continue the Socks protocol after the EINPROGRESS.
+ * Disable the timeout to use a blocking connect. */
+ timeout = 0;
}
server = *hd->uri->host ? hd->uri->host : "localhost";
@@ -1731,9 +1769,12 @@ send_request (http_t hd, const char *httphost, const char *auth,
if (err)
;
- else if (!strcmp (uri->scheme, "http") || !strcmp (uri->scheme, "socks4"))
- ;
- else if (!strcmp (uri->scheme, "socks5h"))
+#ifdef USE_TLS
+ else if (!strcmp (uri->scheme, "http"))
+ have_http_proxy = 1;
+#endif
+ else if (!strcmp (uri->scheme, "socks4")
+ || !strcmp (uri->scheme, "socks5h"))
err = gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
else
err = gpg_err_make (default_errsource, GPG_ERR_INV_URI);
@@ -1762,12 +1803,12 @@ send_request (http_t hd, const char *httphost, const char *auth,
err = connect_server (*uri->host ? uri->host : "localhost",
uri->port ? uri->port : 80,
- hd->flags, srvtag, &sock);
+ hd->flags, NULL, timeout, &sock);
http_release_parsed_uri (uri);
}
else
{
- err = connect_server (server, port, hd->flags, srvtag, &sock);
+ err = connect_server (server, port, hd->flags, srvtag, timeout, &sock);
}
if (err)
@@ -1782,6 +1823,94 @@ send_request (http_t hd, const char *httphost, const char *auth,
return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
}
+#if USE_TLS
+ if (have_http_proxy && hd->uri->use_tls)
+ {
+ int saved_flags;
+ cookie_t cookie;
+
+ /* Try to use the CONNECT method to proxy our TLS stream. */
+ request = es_bsprintf
+ ("CONNECT %s:%hu HTTP/1.0\r\nHost: %s:%hu\r\n%s",
+ httphost ? httphost : server,
+ port,
+ httphost ? httphost : server,
+ port,
+ proxy_authstr ? proxy_authstr : "");
+ xfree (proxy_authstr);
+ proxy_authstr = NULL;
+
+ if (! request)
+ return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+
+ if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
+ log_debug_with_string (request, "http.c:request:");
+
+ cookie = xtrycalloc (1, sizeof *cookie);
+ if (! cookie)
+ {
+ err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+ xfree (request);
+ return err;
+ }
+ cookie->sock = my_socket_ref (hd->sock);
+ hd->write_cookie = cookie;
+
+ hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
+ if (! hd->fp_write)
+ {
+ err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+ my_socket_unref (cookie->sock, NULL, NULL);
+ xfree (cookie);
+ xfree (request);
+ hd->write_cookie = NULL;
+ return err;
+ }
+ else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
+ err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+
+ xfree (request);
+ request = NULL;
+
+ /* Make sure http_wait_response doesn't close the stream. */
+ saved_flags = hd->flags;
+ hd->flags &= ~HTTP_FLAG_SHUTDOWN;
+
+ /* Get the response. */
+ err = http_wait_response (hd);
+
+ /* Restore flags, destroy stream. */
+ hd->flags = saved_flags;
+ es_fclose (hd->fp_read);
+ hd->fp_read = NULL;
+ hd->read_cookie = NULL;
+
+ /* Reset state. */
+ hd->in_data = 0;
+
+ if (err)
+ return err;
+
+ if (hd->status_code != 200)
+ {
+ request = es_bsprintf
+ ("CONNECT %s:%hu",
+ httphost ? httphost : server,
+ port);
+
+ log_error (_("error accessing '%s': http status %u\n"),
+ request ? request : "out of core",
+ http_get_status_code (hd));
+
+ xfree (request);
+ return gpg_error (GPG_ERR_NO_DATA);
+ }
+
+ /* We are done with the proxy, the code below will establish a
+ * TLS session and talk directly to the target server. */
+ http_proxy = NULL;
+ }
+#endif /* USE_TLS */
#if HTTP_USE_NTBTLS
if (hd->uri->use_tls)
@@ -2316,7 +2445,7 @@ parse_response (http_t hd)
if (!len)
return GPG_ERR_EOF;
- if ((hd->flags & HTTP_FLAG_LOG_RESP))
+ if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
log_debug_with_string (line, "http.c:response:\n");
}
while (!*line);
@@ -2361,7 +2490,7 @@ parse_response (http_t hd)
/* Trim line endings of empty lines. */
if ((*line == '\r' && line[1] == '\n') || *line == '\n')
*line = 0;
- if ((hd->flags & HTTP_FLAG_LOG_RESP))
+ if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
log_info ("http.c:RESP: '%.*s'\n",
(int)strlen(line)-(*line&&line[1]?2:0),line);
if (*line)
@@ -2526,13 +2655,162 @@ my_sock_new_for_addr (struct sockaddr_storage *addr, int type, int proto)
}
+/* Call WSAGetLastError and map it to a libgpg-error. */
+#ifdef HAVE_W32_SYSTEM
+static gpg_error_t
+my_wsagetlasterror (void)
+{
+ int wsaerr;
+ gpg_err_code_t ec;
+
+ wsaerr = WSAGetLastError ();
+ switch (wsaerr)
+ {
+ case WSAENOTSOCK: ec = GPG_ERR_EINVAL; break;
+ case WSAEWOULDBLOCK: ec = GPG_ERR_EAGAIN; break;
+ case ERROR_BROKEN_PIPE: ec = GPG_ERR_EPIPE; break;
+ case WSANOTINITIALISED: ec = GPG_ERR_ENOSYS; break;
+ case WSAENOBUFS: ec = GPG_ERR_ENOBUFS; break;
+ case WSAEMSGSIZE: ec = GPG_ERR_EMSGSIZE; break;
+ case WSAECONNREFUSED: ec = GPG_ERR_ECONNREFUSED; break;
+ case WSAEISCONN: ec = GPG_ERR_EISCONN; break;
+ case WSAEALREADY: ec = GPG_ERR_EALREADY; break;
+ case WSAETIMEDOUT: ec = GPG_ERR_ETIMEDOUT; break;
+ default: ec = GPG_ERR_EIO; break;
+ }
+
+ return gpg_err_make (default_errsource, ec);
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Connect SOCK and return GPG_ERR_ETIMEOUT if a connection could not
+ * be established within TIMEOUT milliseconds. 0 indicates the
+ * system's default timeout. The other args are the usual connect
+ * args. On success 0 is returned, on timeout GPG_ERR_ETIMEDOUT, and
+ * another error code for other errors. On timeout the caller needs
+ * to close the socket as soon as possible to stop an ongoing
+ * handshake.
+ *
+ * This implementation is for well-behaving systems; see Stevens,
+ * Network Programming, 2nd edition, Vol 1, 15.4. */
+static gpg_error_t
+connect_with_timeout (assuan_fd_t sock,
+ struct sockaddr *addr, int addrlen,
+ unsigned int timeout)
+{
+ gpg_error_t err;
+ int syserr;
+ socklen_t slen;
+ fd_set rset, wset;
+ struct timeval tval;
+ int n;
+
+#ifndef HAVE_W32_SYSTEM
+ int oflags;
+# define RESTORE_BLOCKING() do { \
+ fcntl (sock, F_SETFL, oflags); \
+ } while (0)
+#else /*HAVE_W32_SYSTEM*/
+# define RESTORE_BLOCKING() do { \
+ unsigned long along = 0; \
+ ioctlsocket (FD2INT (sock), FIONBIO, &along); \
+ } while (0)
+#endif /*HAVE_W32_SYSTEM*/
+
+
+ if (!timeout)
+ {
+ /* Shortcut. */
+ if (assuan_sock_connect (sock, addr, addrlen))
+ err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+ else
+ err = 0;
+ return err;
+ }
+
+ /* Switch the socket into non-blocking mode. */
+#ifdef HAVE_W32_SYSTEM
+ {
+ unsigned long along = 1;
+ if (ioctlsocket (FD2INT (sock), FIONBIO, &along))
+ return my_wsagetlasterror ();
+ }
+#else
+ oflags = fcntl (sock, F_GETFL, 0);
+ if (fcntl (sock, F_SETFL, oflags | O_NONBLOCK))
+ return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+#endif
+
+ /* Do the connect. */
+ if (!assuan_sock_connect (sock, addr, addrlen))
+ {
+ /* Immediate connect. Restore flags. */
+ RESTORE_BLOCKING ();
+ return 0; /* Success. */
+ }
+ err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+ if (gpg_err_code (err) != GPG_ERR_EINPROGRESS)
+ {
+ RESTORE_BLOCKING ();
+ return err;
+ }
+
+ FD_ZERO (&rset);
+ FD_SET (FD2INT (sock), &rset);
+ wset = rset;
+ tval.tv_sec = timeout / 1000;
+ tval.tv_usec = (timeout % 1000) * 1000;
+
+ n = my_select (FD2INT(sock)+1, &rset, &wset, NULL, &tval);
+ if (n < 0)
+ {
+ err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+ RESTORE_BLOCKING ();
+ return err;
+ }
+ if (!n)
+ {
+ /* Timeout: We do not restore the socket flags on timeout
+ * because the caller is expected to close the socket. */
+ return gpg_err_make (default_errsource, GPG_ERR_ETIMEDOUT);
+ }
+ if (!FD_ISSET (sock, &rset) && !FD_ISSET (sock, &wset))
+ {
+ /* select misbehaved. */
+ return gpg_err_make (default_errsource, GPG_ERR_SYSTEM_BUG);
+ }
+
+ slen = sizeof (syserr);
+ if (getsockopt (FD2INT(sock), SOL_SOCKET, SO_ERROR,
+ (void*)&syserr, &slen) < 0)
+ {
+ /* Assume that this is Solaris which returns the error in ERRNO. */
+ err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+ }
+ else if (syserr)
+ err = gpg_err_make (default_errsource, gpg_err_code_from_errno (syserr));
+ else
+ err = 0; /* Connected. */
+
+ RESTORE_BLOCKING ();
+
+ return err;
+
+#undef RESTORE_BLOCKING
+}
+
+
/* Actually connect to a server. On success 0 is returned and the
* file descriptor for the socket is stored at R_SOCK; on error an
- * error code is returned and ASSUAN_INVALID_FD is stored at
- * R_SOCK. */
+ * error code is returned and ASSUAN_INVALID_FD is stored at R_SOCK.
+ * TIMEOUT is the connect timeout in milliseconds. Note that the
+ * function tries to connect to all known addresses and the timeout is
+ * for each one. */
static gpg_error_t
connect_server (const char *server, unsigned short port,
- unsigned int flags, const char *srvtag, assuan_fd_t *r_sock)
+ unsigned int flags, const char *srvtag, unsigned int timeout,
+ assuan_fd_t *r_sock)
{
gpg_error_t err;
assuan_fd_t sock = ASSUAN_INVALID_FD;
@@ -2645,11 +2923,11 @@ connect_server (const char *server, unsigned short port,
}
anyhostaddr = 1;
- if (assuan_sock_connect (sock, (struct sockaddr *)ai->addr,
- ai->addrlen))
+ err = connect_with_timeout (sock, (struct sockaddr *)ai->addr,
+ ai->addrlen, timeout);
+ if (err)
{
- last_err = gpg_err_make (default_errsource,
- gpg_err_code_from_syserror ());
+ last_err = err;
}
else
{
@@ -3016,7 +3294,7 @@ gpg_error_t
http_verify_server_credentials (http_session_t sess)
{
#if HTTP_USE_GNUTLS
- static const char const errprefix[] = "TLS verification of peer failed";
+ static const char errprefix[] = "TLS verification of peer failed";
int rc;
unsigned int status;
const char *hostname;
diff --git a/dirmngr/http.h b/dirmngr/http.h
index 2609b9e..9fa462c 100644
--- a/dirmngr/http.h
+++ b/dirmngr/http.h
@@ -47,6 +47,7 @@ typedef struct uri_tuple_s *uri_tuple_t;
struct parsed_uri_s
{
/* All these pointers point into BUFFER; most stuff is not escaped. */
+ char *original; /* Unmodified copy of the parsed URI. */
char *scheme; /* Pointer to the scheme string (always lowercase). */
unsigned int is_http:1; /* This is a HTTP style URI. */
unsigned int use_tls:1; /* Whether TLS should be used. */
@@ -124,6 +125,7 @@ void http_session_set_log_cb (http_session_t sess,
void (*cb)(http_session_t, gpg_error_t,
const char *,
const void **, size_t *));
+void http_session_set_timeout (http_session_t sess, unsigned int timeout);
gpg_error_t http_parse_uri (parsed_uri_t *ret_uri, const char *uri,
@@ -133,7 +135,8 @@ void http_release_parsed_uri (parsed_uri_t uri);
gpg_error_t http_raw_connect (http_t *r_hd,
const char *server, unsigned short port,
- unsigned int flags, const char *srvtag);
+ unsigned int flags, const char *srvtag,
+ unsigned int timeout);
gpg_error_t http_open (http_t *r_hd, http_req_t reqtype,
const char *url,
diff --git a/dirmngr/ks-action.c b/dirmngr/ks-action.c
index 1087bb5..857aab1 100644
--- a/dirmngr/ks-action.c
+++ b/dirmngr/ks-action.c
@@ -232,7 +232,10 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
Need to think about a better strategy. */
for (uri = keyservers; !err && uri; uri = uri->next)
{
- int is_http = uri->parsed_uri->is_http;
+ int is_hkp_s = (strcmp (uri->parsed_uri->scheme, "hkp") == 0
+ || strcmp (uri->parsed_uri->scheme, "hkps") == 0);
+ int is_http_s = (strcmp (uri->parsed_uri->scheme, "http") == 0
+ || strcmp (uri->parsed_uri->scheme, "https") == 0);
int is_ldap = 0;
#if USE_LDAP
@@ -241,7 +244,7 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
|| strcmp (uri->parsed_uri->scheme, "ldapi") == 0);
#endif
- if (is_http || is_ldap)
+ if (is_hkp_s || is_http_s || is_ldap)
{
any_server = 1;
for (sl = patterns; !err && sl; sl = sl->next)
@@ -251,9 +254,12 @@ ks_action_get (ctrl_t ctrl, uri_item_t keyservers,
err = ks_ldap_get (ctrl, uri->parsed_uri, sl->d, &infp);
else
#endif
- {
- err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
- }
+ if (is_hkp_s)
+ err = ks_hkp_get (ctrl, uri->parsed_uri, sl->d, &infp);
+ else if (is_http_s)
+ err = ks_http_fetch (ctrl, uri->parsed_uri->original, &infp);
+ else
+ BUG ();
if (err)
{
diff --git a/dirmngr/ks-engine-finger.c b/dirmngr/ks-engine-finger.c
index f56a9ff..e53a0ee 100644
--- a/dirmngr/ks-engine-finger.c
+++ b/dirmngr/ks-engine-finger.c
@@ -86,7 +86,7 @@ ks_finger_fetch (ctrl_t ctrl, parsed_uri_t uri, estream_t *r_fp)
((dirmngr_use_tor ()? HTTP_FLAG_FORCE_TOR : 0)
| (opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4 : 0)
| (opt.disable_ipv6? HTTP_FLAG_IGNORE_IPv6 : 0)),
- NULL);
+ NULL, ctrl->timeout);
if (err)
{
xfree (name);
diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c
index ddb8549..4a0b08f 100644
--- a/dirmngr/ks-engine-hkp.c
+++ b/dirmngr/ks-engine-hkp.c
@@ -67,6 +67,8 @@
/* Number of retries done for a dead host etc. */
#define SEND_REQUEST_RETRIES 3
+enum ks_protocol { KS_PROTOCOL_HKP, KS_PROTOCOL_HKPS, KS_PROTOCOL_MAX };
+
/* Objects used to maintain information about hosts. */
struct hostinfo_s;
typedef struct hostinfo_s *hostinfo_t;
@@ -74,9 +76,11 @@ struct hostinfo_s
{
time_t lastfail; /* Time we tried to connect and failed. */
time_t lastused; /* Time of last use. */
- int *pool; /* A -1 terminated array with indices into
- HOSTTABLE or NULL if NAME is not a pool
- name. */
+ int *pool; /* An array with indices into HOSTTABLE or NULL
+ if NAME is not a pool name. */
+ size_t pool_len; /* Length of POOL. */
+ size_t pool_size; /* Allocated size of POOL. */
+#define MAX_POOL_SIZE 128
int poolidx; /* Index into POOL with the used host. -1 if not set. */
unsigned int v4:1; /* Host supports AF_INET. */
unsigned int v6:1; /* Host supports AF_INET6. */
@@ -84,12 +88,18 @@ struct hostinfo_s
unsigned int dead:1; /* Host is currently unresponsive. */
unsigned int iporname_valid:1; /* The field IPORNAME below is valid */
/* (but may be NULL) */
+ unsigned int did_a_lookup:1; /* Have we done an A lookup yet? */
+ unsigned int did_srv_lookup:2; /* One bit per protocol indicating
+ whether we already did a SRV
+ lookup. */
time_t died_at; /* The time the host was marked dead. If this is
0 the host has been manually marked dead. */
char *cname; /* Canonical name of the host. Only set if this
is a pool or NAME has a numerical IP address. */
char *iporname; /* Numeric IP address or name for printing. */
- unsigned short port; /* The port used by the host, 0 if unknown. */
+ unsigned short port[KS_PROTOCOL_MAX];
+ /* The port used by the host for all protocols, 0
+ if unknown. */
char name[1]; /* The hostname. */
};
@@ -118,6 +128,8 @@ create_new_hostinfo (const char *name)
return -1;
strcpy (hi->name, name);
hi->pool = NULL;
+ hi->pool_len = 0;
+ hi->pool_size = 0;
hi->poolidx = -1;
hi->lastused = (time_t)(-1);
hi->lastfail = (time_t)(-1);
@@ -125,11 +137,14 @@ create_new_hostinfo (const char *name)
hi->v6 = 0;
hi->onion = 0;
hi->dead = 0;
+ hi->did_a_lookup = 0;
+ hi->did_srv_lookup = 0;
hi->iporname_valid = 0;
hi->died_at = 0;
hi->cname = NULL;
hi->iporname = NULL;
- hi->port = 0;
+ hi->port[KS_PROTOCOL_HKP] = 0;
+ hi->port[KS_PROTOCOL_HKPS] = 0;
/* Add it to the hosttable. */
for (idx=0; idx < hosttable_size; idx++)
@@ -187,24 +202,24 @@ sort_hostpool (const void *xa, const void *xb)
}
-/* Return true if the host with the hosttable index TBLIDX is in POOL. */
+/* Return true if the host with the hosttable index TBLIDX is in HI->pool. */
static int
-host_in_pool_p (int *pool, int tblidx)
+host_in_pool_p (hostinfo_t hi, int tblidx)
{
int i, pidx;
- for (i=0; (pidx = pool[i]) != -1; i++)
+ for (i = 0; i < hi->pool_len && (pidx = hi->pool[i]) != -1; i++)
if (pidx == tblidx && hosttable[pidx])
return 1;
return 0;
}
-/* Select a random host. Consult TABLE which indices into the global
- hosttable. Returns index into TABLE or -1 if no host could be
+/* Select a random host. Consult HI->pool which indices into the global
+ hosttable. Returns index into HI->pool or -1 if no host could be
selected. */
static int
-select_random_host (int *table)
+select_random_host (hostinfo_t hi)
{
int *tbl;
size_t tblsize;
@@ -212,7 +227,9 @@ select_random_host (int *table)
/* We create a new table so that we randomly select only from
currently alive hosts. */
- for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++)
+ for (idx = 0, tblsize = 0;
+ idx < hi->pool_len && (pidx = hi->pool[idx]) != -1;
+ idx++)
if (hosttable[pidx] && !hosttable[pidx]->dead)
tblsize++;
if (!tblsize)
@@ -221,7 +238,9 @@ select_random_host (int *table)
tbl = xtrymalloc (tblsize * sizeof *tbl);
if (!tbl)
return -1;
- for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++)
+ for (idx = 0, tblsize = 0;
+ idx < hi->pool_len && (pidx = hi->pool[idx]) != -1;
+ idx++)
if (hosttable[pidx] && !hosttable[pidx]->dead)
tbl[tblsize++] = pidx;
@@ -255,7 +274,7 @@ arecords_is_pool (dns_addrinfo_t aibuf)
}
-/* Print a warninng iff Tor is not running but Tor has been requested.
+/* Print a warning iff Tor is not running but Tor has been requested.
* Also return true if it is not running. */
static int
tor_not_running_p (ctrl_t ctrl)
@@ -281,20 +300,22 @@ tor_not_running_p (ctrl_t ctrl)
/* Add the host AI under the NAME into the HOSTTABLE. If PORT is not
- zero, it specifies which port to use to talk to the host. If NAME
- specifies a pool (as indicated by IS_POOL), update the given
- reference table accordingly. */
+ zero, it specifies which port to use to talk to the host for
+ PROTOCOL. If NAME specifies a pool (as indicated by IS_POOL),
+ update the given reference table accordingly. */
static void
add_host (const char *name, int is_pool,
- const dns_addrinfo_t ai, unsigned short port,
- int *reftbl, size_t reftblsize, int *refidx)
+ const dns_addrinfo_t ai,
+ enum ks_protocol protocol, unsigned short port)
{
gpg_error_t tmperr;
char *tmphost;
int idx, tmpidx;
+ hostinfo_t host;
int i;
idx = find_hostinfo (name);
+ host = hosttable[idx];
if (is_pool)
{
@@ -325,7 +346,7 @@ add_host (const char *name, int is_pool,
log_info ("resolve_dns_addr failed while checking '%s': %s\n",
name, gpg_strerror (tmperr));
}
- else if ((*refidx) + 1 >= reftblsize)
+ else if (host->pool_len + 1 >= MAX_POOL_SIZE)
{
log_error ("resolve_dns_addr for '%s': '%s'"
" [index table full - ignored]\n", name, tmphost);
@@ -352,7 +373,7 @@ add_host (const char *name, int is_pool,
else /* Set or update the entry. */
{
if (port)
- hosttable[tmpidx]->port = port;
+ hosttable[tmpidx]->port[protocol] = port;
if (ai->family == AF_INET6)
{
@@ -365,17 +386,54 @@ add_host (const char *name, int is_pool,
else
BUG ();
- for (i=0; i < *refidx; i++)
- if (reftbl[i] == tmpidx)
- break;
- if (!(i < *refidx) && tmpidx != idx)
- reftbl[(*refidx)++] = tmpidx;
+ /* If we updated the main entry, we're done. */
+ if (idx == tmpidx)
+ goto leave;
+
+ /* If we updated an existing entry, we're done. */
+ for (i = 0; i < host->pool_len; i++)
+ if (host->pool[i] == tmpidx)
+ goto leave;
+
+ /* Otherwise, we need to add it to the pool. Check if there
+ is space. */
+ if (host->pool_len + 1 > host->pool_size)
+ {
+ int *new_pool;
+ size_t new_size;
+
+ if (host->pool_size == 0)
+ new_size = 4;
+ else
+ new_size = host->pool_size * 2;
+
+ new_pool = xtryrealloc (host->pool,
+ new_size * sizeof *new_pool);
+
+ if (new_pool == NULL)
+ goto leave;
+
+ host->pool = new_pool;
+ host->pool_size = new_size;
+ }
+
+ /* Finally, add it. */
+ log_assert (host->pool_len < host->pool_size);
+ host->pool[host->pool_len++] = tmpidx;
}
}
+ leave:
xfree (tmphost);
}
+/* Sort the pool of the given hostinfo HI. */
+static void
+hostinfo_sort_pool (hostinfo_t hi)
+{
+ qsort (hi->pool, hi->pool_len, sizeof *hi->pool, sort_hostpool);
+}
+
/* Map the host name NAME to the actual to be used host name. This
* allows us to manage round robin DNS names. We use our own strategy
* to choose one of the hosts. For example we skip those hosts which
@@ -393,12 +451,16 @@ add_host (const char *name, int is_pool,
* pool. */
static gpg_error_t
map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect,
- char **r_host, char *r_portstr,
+ enum ks_protocol protocol, char **r_host, char *r_portstr,
unsigned int *r_httpflags, char **r_httphost)
{
gpg_error_t err = 0;
hostinfo_t hi;
int idx;
+ dns_addrinfo_t aibuf, ai;
+ int is_pool;
+ int new_hosts = 0;
+ char *cname;
*r_host = NULL;
if (r_httpflags)
@@ -415,74 +477,62 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect,
/* See whether the host is in our table. */
idx = find_hostinfo (name);
- if (idx == -1 && is_onion_address (name))
+ if (idx == -1)
{
idx = create_new_hostinfo (name);
if (idx == -1)
return gpg_error_from_syserror ();
hi = hosttable[idx];
- hi->onion = 1;
+ hi->onion = is_onion_address (name);
}
- else if (idx == -1)
+ else
+ hi = hosttable[idx];
+
+ is_pool = hi->pool != NULL;
+
+ if (srvtag && !is_ip_address (name)
+ && ! hi->onion
+ && ! (hi->did_srv_lookup & 1 << protocol))
{
- /* We never saw this host. Allocate a new entry. */
- dns_addrinfo_t aibuf, ai;
- int *reftbl;
- size_t reftblsize;
- int refidx;
- int is_pool = 0;
- char *cname;
struct srventry *srvs;
unsigned int srvscount;
- reftblsize = 100;
- reftbl = xtrymalloc (reftblsize * sizeof *reftbl);
- if (!reftbl)
- return gpg_error_from_syserror ();
- refidx = 0;
-
- idx = create_new_hostinfo (name);
- if (idx == -1)
+ /* Check for SRV records. */
+ err = get_dns_srv (name, srvtag, NULL, &srvs, &srvscount);
+ if (err)
{
- err = gpg_error_from_syserror ();
- xfree (reftbl);
+ if (gpg_err_code (err) == GPG_ERR_ECONNREFUSED)
+ tor_not_running_p (ctrl);
return err;
}
- hi = hosttable[idx];
- if (srvtag && !is_ip_address (name))
+ if (srvscount > 0)
{
- /* Check for SRV records. */
- err = get_dns_srv (name, srvtag, NULL, &srvs, &srvscount);
- if (err)
- {
- xfree (reftbl);
- if (gpg_err_code (err) == GPG_ERR_ECONNREFUSED)
- tor_not_running_p (ctrl);
- return err;
- }
+ int i;
+ if (! is_pool)
+ is_pool = srvscount > 1;
- if (srvscount > 0)
+ for (i = 0; i < srvscount; i++)
{
- int i;
- is_pool = srvscount > 1;
-
- for (i = 0; i < srvscount; i++)
- {
- err = resolve_dns_name (srvs[i].target, 0,
- AF_UNSPEC, SOCK_STREAM,
- &ai, &cname);
- if (err)
- continue;
- dirmngr_tick (ctrl);
- add_host (name, is_pool, ai, srvs[i].port,
- reftbl, reftblsize, &refidx);
- }
-
- xfree (srvs);
+ err = resolve_dns_name (srvs[i].target, 0,
+ AF_UNSPEC, SOCK_STREAM,
+ &ai, &cname);
+ if (err)
+ continue;
+ dirmngr_tick (ctrl);
+ add_host (name, is_pool, ai, protocol, srvs[i].port);
+ new_hosts = 1;
}
+
+ xfree (srvs);
}
+ hi->did_srv_lookup |= 1 << protocol;
+ }
+
+ if (! hi->did_a_lookup
+ && ! hi->onion)
+ {
/* Find all A records for this entry and put them into the pool
list - if any. */
err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname);
@@ -516,32 +566,18 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect,
continue;
dirmngr_tick (ctrl);
- add_host (name, is_pool, ai, 0, reftbl, reftblsize, &refidx);
+ add_host (name, is_pool, ai, 0, 0);
+ new_hosts = 1;
}
+
+ hi->did_a_lookup = 1;
}
- reftbl[refidx] = -1;
xfree (cname);
free_dns_addrinfo (aibuf);
-
- if (refidx && is_pool)
- {
- assert (!hi->pool);
- hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl);
- if (!hi->pool)
- {
- err = gpg_error_from_syserror ();
- log_error ("shrinking index table in map_host failed: %s\n",
- gpg_strerror (err));
- xfree (reftbl);
- return err;
- }
- qsort (hi->pool, refidx, sizeof *reftbl, sort_hostpool);
- }
- else
- xfree (reftbl);
}
+ if (new_hosts)
+ hostinfo_sort_pool (hi);
- hi = hosttable[idx];
if (hi->pool)
{
/* Deal with the pool name before selecting a host. */
@@ -563,7 +599,7 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect,
/* Select a host if needed. */
if (hi->poolidx == -1)
{
- hi->poolidx = select_random_host (hi->pool);
+ hi->poolidx = select_random_host (hi);
if (hi->poolidx == -1)
{
log_error ("no alive host found in pool '%s'\n", name);
@@ -586,7 +622,6 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect,
* find the canonical name so that it can be used in the HTTP
* Host header. Fixme: We should store that name in the
* hosttable. */
- dns_addrinfo_t aibuf, ai;
char *host;
err = resolve_dns_name (hi->name, 0, 0, SOCK_STREAM, &aibuf, NULL);
@@ -650,9 +685,9 @@ map_host (ctrl_t ctrl, const char *name, const char *srvtag, int force_reselect,
}
return err;
}
- if (hi->port)
+ if (hi->port[protocol])
snprintf (r_portstr, 6 /* five digits and the sentinel */,
- "%hu", hi->port);
+ "%hu", hi->port[protocol]);
return 0;
}
@@ -740,7 +775,9 @@ ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive)
/* If the host is a pool mark all member hosts. */
if (!err && hi->pool)
{
- for (idx2=0; !err && (n=hi->pool[idx2]) != -1; idx2++)
+ for (idx2 = 0;
+ !err && idx2 < hi->pool_len && (n = hi->pool[idx2]) != -1;
+ idx2++)
{
assert (n >= 0 && n < hosttable_size);
@@ -753,7 +790,7 @@ ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive)
if (hosttable[idx3]
&& hosttable[idx3]->pool
&& idx3 != idx
- && host_in_pool_p (hosttable[idx3]->pool, n))
+ && host_in_pool_p (hosttable[idx3], n))
break;
}
if (idx3 < hosttable_size)
@@ -903,7 +940,7 @@ ks_hkp_print_hosttable (ctrl_t ctrl)
{
init_membuf (&mb, 256);
put_membuf_printf (&mb, " . -->");
- for (idx2=0; hi->pool[idx2] != -1; idx2++)
+ for (idx2 = 0; idx2 < hi->pool_len && hi->pool[idx2] != -1; idx2++)
{
put_membuf_printf (&mb, " %d", hi->pool[idx2]);
if (hi->poolidx == hi->pool[idx2])
@@ -971,6 +1008,7 @@ make_host_part (ctrl_t ctrl,
const char *srvtag;
char portstr[10];
char *hostname;
+ enum ks_protocol protocol;
*r_hostport = NULL;
@@ -978,15 +1016,17 @@ make_host_part (ctrl_t ctrl,
{
scheme = "https";
srvtag = no_srv? NULL : "pgpkey-https";
+ protocol = KS_PROTOCOL_HKPS;
}
else /* HKP or HTTP. */
{
scheme = "http";
srvtag = no_srv? NULL : "pgpkey-http";
+ protocol = KS_PROTOCOL_HKP;
}
portstr[0] = 0;
- err = map_host (ctrl, host, srvtag, force_reselect,
+ err = map_host (ctrl, host, srvtag, force_reselect, protocol,
&hostname, portstr, r_httpflags, r_httphost);
if (err)
return err;
@@ -1122,9 +1162,16 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
int redirects_left = MAX_REDIRECTS;
estream_t fp = NULL;
char *request_buffer = NULL;
+ parsed_uri_t uri = NULL;
+ int is_onion;
*r_fp = NULL;
+ err = http_parse_uri (&uri, request, 0);
+ if (err)
+ goto leave;
+ is_onion = uri->onion;
+
err = http_session_new (&session, httphost,
((ctrl->http_no_crl? HTTP_FLAG_NO_CRL : 0)
| HTTP_FLAG_TRUST_DEF),
@@ -1132,6 +1179,7 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
if (err)
goto leave;
http_session_set_log_cb (session, cert_log_cb);
+ http_session_set_timeout (session, ctrl->timeout);
once_more:
err = http_open (&http,
@@ -1209,6 +1257,23 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
request, s?s:"[none]", http_get_status_code (http));
if (s && *s && redirects_left-- )
{
+ if (is_onion)
+ {
+ /* Make sure that an onion address only redirects to
+ * another onion address. */
+ http_release_parsed_uri (uri);
+ uri = NULL;
+ err = http_parse_uri (&uri, s, 0);
+ if (err)
+ goto leave;
+
+ if (! uri->onion)
+ {
+ err = gpg_error (GPG_ERR_FORBIDDEN);
+ goto leave;
+ }
+ }
+
xfree (request_buffer);
request_buffer = xtrystrdup (s);
if (request_buffer)
@@ -1257,6 +1322,7 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
http_close (http, 0);
http_session_release (session);
xfree (request_buffer);
+ http_release_parsed_uri (uri);
return err;
}
diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c
index 02269da..7fb7731 100644
--- a/dirmngr/ks-engine-http.c
+++ b/dirmngr/ks-engine-http.c
@@ -72,6 +72,13 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp)
int redirects_left = MAX_REDIRECTS;
estream_t fp = NULL;
char *request_buffer = NULL;
+ parsed_uri_t uri = NULL;
+ int is_onion;
+
+ err = http_parse_uri (&uri, url, 0);
+ if (err)
+ goto leave;
+ is_onion = uri->onion;
once_more:
/* Note that we only use the system provided certificates with the
@@ -83,6 +90,7 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp)
if (err)
goto leave;
http_session_set_log_cb (session, cert_log_cb);
+ http_session_set_timeout (session, ctrl->timeout);
*r_fp = NULL;
err = http_open (&http,
@@ -144,6 +152,23 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp)
url, s?s:"[none]", http_get_status_code (http));
if (s && *s && redirects_left-- )
{
+ if (is_onion)
+ {
+ /* Make sure that an onion address only redirects to
+ * another onion address. */
+ http_release_parsed_uri (uri);
+ uri = NULL;
+ err = http_parse_uri (&uri, s, 0);
+ if (err)
+ goto leave;
+
+ if (! uri->onion)
+ {
+ err = gpg_error (GPG_ERR_FORBIDDEN);
+ goto leave;
+ }
+ }
+
xfree (request_buffer);
request_buffer = xtrystrdup (s);
if (request_buffer)
@@ -185,5 +210,6 @@ ks_http_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp)
http_close (http, 0);
http_session_release (session);
xfree (request_buffer);
+ http_release_parsed_uri (uri);
return err;
}
diff --git a/dirmngr/ldap.c b/dirmngr/ldap.c
index d661a68..adf8307 100644
--- a/dirmngr/ldap.c
+++ b/dirmngr/ldap.c
@@ -363,6 +363,7 @@ parse_one_pattern (const char *pattern)
break;
case '*':
pattern++;
+ /* fall through */
default: /* Take as substring match. */
{
const char format[] = "(|(sn=*%s*)(|(cn=*%s*)(mail=*%s*)))";
diff --git a/dirmngr/loadswdb.c b/dirmngr/loadswdb.c
index 7791f68..bc00466 100644
--- a/dirmngr/loadswdb.c
+++ b/dirmngr/loadswdb.c
@@ -1,5 +1,6 @@
/* loadswdb.c - Load the swdb file from versions.gnupg.org
* Copyright (C) 2016 g10 Code GmbH
+ * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GnuPG.
*
diff --git a/dirmngr/server.c b/dirmngr/server.c
index 237cb52..7ed6cde 100644
--- a/dirmngr/server.c
+++ b/dirmngr/server.c
@@ -2,6 +2,7 @@
* Copyright (C) 2002 Klarälvdalens Datakonsult AB
* Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009, 2011, 2015 g10 Code GmbH
* Copyright (C) 2014, 2015, 2016 Werner Koch
+ * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik
*
* This file is part of GnuPG.
*
@@ -307,8 +308,8 @@ strcpy_escaped_plus (char *d, const unsigned char *s)
}
-/* This function returns true if a Tor server is running. The sattus
- is cached for the current connection. */
+/* This function returns true if a Tor server is running. The status
+ * is cached for the current connection. */
static int
is_tor_running (ctrl_t ctrl)
{
@@ -842,6 +843,8 @@ cmd_wkd_get (assuan_context_t ctx, char *line)
opt_submission_addr = has_option (line, "--submission-address");
opt_policy_flags = has_option (line, "--policy-flags");
+ if (has_option (line, "--quick"))
+ ctrl->timeout = opt.connect_quick_timeout;
line = skip_options (line);
mbox = mailbox_from_userid (line);
@@ -893,7 +896,6 @@ cmd_wkd_get (assuan_context_t ctx, char *line)
}
}
xfree (srvs);
- log_debug ("srv: got '%s%s'\n", domain, portstr);
}
gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, mbox, strlen (mbox));
@@ -929,6 +931,13 @@ cmd_wkd_get (assuan_context_t ctx, char *line)
encodedhash,
NULL);
no_log = 1;
+ if (uri)
+ {
+ err = dirmngr_status_printf (ctrl, "SOURCE", "https://%s%s",
+ domain, portstr);
+ if (err)
+ goto leave;
+ }
}
if (!uri)
{
@@ -2123,7 +2132,8 @@ cmd_ks_search (assuan_context_t ctx, char *line)
char *p;
estream_t outfp;
- /* No options for now. */
+ if (has_option (line, "--quick"))
+ ctrl->timeout = opt.connect_quick_timeout;
line = skip_options (line);
/* Break the line down into an strlist. Each pattern is
@@ -2187,7 +2197,8 @@ cmd_ks_get (assuan_context_t ctx, char *line)
char *p;
estream_t outfp;
- /* No options for now. */
+ if (has_option (line, "--quick"))
+ ctrl->timeout = opt.connect_quick_timeout;
line = skip_options (line);
/* Break the line into a strlist. Each pattern is by
@@ -2251,7 +2262,8 @@ cmd_ks_fetch (assuan_context_t ctx, char *line)
gpg_error_t err;
estream_t outfp;
- /* No options for now. */
+ if (has_option (line, "--quick"))
+ ctrl->timeout = opt.connect_quick_timeout;
line = skip_options (line);
err = ensure_keyserver (ctrl); /* FIXME: Why do we needs this here? */
@@ -2770,6 +2782,24 @@ dirmngr_status_help (ctrl_t ctrl, const char *text)
return err;
}
+
+/* This function is similar to print_assuan_status but takes a CTRL
+ * arg instead of an assuan context as first argument. */
+gpg_error_t
+dirmngr_status_printf (ctrl_t ctrl, const char *keyword,
+ const char *format, ...)
+{
+ gpg_error_t err;
+ va_list arg_ptr;
+ assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+ va_start (arg_ptr, format);
+ err = vprint_assuan_status (ctx, keyword, format, arg_ptr);
+ va_end (arg_ptr);
+ return err;
+}
+
+
/* Send a tick progress indicator back. Fixme: This is only done for
the currently active channel. */
gpg_error_t
diff --git a/dirmngr/t-http.c b/dirmngr/t-http.c
index a3a74dd..440633d 100644
--- a/dirmngr/t-http.c
+++ b/dirmngr/t-http.c
@@ -40,6 +40,7 @@
#include "../common/util.h"
#include "../common/logging.h"
+#include "dns-stuff.h"
#include "http.h"
#include <ksba.h>
@@ -203,6 +204,7 @@ main (int argc, char **argv)
int no_crl = 0;
const char *cafile = NULL;
http_session_t session = NULL;
+ unsigned int timeout = 0;
gpgrt_init ();
log_set_prefix (PGM, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID);
@@ -224,6 +226,7 @@ main (int argc, char **argv)
" --debug flyswatter\n"
" --tls-debug N use TLS debug level N\n"
" --cacert FNAME expect CA certificate in file FNAME\n"
+ " --timeout MS timeout for connect in MS\n"
" --no-verify do not verify the certificate\n"
" --force-tls use HTTP_FLAG_FORCE_TLS\n"
" --force-tor use HTTP_FLAG_FORCE_TOR\n"
@@ -261,6 +264,15 @@ main (int argc, char **argv)
argc--; argv++;
}
}
+ else if (!strcmp (*argv, "--timeout"))
+ {
+ argc--; argv++;
+ if (argc)
+ {
+ timeout = strtoul (*argv, NULL, 10);
+ argc--; argv++;
+ }
+ }
else if (!strcmp (*argv, "--no-verify"))
{
no_verify = 1;
@@ -301,9 +313,25 @@ main (int argc, char **argv)
if (!cafile)
cafile = prepend_srcdir ("tls-ca.pem");
+ if (verbose)
+ my_http_flags |= HTTP_FLAG_LOG_RESP;
+
+ if (verbose || debug)
+ http_set_verbose (verbose, debug);
+
/* http.c makes use of the assuan socket wrapper. */
assuan_sock_init ();
+ if ((my_http_flags & HTTP_FLAG_FORCE_TOR))
+ {
+ enable_dns_tormode (1);
+ if (assuan_sock_set_flag (ASSUAN_INVALID_FD, "tor-mode", 1))
+ {
+ log_error ("error enabling Tor mode: %s\n", strerror (errno));
+ log_info ("(is your Libassuan recent enough?)\n");
+ }
+ }
+
#if HTTP_USE_NTBTLS
log_info ("new session.\n");
err = http_session_new (&session, NULL,
@@ -407,6 +435,9 @@ main (int argc, char **argv)
http_release_parsed_uri (uri);
uri = NULL;
+ if (session)
+ http_session_set_timeout (session, timeout);
+
rc = http_open_document (&hd, *argv, NULL, my_http_flags,
NULL, session, NULL, NULL);
if (rc)