aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/Makefile.am46
-rw-r--r--src/common/Makefile.nmake20
-rw-r--r--src/common/address.c43
-rw-r--r--src/common/address.h16
-rw-r--r--src/common/aes.c19
-rw-r--r--src/common/compat.c109
-rw-r--r--src/common/compat.h13
-rw-r--r--src/common/compat_libevent.c106
-rw-r--r--src/common/compat_libevent.h22
-rw-r--r--src/common/container.c1
-rw-r--r--src/common/crypto.c107
-rw-r--r--src/common/crypto.h7
-rw-r--r--src/common/log.c31
-rw-r--r--src/common/mempool.c2
-rw-r--r--src/common/torgzip.c4
-rw-r--r--src/common/torint.h9
-rw-r--r--src/common/torlog.h5
-rw-r--r--src/common/tortls.c952
-rw-r--r--src/common/tortls.h39
-rw-r--r--src/common/util.c1206
-rw-r--r--src/common/util.h84
-rw-r--r--src/or/Makefile.am115
-rw-r--r--src/or/Makefile.nmake28
-rw-r--r--src/or/buffers.c865
-rw-r--r--src/or/buffers.h38
-rw-r--r--src/or/circuitbuild.c1024
-rw-r--r--src/or/circuitbuild.h55
-rw-r--r--src/or/circuitlist.c64
-rw-r--r--src/or/circuituse.c364
-rw-r--r--src/or/circuituse.h12
-rw-r--r--src/or/command.c639
-rw-r--r--src/or/config.c1673
-rw-r--r--src/or/config.h29
-rw-r--r--src/or/connection.c1611
-rw-r--r--src/or/connection.h70
-rw-r--r--src/or/connection_edge.c878
-rw-r--r--src/or/connection_edge.h40
-rw-r--r--src/or/connection_or.c1022
-rw-r--r--src/or/connection_or.h33
-rw-r--r--src/or/control.c448
-rw-r--r--src/or/control.h6
-rw-r--r--src/or/cpuworker.c9
-rw-r--r--src/or/directory.c451
-rw-r--r--src/or/directory.h9
-rw-r--r--src/or/dirserv.c342
-rw-r--r--src/or/dirserv.h35
-rw-r--r--src/or/dirvote.c29
-rw-r--r--src/or/dirvote.h5
-rw-r--r--src/or/dns.c18
-rw-r--r--src/or/dnsserv.c73
-rw-r--r--src/or/dnsserv.h4
-rw-r--r--src/or/eventdns.c10
-rw-r--r--src/or/geoip.c344
-rw-r--r--src/or/geoip.h10
-rw-r--r--src/or/hibernate.c49
-rw-r--r--src/or/hibernate.h26
-rw-r--r--src/or/main.c478
-rw-r--r--src/or/main.h12
-rw-r--r--src/or/microdesc.c297
-rw-r--r--src/or/microdesc.h18
-rw-r--r--src/or/networkstatus.c467
-rw-r--r--src/or/networkstatus.h40
-rw-r--r--src/or/nodelist.c758
-rw-r--r--src/or/nodelist.h60
-rw-r--r--src/or/ntmain.c1
-rw-r--r--src/or/or.h842
-rw-r--r--src/or/policies.c245
-rw-r--r--src/or/policies.h22
-rw-r--r--src/or/relay.c219
-rw-r--r--src/or/relay.h4
-rw-r--r--src/or/rendclient.c244
-rw-r--r--src/or/rendclient.h15
-rw-r--r--src/or/rendcommon.c9
-rw-r--r--src/or/rendcommon.h4
-rw-r--r--src/or/rendservice.c60
-rw-r--r--src/or/rendservice.h2
-rw-r--r--src/or/rephist.c750
-rw-r--r--src/or/rephist.h17
-rw-r--r--src/or/router.c284
-rw-r--r--src/or/router.h43
-rw-r--r--src/or/routerlist.c1370
-rw-r--r--src/or/routerlist.h104
-rw-r--r--src/or/routerparse.c62
-rw-r--r--src/or/status.c115
-rw-r--r--src/or/status.h10
-rw-r--r--src/or/transports.c1048
-rw-r--r--src/or/transports.h105
-rw-r--r--src/test/Makefile.am18
-rw-r--r--src/test/test-child.c40
-rw-r--r--src/test/test.c789
-rw-r--r--src/test/test.h3
-rw-r--r--src/test/test_addr.c50
-rw-r--r--src/test/test_crypto.c95
-rw-r--r--src/test/test_dir.c24
-rw-r--r--src/test/test_microdesc.c233
-rw-r--r--src/test/test_pt.c147
-rw-r--r--src/test/test_util.c474
-rw-r--r--src/test/tinytest.c17
-rw-r--r--src/test/tinytest_demo.c14
-rw-r--r--src/test/tinytest_macros.h39
-rw-r--r--src/tools/Makefile.am18
-rw-r--r--src/tools/tor-fw-helper/Makefile.am38
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper-natpmp.c233
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper-natpmp.h47
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper-upnp.c189
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper-upnp.h43
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper.c396
-rw-r--r--src/tools/tor-fw-helper/tor-fw-helper.h57
-rw-r--r--src/tools/tor-gencert.c2
-rw-r--r--src/tools/tor-resolve.c2
-rw-r--r--src/win32/orconfig.h11
111 files changed, 19501 insertions, 4452 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 6952591d6..2244fe58d 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -1,7 +1,7 @@
noinst_LIBRARIES = libor.a libor-crypto.a libor-event.a
-EXTRA_DIST = common_sha1.i sha256.c
+EXTRA_DIST = common_sha1.i sha256.c Makefile.nmake
#CFLAGS = -Wall -Wpointer-arith -O2
@@ -11,12 +11,48 @@ else
libor_extra_source=
endif
-libor_a_SOURCES = address.c log.c util.c compat.c container.c mempool.c \
- memarea.c di_ops.c procmon.c util_codedigest.c $(libor_extra_source)
-libor_crypto_a_SOURCES = crypto.c aes.c tortls.c torgzip.c
+libor_a_SOURCES = \
+ address.c \
+ compat.c \
+ container.c \
+ di_ops.c \
+ log.c \
+ memarea.c \
+ mempool.c \
+ procmon.c \
+ util.c \
+ util_codedigest.c \
+ $(libor_extra_source)
+
+libor_crypto_a_SOURCES = \
+ aes.c \
+ crypto.c \
+ torgzip.c \
+ tortls.c
+
libor_event_a_SOURCES = compat_libevent.c
-noinst_HEADERS = address.h torlog.h crypto.h util.h compat.h aes.h torint.h tortls.h strlcpy.c strlcat.c torgzip.h container.h ht.h mempool.h memarea.h ciphers.inc compat_libevent.h tortls_states.h di_ops.h procmon.h
+noinst_HEADERS = \
+ address.h \
+ aes.h \
+ ciphers.inc \
+ compat.h \
+ compat_libevent.h \
+ container.h \
+ crypto.h \
+ di_ops.h \
+ ht.h \
+ memarea.h \
+ mempool.h \
+ procmon.h \
+ strlcat.c \
+ strlcpy.c \
+ torgzip.h \
+ torint.h \
+ torlog.h \
+ tortls.h \
+ tortls_states.h \
+ util.h
common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(noinst_HEADERS)
if test "@SHA1SUM@" != none; then \
diff --git a/src/common/Makefile.nmake b/src/common/Makefile.nmake
new file mode 100644
index 000000000..c8b598866
--- /dev/null
+++ b/src/common/Makefile.nmake
@@ -0,0 +1,20 @@
+all: libor.lib libor-crypto.lib libor-event.lib
+
+CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include
+
+LIBOR_OBJECTS = address.obj compat.obj container.obj di_ops.obj \
+ log.obj memarea.obj mempool.obj procmon.obj util.obj \
+ util_codedigest.obj
+
+LIBOR_CRYPTO_OBJECTS = aes.obj crypto.obj torgzip.obj tortls.obj
+
+LIBOR_EVENT_OBJECTS = compat_libevent.obj
+
+libor.lib: $(LIBOR_OBJECTS)
+ lib $(LIBOR_OBJECTS) /out:libor.lib
+
+libor-crypto.lib: $(LIBOR_CRYPTO_OBJECTS)
+ lib $(LIBOR_CRYPTO_OBJECTS) /out:libor-crypto.lib
+
+libor-event.lib: $(LIBOR_EVENT_OBJECTS)
+ lib $(LIBOR_EVENT_OBJECTS) /out:libor-event.lib
diff --git a/src/common/address.c b/src/common/address.c
index 7fc730105..b41456f8d 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -343,7 +343,7 @@ tor_addr_is_internal(const tor_addr_t *addr, int for_listening)
* brackets.
*/
const char *
-tor_addr_to_str(char *dest, const tor_addr_t *addr, int len, int decorate)
+tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate)
{
const char *ptr;
tor_assert(addr && dest);
@@ -384,7 +384,7 @@ tor_addr_to_str(char *dest, const tor_addr_t *addr, int len, int decorate)
* IPv4 or IPv6 address too.
*/
int
-tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address,
+tor_addr_parse_PTR_name(tor_addr_t *result, const char *address,
int family, int accept_regular)
{
if (!strcasecmpend(address, ".in-addr.arpa")) {
@@ -455,7 +455,7 @@ tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address,
if (accept_regular) {
tor_addr_t tmp;
- int r = tor_addr_from_str(&tmp, address);
+ int r = tor_addr_parse(&tmp, address);
if (r < 0)
return 0;
if (r != family && family != AF_UNSPEC)
@@ -474,7 +474,7 @@ tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address,
* the result in the <b>outlen</b>-byte buffer at <b>out</b>. Return 0 on
* success, -1 on failure. */
int
-tor_addr_to_reverse_lookup_name(char *out, size_t outlen,
+tor_addr_to_PTR_name(char *out, size_t outlen,
const tor_addr_t *addr)
{
if (addr->family == AF_INET) {
@@ -964,6 +964,19 @@ fmt_addr(const tor_addr_t *addr)
return "???";
}
+/** Like fmt_addr(), but takes <b>addr</b> as a host-order IPv4
+ * addresses. Also not thread-safe, also clobbers its return buffer on
+ * repeated calls. */
+const char *
+fmt_addr32(uint32_t addr)
+{
+ static char buf[INET_NTOA_BUF_LEN];
+ struct in_addr in;
+ in.s_addr = htonl(addr);
+ tor_inet_ntoa(&in, buf, sizeof(buf));
+ return buf;
+}
+
/** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string
* may be an IPv4 address, an IPv6 address, or an IPv6 address surrounded by
* square brackets.
@@ -971,7 +984,7 @@ fmt_addr(const tor_addr_t *addr)
* Return an address family on success, or -1 if an invalid address string is
* provided. */
int
-tor_addr_from_str(tor_addr_t *addr, const char *src)
+tor_addr_parse(tor_addr_t *addr, const char *src)
{
char *tmp = NULL; /* Holds substring if we got a dotted quad. */
int result;
@@ -999,7 +1012,7 @@ tor_addr_from_str(tor_addr_t *addr, const char *src)
* address as needed, and put the result in <b>addr_out</b> and (optionally)
* <b>port_out</b>. Return 0 on success, negative on failure. */
int
-tor_addr_port_parse(const char *s, tor_addr_t *addr_out, uint16_t *port_out)
+tor_addr_port_lookup(const char *s, tor_addr_t *addr_out, uint16_t *port_out)
{
const char *port;
tor_addr_t addr;
@@ -1135,6 +1148,20 @@ is_internal_IP(uint32_t ip, int for_listening)
return tor_addr_is_internal(&myaddr, for_listening);
}
+/** Given an address of the form "host:port", try to divide it into its host
+ * ane port portions, setting *<b>address_out</b> to a newly allocated string
+ * holding the address portion and *<b>port_out</b> to the port (or 0 if no
+ * port is given). Return 0 on success, -1 on failure. */
+int
+tor_addr_port_split(int severity, const char *addrport,
+ char **address_out, uint16_t *port_out)
+{
+ tor_assert(addrport);
+ tor_assert(address_out);
+ tor_assert(port_out);
+ return addr_port_lookup(severity, addrport, address_out, NULL, port_out);
+}
+
/** Parse a string of the form "host[:port]" from <b>addrport</b>. If
* <b>address</b> is provided, set *<b>address</b> to a copy of the
* host portion of the string. If <b>addr</b> is provided, try to
@@ -1146,7 +1173,7 @@ is_internal_IP(uint32_t ip, int for_listening)
* Return 0 on success, -1 on failure.
*/
int
-parse_addr_port(int severity, const char *addrport, char **address,
+addr_port_lookup(int severity, const char *addrport, char **address,
uint32_t *addr, uint16_t *port_out)
{
const char *colon;
@@ -1156,7 +1183,7 @@ parse_addr_port(int severity, const char *addrport, char **address,
tor_assert(addrport);
- colon = strchr(addrport, ':');
+ colon = strrchr(addrport, ':');
if (colon) {
_address = tor_strndup(addrport, colon-addrport);
_port = (int) tor_parse_long(colon+1,10,1,65535,NULL,NULL);
diff --git a/src/common/address.h b/src/common/address.h
index 9a7656f69..359b0264d 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -127,6 +127,7 @@ tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u)
int tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr_out);
char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC;
const char *fmt_addr(const tor_addr_t *addr);
+const char * fmt_addr32(uint32_t addr);
int get_interface_address6(int severity, sa_family_t family, tor_addr_t *addr);
/** Flag to specify how to do a comparison between addresses. In an "exact"
@@ -153,19 +154,19 @@ int tor_addr_is_internal(const tor_addr_t *ip, int for_listening) ATTR_PURE;
/** Longest length that can be required for a reverse lookup name. */
/* 32 nybbles, 32 dots, 8 characters of "ip6.arpa", 1 NUL: 73 characters. */
#define REVERSE_LOOKUP_NAME_BUF_LEN 73
-int tor_addr_to_reverse_lookup_name(char *out, size_t outlen,
+int tor_addr_to_PTR_name(char *out, size_t outlen,
const tor_addr_t *addr);
-int tor_addr_parse_reverse_lookup_name(tor_addr_t *result, const char *address,
+int tor_addr_parse_PTR_name(tor_addr_t *result, const char *address,
int family, int accept_regular);
-int tor_addr_port_parse(const char *s, tor_addr_t *addr_out,
+int tor_addr_port_lookup(const char *s, tor_addr_t *addr_out,
uint16_t *port_out);
int tor_addr_parse_mask_ports(const char *s,
tor_addr_t *addr_out, maskbits_t *mask_out,
uint16_t *port_min_out, uint16_t *port_max_out);
-const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, int len,
+const char * tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len,
int decorate);
-int tor_addr_from_str(tor_addr_t *addr, const char *src);
+int tor_addr_parse(tor_addr_t *addr, const char *src);
void tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src);
void tor_addr_from_ipv4n(tor_addr_t *dest, uint32_t v4addr);
/** Set <b>dest</b> to the IPv4 address encoded in <b>v4addr</b> in host
@@ -180,9 +181,12 @@ void tor_addr_from_in6(tor_addr_t *dest, const struct in6_addr *in6);
int tor_addr_is_null(const tor_addr_t *addr);
int tor_addr_is_loopback(const tor_addr_t *addr);
+int tor_addr_port_split(int severity, const char *addrport,
+ char **address_out, uint16_t *port_out);
+
/* IPv4 helpers */
int is_internal_IP(uint32_t ip, int for_listening) ATTR_PURE;
-int parse_addr_port(int severity, const char *addrport, char **address,
+int addr_port_lookup(int severity, const char *addrport, char **address,
uint32_t *addr, uint16_t *port_out);
int parse_port_range(const char *port, uint16_t *port_min_out,
uint16_t *port_max_out);
diff --git a/src/common/aes.c b/src/common/aes.c
index c2fdeb594..81091e9f0 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -291,11 +291,20 @@ void
aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
char *output)
{
-
- /* XXXX This function is up to 5% of our runtime in some profiles;
- * we should look into unrolling some of the loops; taking advantage
- * of alignment, using a bigger buffer, and so on. Not till after 0.1.2.x,
- * though. */
+ /* This function alone is up to 5% of our runtime in some profiles; anything
+ * we could do to make it faster would be great.
+ *
+ * Experimenting suggests that unrolling the inner loop into a switch
+ * statement doesn't help. What does seem to help is making the input and
+ * output buffers word aligned, and never crypting anything besides an
+ * integer number of words at a time -- it shaves maybe 4-5% of the per-byte
+ * encryption time measured by bench_aes. We can't do that with the current
+ * Tor protocol, though: Tor really likes to crypt things in 509-byte
+ * chunks.
+ *
+ * If we were really ambitous, we'd force len to be a multiple of the block
+ * size, and shave maybe another 4-5% off.
+ */
int c = cipher->pos;
if (PREDICT_UNLIKELY(!len)) return;
diff --git a/src/common/compat.c b/src/common/compat.c
index 266fc61c4..2cf7f463c 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -103,6 +103,35 @@
#include "strlcat.c"
#endif
+/** As open(path, flags, mode), but return an fd with the close-on-exec mode
+ * set. */
+int
+tor_open_cloexec(const char *path, int flags, unsigned mode)
+{
+#ifdef O_CLOEXEC
+ return open(path, flags|O_CLOEXEC, mode);
+#else
+ int fd = open(path, flags, mode);
+#ifdef FD_CLOEXEC
+ if (fd >= 0)
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+ return fd;
+#endif
+}
+
+/** DOCDOC */
+FILE *
+tor_fopen_cloexec(const char *path, const char *mode)
+{
+ FILE *result = fopen(path, mode);
+#ifdef FD_CLOEXEC
+ if (result != NULL)
+ fcntl(fileno(result), F_SETFD, FD_CLOEXEC);
+#endif
+ return result;
+}
+
#ifdef HAVE_SYS_MMAN_H
/** Try to create a memory mapping for <b>filename</b> and return it. On
* failure, return NULL. Sets errno properly, using ERANGE to mean
@@ -118,7 +147,7 @@ tor_mmap_file(const char *filename)
tor_assert(filename);
- fd = open(filename, O_RDONLY, 0);
+ fd = tor_open_cloexec(filename, O_RDONLY, 0);
if (fd<0) {
int save_errno = errno;
int severity = (errno == ENOENT) ? LOG_INFO : LOG_WARN;
@@ -695,7 +724,7 @@ tor_lockfile_lock(const char *filename, int blocking, int *locked_out)
*locked_out = 0;
log_info(LD_FS, "Locking \"%s\"", filename);
- fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
+ fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
if (fd < 0) {
log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename,
strerror(errno));
@@ -922,8 +951,16 @@ mark_socket_open(tor_socket_t s)
tor_socket_t
tor_open_socket(int domain, int type, int protocol)
{
- tor_socket_t s = socket(domain, type, protocol);
+ tor_socket_t s;
+#ifdef SOCK_CLOEXEC
+#define LINUX_CLOEXEC_OPEN_SOCKET
+ type |= SOCK_CLOEXEC;
+#endif
+ s = socket(domain, type, protocol);
if (SOCKET_OK(s)) {
+#if !defined(LINUX_CLOEXEC_OPEN_SOCKET) && defined(FD_CLOEXEC)
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+#endif
socket_accounting_lock();
++n_sockets_open;
mark_socket_open(s);
@@ -936,8 +973,17 @@ tor_open_socket(int domain, int type, int protocol)
tor_socket_t
tor_accept_socket(int sockfd, struct sockaddr *addr, socklen_t *len)
{
- tor_socket_t s = accept(sockfd, addr, len);
+ tor_socket_t s;
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+#define LINUX_CLOEXEC_ACCEPT
+ s = accept4(sockfd, addr, len, SOCK_CLOEXEC);
+#else
+ s = accept(sockfd, addr, len);
+#endif
if (SOCKET_OK(s)) {
+#if !defined(LINUX_CLOEXEC_ACCEPT) && defined(FD_CLOEXEC)
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+#endif
socket_accounting_lock();
++n_sockets_open;
mark_socket_open(s);
@@ -993,8 +1039,17 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
//don't use win32 socketpairs (they are always bad)
#if defined(HAVE_SOCKETPAIR) && !defined(MS_WINDOWS)
int r;
+#ifdef SOCK_CLOEXEC
+ type |= SOCK_CLOEXEC;
+#endif
r = socketpair(family, type, protocol, fd);
if (r == 0) {
+#if !defined(SOCK_CLOEXEC) && defined(FD_CLOEXEC)
+ if (fd[0] >= 0)
+ fcntl(fd[0], F_SETFD, FD_CLOEXEC);
+ if (fd[1] >= 0)
+ fcntl(fd[1], F_SETFD, FD_CLOEXEC);
+#endif
socket_accounting_lock();
if (fd[0] >= 0) {
++n_sockets_open;
@@ -1995,6 +2050,52 @@ spawn_exit(void)
#endif
}
+/** Implementation logic for compute_num_cpus(). */
+static int
+compute_num_cpus_impl(void)
+{
+#ifdef MS_WINDOWS
+ SYSTEM_INFO info;
+ memset(&info, 0, sizeof(info));
+ GetSystemInfo(&info);
+ if (info.dwNumberOfProcessors >= 1 && info.dwNumberOfProcessors < INT_MAX)
+ return (int)info.dwNumberOfProcessors;
+ else
+ return -1;
+#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF)
+ long cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (cpus >= 1 && cpus < INT_MAX)
+ return (int)cpus;
+ else
+ return -1;
+#else
+ return -1;
+#endif
+}
+
+#define MAX_DETECTABLE_CPUS 16
+
+/** Return how many CPUs we are running with. We assume that nobody is
+ * using hot-swappable CPUs, so we don't recompute this after the first
+ * time. Return -1 if we don't know how to tell the number of CPUs on this
+ * system.
+ */
+int
+compute_num_cpus(void)
+{
+ static int num_cpus = -2;
+ if (num_cpus == -2) {
+ num_cpus = compute_num_cpus_impl();
+ tor_assert(num_cpus != -2);
+ if (num_cpus > MAX_DETECTABLE_CPUS)
+ log_notice(LD_GENERAL, "Wow! I detected that you have %d CPUs. I "
+ "will not autodetect any more than %d, though. If you "
+ "want to configure more, set NumCPUs in your torrc",
+ num_cpus, MAX_DETECTABLE_CPUS);
+ }
+ return num_cpus;
+}
+
/** Set *timeval to the current time of day. On error, log and terminate.
* (Same as gettimeofday(timeval,NULL), but never returns -1.)
*/
diff --git a/src/common/compat.h b/src/common/compat.h
index eb79b0444..8e271ba90 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -9,8 +9,12 @@
#include "orconfig.h"
#include "torint.h"
#ifdef MS_WINDOWS
+#ifndef WIN32_WINNT
#define WIN32_WINNT 0x400
+#endif
+#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x400
+#endif
#define WIN32_LEAN_AND_MEAN
#if defined(_MSC_VER) && (_MSC_VER < 1300)
#include <winsock.h>
@@ -51,6 +55,8 @@
#include <netinet6/in6.h>
#endif
+#include <stdio.h>
+
#if defined (WINCE)
#include <fcntl.h>
#include <io.h>
@@ -367,6 +373,9 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
#endif
/* ===== File compatibility */
+int tor_open_cloexec(const char *path, int flags, unsigned mode);
+FILE *tor_fopen_cloexec(const char *path, const char *mode);
+
int replace_file(const char *from, const char *to);
int touch_file(const char *fname);
@@ -392,7 +401,7 @@ typedef int socklen_t;
#ifdef MS_WINDOWS
#define tor_socket_t intptr_t
-#define SOCKET_OK(s) ((s) != INVALID_SOCKET)
+#define SOCKET_OK(s) ((unsigned)(s) != INVALID_SOCKET)
#else
#define tor_socket_t int
#define SOCKET_OK(s) ((s) >= 0)
@@ -577,6 +586,8 @@ void spawn_exit(void) ATTR_NORETURN;
#undef TOR_IS_MULTITHREADED
#endif
+int compute_num_cpus(void);
+
/* Because we use threads instead of processes on most platforms (Windows,
* Linux, etc), we need locking for them. On platforms with poor thread
* support or broken gethostbyname_r, these functions are no-ops. */
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 6d89be804..320173870 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -19,6 +19,10 @@
#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
+#include <event2/thread.h>
+#ifdef USE_BUFFEREVENTS
+#include <event2/bufferevent.h>
+#endif
#else
#include <event.h>
#endif
@@ -163,11 +167,24 @@ struct event_base *the_event_base = NULL;
#endif
#endif
+#ifdef USE_BUFFEREVENTS
+static int using_iocp_bufferevents = 0;
+static void tor_libevent_set_tick_timeout(int msec_per_tick);
+
+int
+tor_libevent_using_iocp_bufferevents(void)
+{
+ return using_iocp_bufferevents;
+}
+#endif
+
/** Initialize the Libevent library and set up the event base. */
void
-tor_libevent_initialize(void)
+tor_libevent_initialize(tor_libevent_cfg *torcfg)
{
tor_assert(the_event_base == NULL);
+ /* some paths below don't use torcfg, so avoid unused variable warnings */
+ (void)torcfg;
#ifdef __APPLE__
if (MACOSX_KQUEUE_IS_BROKEN ||
@@ -177,7 +194,32 @@ tor_libevent_initialize(void)
#endif
#ifdef HAVE_EVENT2_EVENT_H
- the_event_base = event_base_new();
+ {
+ struct event_config *cfg = event_config_new();
+
+#if defined(MS_WINDOWS) && defined(USE_BUFFEREVENTS)
+ if (! torcfg->disable_iocp) {
+ evthread_use_windows_threads();
+ event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
+ using_iocp_bufferevents = 1;
+ }
+#endif
+
+#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,0,7)
+ if (torcfg->num_cpus > 0)
+ event_config_set_num_cpus_hint(cfg, torcfg->num_cpus);
+#endif
+
+#if LIBEVENT_VERSION_NUMBER >= V(2,0,9)
+ /* We can enable changelist support with epoll, since we don't give
+ * Libevent any dup'd fds. This lets us avoid some syscalls. */
+ event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);
+#endif
+
+ the_event_base = event_base_new_with_config(cfg);
+
+ event_config_free(cfg);
+ }
#else
the_event_base = event_init();
#endif
@@ -195,6 +237,10 @@ tor_libevent_initialize(void)
"You have a *VERY* old version of libevent. It is likely to be buggy; "
"please build Tor with a more recent version.");
#endif
+
+#ifdef USE_BUFFEREVENTS
+ tor_libevent_set_tick_timeout(torcfg->msec_per_tick);
+#endif
}
/** Return the current Libevent event base that we're set up to use. */
@@ -240,7 +286,7 @@ tor_decode_libevent_version(const char *v)
/* Try the new preferred "1.4.11-stable" format.
* Also accept "1.4.14b-stable". */
- fields = sscanf(v, "%u.%u.%u%c%c", &major, &minor, &patchlevel, &c, &e);
+ fields = tor_sscanf(v, "%u.%u.%u%c%c", &major, &minor, &patchlevel, &c, &e);
if (fields == 3 ||
((fields == 4 || fields == 5 ) && (c == '-' || c == '_')) ||
(fields == 5 && TOR_ISALPHA(c) && (e == '-' || e == '_'))) {
@@ -248,7 +294,7 @@ tor_decode_libevent_version(const char *v)
}
/* Try the old "1.3e" format. */
- fields = sscanf(v, "%u.%u%c%c", &major, &minor, &c, &extra);
+ fields = tor_sscanf(v, "%u.%u%c%c", &major, &minor, &c, &extra);
if (fields == 3 && TOR_ISALPHA(c)) {
return V_OLD(major, minor, c);
} else if (fields == 2) {
@@ -552,3 +598,55 @@ periodic_timer_free(periodic_timer_t *timer)
tor_free(timer);
}
+#ifdef USE_BUFFEREVENTS
+static const struct timeval *one_tick = NULL;
+/**
+ * Return a special timeout to be passed whenever libevent's O(1) timeout
+ * implementation should be used. Only use this when the timer is supposed
+ * to fire after msec_per_tick ticks have elapsed.
+*/
+const struct timeval *
+tor_libevent_get_one_tick_timeout(void)
+{
+ tor_assert(one_tick);
+ return one_tick;
+}
+
+/** Initialize the common timeout that we'll use to refill the buckets every
+ * time a tick elapses. */
+static void
+tor_libevent_set_tick_timeout(int msec_per_tick)
+{
+ struct event_base *base = tor_libevent_get_base();
+ struct timeval tv;
+
+ tor_assert(! one_tick);
+ tv.tv_sec = msec_per_tick / 1000;
+ tv.tv_usec = (msec_per_tick % 1000) * 1000;
+ one_tick = event_base_init_common_timeout(base, &tv);
+}
+
+static struct bufferevent *
+tor_get_root_bufferevent(struct bufferevent *bev)
+{
+ struct bufferevent *u;
+ while ((u = bufferevent_get_underlying(bev)) != NULL)
+ bev = u;
+ return bev;
+}
+
+int
+tor_set_bufferevent_rate_limit(struct bufferevent *bev,
+ struct ev_token_bucket_cfg *cfg)
+{
+ return bufferevent_set_rate_limit(tor_get_root_bufferevent(bev), cfg);
+}
+
+int
+tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev,
+ struct bufferevent_rate_limit_group *g)
+{
+ return bufferevent_add_to_rate_limit_group(tor_get_root_bufferevent(bev), g);
+}
+#endif
+
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 89b256396..024729717 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -8,6 +8,11 @@
struct event;
struct event_base;
+#ifdef USE_BUFFEREVENTS
+struct bufferevent;
+struct ev_token_bucket_cfg;
+struct bufferevent_rate_limit_group;
+#endif
#ifdef HAVE_EVENT2_EVENT_H
#include <event2/util.h>
@@ -54,7 +59,13 @@ struct timeval;
int tor_event_base_loopexit(struct event_base *base, struct timeval *tv);
#endif
-void tor_libevent_initialize(void);
+typedef struct tor_libevent_cfg {
+ int disable_iocp;
+ int num_cpus;
+ int msec_per_tick;
+} tor_libevent_cfg;
+
+void tor_libevent_initialize(tor_libevent_cfg *cfg);
struct event_base *tor_libevent_get_base(void);
const char *tor_libevent_get_method(void);
void tor_check_libevent_version(const char *m, int server,
@@ -62,5 +73,14 @@ void tor_check_libevent_version(const char *m, int server,
void tor_check_libevent_header_compatibility(void);
const char *tor_libevent_get_version_str(void);
+#ifdef USE_BUFFEREVENTS
+const struct timeval *tor_libevent_get_one_tick_timeout(void);
+int tor_libevent_using_iocp_bufferevents(void);
+int tor_set_bufferevent_rate_limit(struct bufferevent *bev,
+ struct ev_token_bucket_cfg *cfg);
+int tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev,
+ struct bufferevent_rate_limit_group *g);
+#endif
+
#endif
diff --git a/src/common/container.c b/src/common/container.c
index 1515c387a..92bfd2ec8 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -287,7 +287,6 @@ smartlist_subtract(smartlist_t *sl1, const smartlist_t *sl2)
/** Remove the <b>idx</b>th element of sl; if idx is not the last
* element, swap the last element of sl into the <b>idx</b>th space.
- * Return the old value of the <b>idx</b>th element.
*/
void
smartlist_del(smartlist_t *sl, int idx)
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 235bd88ff..0763a2c8c 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -13,8 +13,12 @@
#include "orconfig.h"
#ifdef MS_WINDOWS
+#ifndef WIN32_WINNT
#define WIN32_WINNT 0x400
+#endif
+#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x400
+#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
@@ -948,7 +952,7 @@ crypto_pk_public_checksig_digest(crypto_pk_env_t *env, const char *data,
log_warn(LD_BUG, "couldn't compute digest");
return -1;
}
- buflen = crypto_pk_keysize(env)+1;
+ buflen = crypto_pk_keysize(env);
buf = tor_malloc(buflen);
r = crypto_pk_public_checksig(env,buf,buflen,sig,siglen);
if (r != DIGEST_LEN) {
@@ -1133,8 +1137,8 @@ crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env,
warnOnFailure);
}
- buf = tor_malloc(pkeylen+1);
- outlen = crypto_pk_private_decrypt(env,buf,pkeylen+1,from,pkeylen,padding,
+ buf = tor_malloc(pkeylen);
+ outlen = crypto_pk_private_decrypt(env,buf,pkeylen,from,pkeylen,padding,
warnOnFailure);
if (outlen<0) {
log_fn(warnOnFailure?LOG_WARN:LOG_DEBUG, LD_CRYPTO,
@@ -1202,9 +1206,6 @@ crypto_pk_asn1_decode(const char *str, size_t len)
{
RSA *rsa;
unsigned char *buf;
- /* This ifdef suppresses a type warning. Take out the first case once
- * everybody is using OpenSSL 0.9.7 or later.
- */
const unsigned char *cp;
cp = buf = tor_malloc(len);
memcpy(buf,str,len);
@@ -1245,6 +1246,32 @@ crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out)
return 0;
}
+/** Compute all digests of the DER encoding of <b>pk</b>, and store them
+ * in <b>digests_out</b>. Return 0 on success, -1 on failure. */
+int
+crypto_pk_get_all_digests(crypto_pk_env_t *pk, digests_t *digests_out)
+{
+ unsigned char *buf, *bufp;
+ int len;
+
+ len = i2d_RSAPublicKey(pk->key, NULL);
+ if (len < 0)
+ return -1;
+ buf = bufp = tor_malloc(len+1);
+ len = i2d_RSAPublicKey(pk->key, &bufp);
+ if (len < 0) {
+ crypto_log_errors(LOG_WARN,"encoding public key");
+ tor_free(buf);
+ return -1;
+ }
+ if (crypto_digest_all(digests_out, (char*)buf, len) < 0) {
+ tor_free(buf);
+ return -1;
+ }
+ tor_free(buf);
+ return 0;
+}
+
/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
* every four spaces. */
/* static */ void
@@ -1714,6 +1741,74 @@ crypto_hmac_sha1(char *hmac_out,
(unsigned char*)hmac_out, NULL);
}
+/** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
+ * the <b>key</b> of length <b>key_len</b>. Store the DIGEST_LEN-byte result
+ * in <b>hmac_out</b>.
+ */
+void
+crypto_hmac_sha256(char *hmac_out,
+ const char *key, size_t key_len,
+ const char *msg, size_t msg_len)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x00908000l)
+ /* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
+ tor_assert(key_len < INT_MAX);
+ tor_assert(msg_len < INT_MAX);
+ HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
+ (unsigned char*)hmac_out, NULL);
+#else
+ /* OpenSSL doesn't have an EVP implementation for SHA256. We'll need
+ to do HMAC on our own.
+
+ HMAC isn't so hard: To compute HMAC(key, msg):
+ 1. If len(key) > blocksize, key = H(key).
+ 2. If len(key) < blocksize, right-pad key up to blocksize with 0 bytes.
+ 3. let ipad = key xor 0x363636363636....36
+ let opad = key xor 0x5c5c5c5c5c5c....5c
+ The result is H(opad | H( ipad | msg ) )
+ */
+#define BLOCKSIZE 64
+#define DIGESTSIZE 32
+ uint8_t k[BLOCKSIZE];
+ uint8_t pad[BLOCKSIZE];
+ uint8_t d[DIGESTSIZE];
+ int i;
+ SHA256_CTX st;
+
+ tor_assert(key_len < INT_MAX);
+ tor_assert(msg_len < INT_MAX);
+
+ if (key_len <= BLOCKSIZE) {
+ memset(k, 0, sizeof(k));
+ memcpy(k, key, key_len); /* not time invariant in key_len */
+ } else {
+ SHA256((const uint8_t *)key, key_len, k);
+ memset(k+DIGESTSIZE, 0, sizeof(k)-DIGESTSIZE);
+ }
+ for (i = 0; i < BLOCKSIZE; ++i)
+ pad[i] = k[i] ^ 0x36;
+ SHA256_Init(&st);
+ SHA256_Update(&st, pad, BLOCKSIZE);
+ SHA256_Update(&st, (uint8_t*)msg, msg_len);
+ SHA256_Final(d, &st);
+
+ for (i = 0; i < BLOCKSIZE; ++i)
+ pad[i] = k[i] ^ 0x5c;
+ SHA256_Init(&st);
+ SHA256_Update(&st, pad, BLOCKSIZE);
+ SHA256_Update(&st, d, DIGESTSIZE);
+ SHA256_Final((uint8_t*)hmac_out, &st);
+
+ /* Now clear everything. */
+ memset(k, 0, sizeof(k));
+ memset(pad, 0, sizeof(pad));
+ memset(d, 0, sizeof(d));
+ memset(&st, 0, sizeof(st));
+#undef BLOCKSIZE
+#undef DIGESTSIZE
+#endif
+}
+
/* DH */
/** Shared P parameter for our circuit-crypto DH key exchanges. */
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 1a8c81f83..80c10296a 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -150,6 +150,7 @@ int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, char *to,
int crypto_pk_asn1_encode(crypto_pk_env_t *pk, char *dest, size_t dest_len);
crypto_pk_env_t *crypto_pk_asn1_decode(const char *str, size_t len);
int crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out);
+int crypto_pk_get_all_digests(crypto_pk_env_t *pk, digests_t *digests_out);
int crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out,int add_space);
int crypto_pk_check_fingerprint_syntax(const char *s);
@@ -195,6 +196,9 @@ void crypto_digest_assign(crypto_digest_env_t *into,
void crypto_hmac_sha1(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len);
+void crypto_hmac_sha256(char *hmac_out,
+ const char *key, size_t key_len,
+ const char *msg, size_t msg_len);
/* Key negotiation */
#define DH_TYPE_CIRCUIT 1
@@ -245,7 +249,8 @@ void secret_to_key(char *key_out, size_t key_out_len, const char *secret,
size_t secret_len, const char *s2k_specifier);
#ifdef CRYPTO_PRIVATE
-/* Prototypes for private functions only used by tortls.c and crypto.c */
+/* Prototypes for private functions only used by tortls.c, crypto.c, and the
+ * unit tests. */
struct rsa_st;
struct evp_pkey_st;
struct dh_st;
diff --git a/src/common/log.c b/src/common/log.c
index ac98f1353..97400623e 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -154,6 +154,17 @@ log_set_application_name(const char *name)
appname = name ? tor_strdup(name) : NULL;
}
+/** Log time granularity in milliseconds. */
+static int log_time_granularity = 1;
+
+/** Define log time granularity for all logs to be <b>granularity_msec</b>
+ * milliseconds. */
+void
+set_log_time_granularity(int granularity_msec)
+{
+ log_time_granularity = granularity_msec;
+}
+
/** Helper: Write the standard prefix for log lines to a
* <b>buf_len</b> character buffer in <b>buf</b>.
*/
@@ -164,14 +175,22 @@ _log_prefix(char *buf, size_t buf_len, int severity)
struct timeval now;
struct tm tm;
size_t n;
- int r;
+ int r, ms;
tor_gettimeofday(&now);
t = (time_t)now.tv_sec;
+ ms = (int)now.tv_usec / 1000;
+ if (log_time_granularity >= 1000) {
+ t -= t % (log_time_granularity / 1000);
+ ms = 0;
+ } else {
+ ms -= ((int)now.tv_usec / 1000) % log_time_granularity;
+ }
n = strftime(buf, buf_len, "%b %d %H:%M:%S", tor_localtime_r(&t, &tm));
- r = tor_snprintf(buf+n, buf_len-n, ".%.3i [%s] ",
- (int)now.tv_usec / 1000, sev_to_string(severity));
+ r = tor_snprintf(buf+n, buf_len-n, ".%.3i [%s] ", ms,
+ sev_to_string(severity));
+
if (r<0)
return buf_len-1;
else
@@ -703,7 +722,7 @@ change_callback_log_severity(int loglevelMin, int loglevelMax,
UNLOCK_LOGS();
}
-/** If there are any log messages that were genered with LD_NOCB waiting to
+/** If there are any log messages that were generated with LD_NOCB waiting to
* be sent to callback-based loggers, send them now. */
void
flush_pending_log_callbacks(void)
@@ -803,7 +822,7 @@ add_file_log(const log_severity_list_t *severity, const char *filename)
int fd;
logfile_t *lf;
- fd = open(filename, O_WRONLY|O_CREAT|O_APPEND, 0644);
+ fd = tor_open_cloexec(filename, O_WRONLY|O_CREAT|O_APPEND, 0644);
if (fd<0)
return -1;
if (tor_fd_seekend(fd)<0)
@@ -880,7 +899,7 @@ log_level_to_string(int level)
static const char *domain_list[] = {
"GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM",
"HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV",
- "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", NULL
+ "OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", NULL
};
/** Return a bitmask for the log domain for which <b>domain</b> is the name,
diff --git a/src/common/mempool.c b/src/common/mempool.c
index c44492318..30d778804 100644
--- a/src/common/mempool.c
+++ b/src/common/mempool.c
@@ -137,7 +137,7 @@ struct mp_chunk_t {
int capacity; /**< Number of items that can be fit into this chunk. */
size_t mem_size; /**< Number of usable bytes in mem. */
char *next_mem; /**< Pointer into part of <b>mem</b> not yet carved up. */
- char mem[1]; /**< Storage for this chunk. (Not actual size.) */
+ char mem[FLEXIBLE_ARRAY_MEMBER]; /**< Storage for this chunk. */
};
/** Number of extra bytes needed beyond mem_size to allocate a chunk. */
diff --git a/src/common/torgzip.c b/src/common/torgzip.c
index a26e5b21e..62e4a0703 100644
--- a/src/common/torgzip.c
+++ b/src/common/torgzip.c
@@ -44,11 +44,7 @@
#define off64_t int64_t
#endif
-#ifdef _MSC_VER
-#include "..\..\contrib\zlib\zlib.h"
-#else
#include <zlib.h>
-#endif
/** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
* set to -1 if we haven't checked yet. */
diff --git a/src/common/torint.h b/src/common/torint.h
index 0b5c29adc..af975471f 100644
--- a/src/common/torint.h
+++ b/src/common/torint.h
@@ -111,6 +111,15 @@ typedef signed int int32_t;
typedef unsigned int uint32_t;
#define HAVE_UINT32_T
#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX 0xffffu
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX 0x7fff
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-INT16_MAX-1)
+#endif
#ifndef UINT32_MAX
#define UINT32_MAX 0xffffffffu
#endif
diff --git a/src/common/torlog.h b/src/common/torlog.h
index 541a0d173..4c5729ef5 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -92,8 +92,10 @@
#define LD_HIST (1u<<18)
/** OR handshaking */
#define LD_HANDSHAKE (1u<<19)
+/** Heartbeat messages */
+#define LD_HEARTBEAT (1u<<20)
/** Number of logging domains in the code. */
-#define N_LOGGING_DOMAINS 20
+#define N_LOGGING_DOMAINS 21
/** This log message is not safe to send to a callback-based logger
* immediately. Used as a flag, not a log domain. */
@@ -145,6 +147,7 @@ void change_callback_log_severity(int loglevelMin, int loglevelMax,
log_callback cb);
void flush_pending_log_callbacks(void);
void log_set_application_name(const char *name);
+void set_log_time_granularity(int granularity_msec);
void tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
CHECK_PRINTF(3,4);
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 352c3d617..7aaa4e089 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -22,8 +22,12 @@
#include <assert.h>
#ifdef MS_WINDOWS /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
+ #ifndef WIN32_WINNT
#define WIN32_WINNT 0x400
+ #endif
+ #ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x400
+ #endif
#define WIN32_LEAN_AND_MEAN
#if defined(_MSC_VER) && (_MSC_VER < 1300)
#include <winsock.h>
@@ -44,14 +48,21 @@
#error "We require OpenSSL >= 0.9.7"
#endif
+#ifdef USE_BUFFEREVENTS
+#include <event2/bufferevent_ssl.h>
+#include <event2/buffer.h>
+#include <event2/event.h>
+#include "compat_libevent.h"
+#endif
+
#define CRYPTO_PRIVATE /* to import prototypes from crypto.h */
+#define TORTLS_PRIVATE
#include "crypto.h"
#include "tortls.h"
#include "util.h"
#include "torlog.h"
#include "container.h"
-#include "ht.h"
#include <string.h>
/* Enable the "v2" TLS handshake.
@@ -86,22 +97,36 @@ static int use_unsafe_renegotiation_op = 0;
* SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */
static int use_unsafe_renegotiation_flag = 0;
+/** Structure that we use for a single certificate. */
+struct tor_cert_t {
+ X509 *cert;
+ uint8_t *encoded;
+ size_t encoded_len;
+ unsigned pkey_digests_set : 1;
+ digests_t cert_digests;
+ digests_t pkey_digests;
+};
+
/** Holds a SSL_CTX object and related state used to configure TLS
* connections.
*/
typedef struct tor_tls_context_t {
int refcnt;
SSL_CTX *ctx;
- X509 *my_cert;
- X509 *my_id_cert;
- crypto_pk_env_t *key;
+ tor_cert_t *my_link_cert;
+ tor_cert_t *my_id_cert;
+ tor_cert_t *my_auth_cert;
+ crypto_pk_env_t *link_key;
+ crypto_pk_env_t *auth_key;
} tor_tls_context_t;
+#define TOR_TLS_MAGIC 0x71571571
+
/** Holds a SSL object and its associated data. Members are only
* accessed from within tortls.c.
*/
struct tor_tls_t {
- HT_ENTRY(tor_tls_t) node;
+ uint32_t magic;
tor_tls_context_t *context; /** A link to the context object for this tls. */
SSL *ssl; /**< An OpenSSL SSL object. */
int socket; /**< The underlying file descriptor for this TLS connection. */
@@ -109,6 +134,7 @@ struct tor_tls_t {
enum {
TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
+ TOR_TLS_ST_BUFFEREVENT
} state : 3; /**< The current SSL state, depending on which operations have
* completed successfully. */
unsigned int isServer:1; /**< True iff this is a server-side connection */
@@ -117,8 +143,10 @@ struct tor_tls_t {
* of the connection protocol (client sends
* different cipher list, server sends only
* one certificate). */
- /** True iff we should call negotiated_callback when we're done reading. */
+ /** True iff we should call negotiated_callback when we're done reading. */
unsigned int got_renegotiate:1;
+ /** Incremented every time we start the server side of a handshake. */
+ uint8_t server_handshake_count;
size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
* time. */
/** Last values retrieved from BIO_number_read()/write(); see
@@ -143,42 +171,29 @@ static SSL_CIPHER *CLIENT_CIPHER_DUMMIES = NULL;
static STACK_OF(SSL_CIPHER) *CLIENT_CIPHER_STACK = NULL;
#endif
-/** Helper: compare tor_tls_t objects by its SSL. */
-static INLINE int
-tor_tls_entries_eq(const tor_tls_t *a, const tor_tls_t *b)
-{
- return a->ssl == b->ssl;
-}
+/** The ex_data index in which we store a pointer to an SSL object's
+ * corresponding tor_tls_t object. */
+static int tor_tls_object_ex_data_index = -1;
-/** Helper: return a hash value for a tor_tls_t by its SSL. */
-static INLINE unsigned int
-tor_tls_entry_hash(const tor_tls_t *a)
+/** Helper: Allocate tor_tls_object_ex_data_index. */
+static void
+tor_tls_allocate_tor_tls_object_ex_data_index(void)
{
-#if SIZEOF_INT == SIZEOF_VOID_P
- return ((unsigned int)(uintptr_t)a->ssl);
-#else
- return (unsigned int) ((((uint64_t)a->ssl)>>2) & UINT_MAX);
-#endif
+ if (tor_tls_object_ex_data_index == -1) {
+ tor_tls_object_ex_data_index =
+ SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ tor_assert(tor_tls_object_ex_data_index != -1);
+ }
}
-/** Map from SSL* pointers to tor_tls_t objects using those pointers.
- */
-static HT_HEAD(tlsmap, tor_tls_t) tlsmap_root = HT_INITIALIZER();
-
-HT_PROTOTYPE(tlsmap, tor_tls_t, node, tor_tls_entry_hash,
- tor_tls_entries_eq)
-HT_GENERATE(tlsmap, tor_tls_t, node, tor_tls_entry_hash,
- tor_tls_entries_eq, 0.6, malloc, realloc, free)
-
/** Helper: given a SSL* pointer, return the tor_tls_t object using that
* pointer. */
static INLINE tor_tls_t *
tor_tls_get_by_ssl(const SSL *ssl)
{
- tor_tls_t search, *result;
- memset(&search, 0, sizeof(search));
- search.ssl = (SSL*)ssl;
- result = HT_FIND(tlsmap, &tlsmap_root, &search);
+ tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index);
+ if (result)
+ tor_assert(result->magic == TOR_TLS_MAGIC);
return result;
}
@@ -189,7 +204,7 @@ static X509* tor_tls_create_certificate(crypto_pk_env_t *rsa,
const char *cname,
const char *cname_sign,
unsigned int lifetime);
-static void tor_tls_unblock_renegotiation(tor_tls_t *tls);
+
static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
crypto_pk_env_t *identity,
unsigned int key_lifetime,
@@ -197,11 +212,13 @@ static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
static tor_tls_context_t *tor_tls_context_new(crypto_pk_env_t *identity,
unsigned int key_lifetime,
int is_client);
+static int check_cert_lifetime_internal(const X509 *cert, int tolerance);
/** Global TLS contexts. We keep them here because nobody else needs
* to touch them. */
static tor_tls_context_t *server_tls_context = NULL;
static tor_tls_context_t *client_tls_context = NULL;
+
/** True iff tor_tls_init() has been called. */
static int tls_library_is_initialized = 0;
@@ -225,36 +242,96 @@ ssl_state_to_string(int ssl_state)
return buf;
}
+/** Write a description of the current state of <b>tls</b> into the
+ * <b>sz</b>-byte buffer at <b>buf</b>. */
+void
+tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz)
+{
+ const char *ssl_state;
+ const char *tortls_state;
+
+ if (PREDICT_UNLIKELY(!tls || !tls->ssl)) {
+ strlcpy(buf, "(No SSL object)", sz);
+ return;
+ }
+
+ ssl_state = ssl_state_to_string(tls->ssl->state);
+ switch (tls->state) {
+#define CASE(st) case TOR_TLS_ST_##st: tortls_state = " in "#st ; break
+ CASE(HANDSHAKE);
+ CASE(OPEN);
+ CASE(GOTCLOSE);
+ CASE(SENTCLOSE);
+ CASE(CLOSED);
+ CASE(RENEGOTIATE);
+#undef CASE
+ case TOR_TLS_ST_BUFFEREVENT:
+ tortls_state = "";
+ break;
+ default:
+ tortls_state = " in unknown TLS state";
+ break;
+ }
+
+ tor_snprintf(buf, sz, "%s%s", ssl_state, tortls_state);
+}
+
+void
+tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
+ int severity, int domain, const char *doing)
+{
+ const char *state = NULL, *addr;
+ const char *msg, *lib, *func;
+ int st;
+
+ st = (tls && tls->ssl) ? tls->ssl->state : -1;
+ state = (st>=0)?ssl_state_to_string(st):"---";
+
+ addr = tls ? tls->address : NULL;
+
+ /* Some errors are known-benign, meaning they are the fault of the other
+ * side of the connection. The caller doesn't know this, so override the
+ * priority for those cases. */
+ switch (ERR_GET_REASON(err)) {
+ case SSL_R_HTTP_REQUEST:
+ case SSL_R_HTTPS_PROXY_REQUEST:
+ case SSL_R_RECORD_LENGTH_MISMATCH:
+ case SSL_R_RECORD_TOO_LARGE:
+ case SSL_R_UNKNOWN_PROTOCOL:
+ case SSL_R_UNSUPPORTED_PROTOCOL:
+ severity = LOG_INFO;
+ break;
+ default:
+ break;
+ }
+
+ msg = (const char*)ERR_reason_error_string(err);
+ lib = (const char*)ERR_lib_error_string(err);
+ func = (const char*)ERR_func_error_string(err);
+ if (!msg) msg = "(null)";
+ if (!lib) lib = "(null)";
+ if (!func) func = "(null)";
+ if (doing) {
+ log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)",
+ doing, addr?" with ":"", addr?addr:"",
+ msg, lib, func, state);
+ } else {
+ log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)",
+ addr?" with ":"", addr?addr:"",
+ msg, lib, func, state);
+ }
+}
+
/** Log all pending tls errors at level <b>severity</b>. Use
* <b>doing</b> to describe our current activities.
*/
static void
tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
{
- const char *state = NULL;
- int st;
unsigned long err;
- const char *msg, *lib, *func, *addr;
- addr = tls ? tls->address : NULL;
- st = (tls && tls->ssl) ? tls->ssl->state : -1;
+
while ((err = ERR_get_error()) != 0) {
- msg = (const char*)ERR_reason_error_string(err);
- lib = (const char*)ERR_lib_error_string(err);
- func = (const char*)ERR_func_error_string(err);
- if (!state)
- state = (st>=0)?ssl_state_to_string(st):"---";
- if (!msg) msg = "(null)";
- if (!lib) lib = "(null)";
- if (!func) func = "(null)";
- if (doing) {
- log(severity, domain, "TLS error while %s%s%s: %s (in %s:%s:%s)",
- doing, addr?" with ":"", addr?addr:"",
- msg, lib, func, state);
- } else {
- log(severity, domain, "TLS error%s%s: %s (in %s:%s:%s)",
- addr?" with ":"", addr?addr:"",
- msg, lib, func, state);
- }
+ tor_tls_log_one_error(tls, err, severity, domain, doing);
}
}
@@ -429,6 +506,8 @@ tor_tls_init(void)
SSLeay_version(SSLEAY_VERSION), version);
}
+ tor_tls_allocate_tor_tls_object_ex_data_index();
+
tls_library_is_initialized = 1;
}
}
@@ -447,10 +526,6 @@ tor_tls_free_all(void)
client_tls_context = NULL;
tor_tls_context_decref(ctx);
}
- if (!HT_EMPTY(&tlsmap_root)) {
- log_warn(LD_MM, "Still have entries in the tlsmap at shutdown.");
- }
- HT_CLEAR(tlsmap, &tlsmap_root);
#ifdef V2_HANDSHAKE_CLIENT
if (CLIENT_CIPHER_DUMMIES)
tor_free(CLIENT_CIPHER_DUMMIES);
@@ -610,6 +685,137 @@ static const int N_CLIENT_CIPHERS =
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
#endif
+/** Free all storage held in <b>cert</b> */
+void
+tor_cert_free(tor_cert_t *cert)
+{
+ if (! cert)
+ return;
+ if (cert->cert)
+ X509_free(cert->cert);
+ tor_free(cert->encoded);
+ memset(cert, 0x03, sizeof(cert));
+ tor_free(cert);
+}
+
+/**
+ * Allocate a new tor_cert_t to hold the certificate "x509_cert".
+ *
+ * Steals a reference to x509_cert.
+ */
+static tor_cert_t *
+tor_cert_new(X509 *x509_cert)
+{
+ tor_cert_t *cert;
+ EVP_PKEY *pkey;
+ RSA *rsa;
+ int length, length2;
+ unsigned char *cp;
+
+ if (!x509_cert)
+ return NULL;
+
+ length = i2d_X509(x509_cert, NULL);
+ cert = tor_malloc_zero(sizeof(tor_cert_t));
+ if (length <= 0) {
+ tor_free(cert);
+ log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
+ X509_free(x509_cert);
+ return NULL;
+ }
+ cert->encoded_len = (size_t) length;
+ cp = cert->encoded = tor_malloc(length);
+ length2 = i2d_X509(x509_cert, &cp);
+ tor_assert(length2 == length);
+
+ cert->cert = x509_cert;
+
+ crypto_digest_all(&cert->cert_digests,
+ (char*)cert->encoded, cert->encoded_len);
+
+ if ((pkey = X509_get_pubkey(x509_cert)) &&
+ (rsa = EVP_PKEY_get1_RSA(pkey))) {
+ crypto_pk_env_t *pk = _crypto_new_pk_env_rsa(rsa);
+ crypto_pk_get_all_digests(pk, &cert->pkey_digests);
+ cert->pkey_digests_set = 1;
+ crypto_free_pk_env(pk);
+ EVP_PKEY_free(pkey);
+ }
+
+ return cert;
+}
+
+/** Read a DER-encoded X509 cert, of length exactly <b>certificate_len</b>,
+ * from a <b>certificate</b>. Return a newly allocated tor_cert_t on success
+ * and NULL on failure. */
+tor_cert_t *
+tor_cert_decode(const uint8_t *certificate, size_t certificate_len)
+{
+ X509 *x509;
+ const unsigned char *cp = (const unsigned char *)certificate;
+ tor_cert_t *newcert;
+ tor_assert(certificate);
+
+ if (certificate_len > INT_MAX)
+ return NULL;
+
+#if OPENSSL_VERSION_NUMBER < 0x00908000l
+ /* This ifdef suppresses a type warning. Take out this case once everybody
+ * is using OpenSSL 0.9.8 or later. */
+ x509 = d2i_X509(NULL, (unsigned char**)&cp, (int)certificate_len);
+#else
+ x509 = d2i_X509(NULL, &cp, (int)certificate_len);
+#endif
+ if (!x509)
+ return NULL; /* Couldn't decode */
+ if (cp - certificate != (int)certificate_len) {
+ X509_free(x509);
+ return NULL; /* Didn't use all the bytes */
+ }
+ newcert = tor_cert_new(x509);
+ if (!newcert) {
+ return NULL;
+ }
+ if (newcert->encoded_len != certificate_len ||
+ fast_memneq(newcert->encoded, certificate, certificate_len)) {
+ /* Cert wasn't in DER */
+ tor_cert_free(newcert);
+ return NULL;
+ }
+ return newcert;
+}
+
+/** Set *<b>encoded_out</b> and *<b>size_out/b> to <b>cert</b>'s encoded DER
+ * representation and length, respectively. */
+void
+tor_cert_get_der(const tor_cert_t *cert,
+ const uint8_t **encoded_out, size_t *size_out)
+{
+ tor_assert(cert);
+ tor_assert(encoded_out);
+ tor_assert(size_out);
+ *encoded_out = cert->encoded;
+ *size_out = cert->encoded_len;
+}
+
+/** Return a set of digests for the public key in <b>cert</b>, or NULL if this
+ * cert's public key is not one we know how to take the digest of. */
+const digests_t *
+tor_cert_get_id_digests(const tor_cert_t *cert)
+{
+ if (cert->pkey_digests_set)
+ return &cert->pkey_digests;
+ else
+ return NULL;
+}
+
+/** Return a set of digests for the public key in <b>cert</b>. */
+const digests_t *
+tor_cert_get_cert_digests(const tor_cert_t *cert)
+{
+ return &cert->cert_digests;
+}
+
/** Remove a reference to <b>ctx</b>, and free it if it has no more
* references. */
static void
@@ -618,13 +824,171 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
tor_assert(ctx);
if (--ctx->refcnt == 0) {
SSL_CTX_free(ctx->ctx);
- X509_free(ctx->my_cert);
- X509_free(ctx->my_id_cert);
- crypto_free_pk_env(ctx->key);
+ tor_cert_free(ctx->my_link_cert);
+ tor_cert_free(ctx->my_id_cert);
+ tor_cert_free(ctx->my_auth_cert);
+ crypto_free_pk_env(ctx->link_key);
+ crypto_free_pk_env(ctx->auth_key);
tor_free(ctx);
}
}
+/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
+ * and ID certificate that we're currently using for our V3 in-protocol
+ * handshake's certificate chain. If <b>server</b> is true, provide the certs
+ * that we use in server mode; otherwise, provide the certs that we use in
+ * client mode. */
+int
+tor_tls_get_my_certs(int server,
+ const tor_cert_t **link_cert_out,
+ const tor_cert_t **id_cert_out)
+{
+ tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context;
+ if (! ctx)
+ return -1;
+ if (link_cert_out)
+ *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert;
+ if (id_cert_out)
+ *id_cert_out = ctx->my_id_cert;
+ return 0;
+}
+
+/**
+ * Return the authentication key that we use to authenticate ourselves as a
+ * client in the V3 in-protocol handshake.
+ */
+crypto_pk_env_t *
+tor_tls_get_my_client_auth_key(void)
+{
+ if (! client_tls_context)
+ return NULL;
+ return client_tls_context->auth_key;
+}
+
+/**
+ * Return a newly allocated copy of the public key that a certificate
+ * certifies. Return NULL if the cert's key is not RSA.
+ */
+crypto_pk_env_t *
+tor_tls_cert_get_key(tor_cert_t *cert)
+{
+ crypto_pk_env_t *result = NULL;
+ EVP_PKEY *pkey = X509_get_pubkey(cert->cert);
+ RSA *rsa;
+ if (!pkey)
+ return NULL;
+ rsa = EVP_PKEY_get1_RSA(pkey);
+ if (!rsa) {
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ result = _crypto_new_pk_env_rsa(rsa);
+ EVP_PKEY_free(pkey);
+ return result;
+}
+
+/** Return true iff <b>a</b> and <b>b</b> represent the same public key. */
+static int
+pkey_eq(EVP_PKEY *a, EVP_PKEY *b)
+{
+ /* We'd like to do this, but openssl 0.9.7 doesn't have it:
+ return EVP_PKEY_cmp(a,b) == 1;
+ */
+ unsigned char *a_enc=NULL, *b_enc=NULL, *a_ptr, *b_ptr;
+ int a_len1, b_len1, a_len2, b_len2, result;
+ a_len1 = i2d_PublicKey(a, NULL);
+ b_len1 = i2d_PublicKey(b, NULL);
+ if (a_len1 != b_len1)
+ return 0;
+ a_ptr = a_enc = tor_malloc(a_len1);
+ b_ptr = b_enc = tor_malloc(b_len1);
+ a_len2 = i2d_PublicKey(a, &a_ptr);
+ b_len2 = i2d_PublicKey(b, &b_ptr);
+ tor_assert(a_len2 == a_len1);
+ tor_assert(b_len2 == b_len1);
+ result = tor_memeq(a_enc, b_enc, a_len1);
+ tor_free(a_enc);
+ tor_free(b_enc);
+ return result;
+}
+
+/** Return true iff the other side of <b>tls</b> has authenticated to us, and
+ * the key certified in <b>cert</b> is the same as the key they used to do it.
+ */
+int
+tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert)
+{
+ X509 *peercert = SSL_get_peer_certificate(tls->ssl);
+ EVP_PKEY *link_key = NULL, *cert_key = NULL;
+ int result;
+
+ if (!peercert)
+ return 0;
+ link_key = X509_get_pubkey(peercert);
+ cert_key = X509_get_pubkey(cert->cert);
+
+ result = link_key && cert_key && pkey_eq(cert_key, link_key);
+
+ X509_free(peercert);
+ if (link_key)
+ EVP_PKEY_free(link_key);
+ if (cert_key)
+ EVP_PKEY_free(cert_key);
+
+ return result;
+}
+
+/** Check whether <b>cert</b> is well-formed, currently live, and correctly
+ * signed by the public key in <b>signing_cert</b>. If <b>check_rsa_1024</b>,
+ * make sure that it has an RSA key with 1024 bits; otherwise, just check that
+ * the key is long enough. Return 1 if the cert is good, and 0 if it's bad or
+ * we couldn't check it. */
+int
+tor_tls_cert_is_valid(const tor_cert_t *cert,
+ const tor_cert_t *signing_cert,
+ int check_rsa_1024)
+{
+ EVP_PKEY *cert_key;
+ EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
+ int r, key_ok = 0;
+ if (!signing_key)
+ return 0;
+ r = X509_verify(cert->cert, signing_key);
+ EVP_PKEY_free(signing_key);
+ if (r <= 0)
+ return 0;
+
+ /* okay, the signature checked out right. Now let's check the check the
+ * lifetime. */
+ /*XXXX tolerance might be iffy here */
+ if (check_cert_lifetime_internal(cert->cert, 60*60) < 0)
+ return 0;
+
+ cert_key = X509_get_pubkey(cert->cert);
+ if (check_rsa_1024 && cert_key) {
+ RSA *rsa = EVP_PKEY_get1_RSA(cert_key);
+ if (rsa && BN_num_bits(rsa->n) == 1024)
+ key_ok = 1;
+ if (rsa)
+ RSA_free(rsa);
+ } else if (cert_key) {
+ int min_bits = 1024;
+#ifdef EVP_PKEY_EC
+ if (EVP_PKEY_type(cert_key->type) == EVP_PKEY_EC)
+ min_bits = 128;
+#endif
+ if (EVP_PKEY_bits(cert_key) >= min_bits)
+ key_ok = 1;
+ }
+ EVP_PKEY_free(cert_key);
+ if (!key_ok)
+ return 0;
+
+ /* XXXX compare DNs or anything? */
+
+ return 1;
+}
+
/** Increase the reference count of <b>ctx</b>. */
static void
tor_tls_context_incref(tor_tls_context_t *ctx)
@@ -730,29 +1094,42 @@ static tor_tls_context_t *
tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
int is_client)
{
- crypto_pk_env_t *rsa = NULL;
+ crypto_pk_env_t *rsa = NULL, *rsa_auth = NULL;
EVP_PKEY *pkey = NULL;
tor_tls_context_t *result = NULL;
- X509 *cert = NULL, *idcert = NULL;
+ X509 *cert = NULL, *idcert = NULL, *authcert = NULL;
char *nickname = NULL, *nn2 = NULL;
tor_tls_init();
nickname = crypto_random_hostname(8, 20, "www.", ".net");
+#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
nn2 = crypto_random_hostname(8, 20, "www.", ".net");
+#else
+ nn2 = crypto_random_hostname(8, 20, "www.", ".com");
+#endif
- /* Generate short-term RSA key. */
+ /* Generate short-term RSA key for use with TLS. */
if (!(rsa = crypto_new_pk_env()))
goto error;
if (crypto_pk_generate_key(rsa)<0)
goto error;
if (!is_client) {
- /* Create certificate signed by identity key. */
+ /* Generate short-term RSA key for use in the in-protocol ("v3")
+ * authentication handshake. */
+ if (!(rsa_auth = crypto_new_pk_env()))
+ goto error;
+ if (crypto_pk_generate_key(rsa_auth)<0)
+ goto error;
+ /* Create a link certificate signed by identity key. */
cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
key_lifetime);
/* Create self-signed certificate for identity key. */
idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
IDENTITY_CERT_LIFETIME);
- if (!cert || !idcert) {
+ /* Create an authentication certificate signed by identity key. */
+ authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2,
+ key_lifetime);
+ if (!cert || !idcert || !authcert) {
log(LOG_WARN, LD_CRYPTO, "Error creating certificate");
goto error;
}
@@ -761,9 +1138,13 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
result = tor_malloc_zero(sizeof(tor_tls_context_t));
result->refcnt = 1;
if (!is_client) {
- result->my_cert = X509_dup(cert);
- result->my_id_cert = X509_dup(idcert);
- result->key = crypto_pk_dup_key(rsa);
+ result->my_link_cert = tor_cert_new(X509_dup(cert));
+ result->my_id_cert = tor_cert_new(X509_dup(idcert));
+ result->my_auth_cert = tor_cert_new(X509_dup(authcert));
+ if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert)
+ goto error;
+ result->link_key = crypto_pk_dup_key(rsa);
+ result->auth_key = crypto_pk_dup_key(rsa_auth);
}
#ifdef EVERYONE_HAS_AES
@@ -834,6 +1215,9 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
if (rsa)
crypto_free_pk_env(rsa);
+ if (rsa_auth)
+ crypto_free_pk_env(rsa_auth);
+ X509_free(authcert);
tor_free(nickname);
tor_free(nn2);
return result;
@@ -846,12 +1230,16 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime,
EVP_PKEY_free(pkey);
if (rsa)
crypto_free_pk_env(rsa);
+ if (rsa_auth)
+ crypto_free_pk_env(rsa_auth);
if (result)
tor_tls_context_decref(result);
if (cert)
X509_free(cert);
if (idcert)
X509_free(idcert);
+ if (authcert)
+ X509_free(authcert);
return NULL;
}
@@ -907,6 +1295,13 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
return 1;
}
+static void
+tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
+{
+ log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
+ ssl, ssl_state_to_string(ssl->state), type, val);
+}
+
/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
* changes state. We use this:
* <ul><li>To alter the state of the handshake partway through, so we
@@ -918,6 +1313,9 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
{
tor_tls_t *tls;
(void) val;
+
+ tor_tls_debug_state_callback(ssl, type, val);
+
if (type != SSL_CB_ACCEPT_LOOP)
return;
if (ssl->state != SSL3_ST_SW_SRVR_HELLO_A)
@@ -928,8 +1326,11 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
/* Check whether we're watching for renegotiates. If so, this is one! */
if (tls->negotiated_callback)
tls->got_renegotiate = 1;
+ if (tls->server_handshake_count < 127) /*avoid any overflow possibility*/
+ ++tls->server_handshake_count;
} else {
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
+ return;
}
/* Now check the cipher list. */
@@ -946,6 +1347,10 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (tls) {
tls->wasV2Handshake = 1;
+#ifdef USE_BUFFEREVENTS
+ if (use_unsafe_renegotiation_flag)
+ tls->ssl->s3->flags |= SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+#endif
} else {
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
}
@@ -1033,6 +1438,7 @@ tor_tls_new(int sock, int isServer)
tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t));
tor_tls_context_t *context = isServer ? server_tls_context :
client_tls_context;
+ result->magic = TOR_TLS_MAGIC;
tor_assert(context); /* make sure somebody made it first */
if (!(result->ssl = SSL_new(context->ctx))) {
@@ -1073,7 +1479,14 @@ tor_tls_new(int sock, int isServer)
tor_free(result);
return NULL;
}
- HT_INSERT(tlsmap, &tlsmap_root, result);
+ {
+ int set_worked =
+ SSL_set_ex_data(result->ssl, tor_tls_object_ex_data_index, result);
+ if (!set_worked) {
+ log_warn(LD_BUG,
+ "Couldn't set the tls for an SSL*; connection will fail");
+ }
+ }
SSL_set_bio(result->ssl, bio, bio);
tor_tls_context_incref(context);
result->context = context;
@@ -1089,8 +1502,11 @@ tor_tls_new(int sock, int isServer)
#ifdef V2_HANDSHAKE_SERVER
if (isServer) {
SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
- }
+ } else
#endif
+ {
+ SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback);
+ }
/* Not expected to get called. */
tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object");
@@ -1124,7 +1540,7 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
if (cb) {
SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback);
} else {
- SSL_set_info_callback(tls->ssl, NULL);
+ SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback);
}
#endif
}
@@ -1132,7 +1548,7 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
/** If this version of openssl requires it, turn on renegotiation on
* <b>tls</b>.
*/
-static void
+void
tor_tls_unblock_renegotiation(tor_tls_t *tls)
{
/* Yes, we know what we are doing here. No, we do not treat a renegotiation
@@ -1156,6 +1572,19 @@ tor_tls_block_renegotiation(tor_tls_t *tls)
tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
}
+void
+tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
+{
+ if (use_unsafe_renegotiation_flag) {
+ tor_assert(0 != (tls->ssl->s3->flags &
+ SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
+ }
+ if (use_unsafe_renegotiation_op) {
+ long options = SSL_get_options(tls->ssl);
+ tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
+ }
+}
+
/** Return whether this tls initiated the connect (client) or
* received it (server). */
int
@@ -1171,14 +1600,9 @@ tor_tls_is_server(tor_tls_t *tls)
void
tor_tls_free(tor_tls_t *tls)
{
- tor_tls_t *removed;
if (!tls)
return;
tor_assert(tls->ssl);
- removed = HT_REMOVE(tlsmap, &tlsmap_root, tls);
- if (!removed) {
- log_warn(LD_BUG, "Freeing a TLS that was not in the ssl->tls map.");
- }
#ifdef SSL_set_tlsext_host_name
SSL_set_tlsext_host_name(tls->ssl, NULL);
#endif
@@ -1188,6 +1612,7 @@ tor_tls_free(tor_tls_t *tls)
if (tls->context)
tor_tls_context_decref(tls->context);
tor_free(tls->address);
+ tls->magic = 0x99999999;
tor_free(tls);
}
@@ -1300,56 +1725,86 @@ tor_tls_handshake(tor_tls_t *tls)
}
if (r == TOR_TLS_DONE) {
tls->state = TOR_TLS_ST_OPEN;
- if (tls->isServer) {
- SSL_set_info_callback(tls->ssl, NULL);
- SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
- /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */
- tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
+ return tor_tls_finish_handshake(tls);
+ }
+ return r;
+}
+
+/** Perform the final part of the intial TLS handshake on <b>tls</b>. This
+ * should be called for the first handshake only: it determines whether the v1
+ * or the v2 handshake was used, and adjusts things for the renegotiation
+ * handshake as appropriate.
+ *
+ * tor_tls_handshake() calls this on its own; you only need to call this if
+ * bufferevent is doing the handshake for you.
+ */
+int
+tor_tls_finish_handshake(tor_tls_t *tls)
+{
+ int r = TOR_TLS_DONE;
+ if (tls->isServer) {
+ SSL_set_info_callback(tls->ssl, NULL);
+ SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
+ /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */
+ tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
#ifdef V2_HANDSHAKE_SERVER
- if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) {
- /* This check is redundant, but back when we did it in the callback,
- * we might have not been able to look up the tor_tls_t if the code
- * was buggy. Fixing that. */
- if (!tls->wasV2Handshake) {
- log_warn(LD_BUG, "For some reason, wasV2Handshake didn't"
- " get set. Fixing that.");
- }
- tls->wasV2Handshake = 1;
- log_debug(LD_HANDSHAKE,
- "Completed V2 TLS handshake with client; waiting "
- "for renegotiation.");
- } else {
- tls->wasV2Handshake = 0;
+ if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) {
+ /* This check is redundant, but back when we did it in the callback,
+ * we might have not been able to look up the tor_tls_t if the code
+ * was buggy. Fixing that. */
+ if (!tls->wasV2Handshake) {
+ log_warn(LD_BUG, "For some reason, wasV2Handshake didn't"
+ " get set. Fixing that.");
}
-#endif
+ tls->wasV2Handshake = 1;
+ log_debug(LD_HANDSHAKE, "Completed V2 TLS handshake with client; waiting"
+ " for renegotiation.");
} else {
+ tls->wasV2Handshake = 0;
+ }
+#endif
+ } else {
#ifdef V2_HANDSHAKE_CLIENT
- /* If we got no ID cert, we're a v2 handshake. */
- X509 *cert = SSL_get_peer_certificate(tls->ssl);
- STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl);
- int n_certs = sk_X509_num(chain);
- if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) {
- log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it "
- "looks like a v1 handshake on %p", tls);
- tls->wasV2Handshake = 0;
- } else {
- log_debug(LD_HANDSHAKE,
- "Server sent back a single certificate; looks like "
- "a v2 handshake on %p.", tls);
- tls->wasV2Handshake = 1;
- }
- if (cert)
- X509_free(cert);
+ /* If we got no ID cert, we're a v2 handshake. */
+ X509 *cert = SSL_get_peer_certificate(tls->ssl);
+ STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl);
+ int n_certs = sk_X509_num(chain);
+ if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) {
+ log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it "
+ "looks like a v1 handshake on %p", tls);
+ tls->wasV2Handshake = 0;
+ } else {
+ log_debug(LD_HANDSHAKE,
+ "Server sent back a single certificate; looks like "
+ "a v2 handshake on %p.", tls);
+ tls->wasV2Handshake = 1;
+ }
+ if (cert)
+ X509_free(cert);
#endif
- if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
- tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers");
- r = TOR_TLS_ERROR_MISC;
- }
+ if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
+ tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers");
+ r = TOR_TLS_ERROR_MISC;
}
}
return r;
}
+#ifdef USE_BUFFEREVENTS
+/** Put <b>tls</b>, which must be a client connection, into renegotiation
+ * mode. */
+int
+tor_tls_start_renegotiating(tor_tls_t *tls)
+{
+ int r = SSL_renegotiate(tls->ssl);
+ if (r <= 0) {
+ return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN,
+ LD_HANDSHAKE);
+ }
+ return 0;
+}
+#endif
+
/** Client only: Renegotiate a TLS session. When finished, returns
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, or
* TOR_TLS_WANTWRITE.
@@ -1455,9 +1910,21 @@ tor_tls_peer_has_cert(tor_tls_t *tls)
return 1;
}
+/** Return the peer certificate, or NULL if there isn't one. */
+tor_cert_t *
+tor_tls_get_peer_cert(tor_tls_t *tls)
+{
+ X509 *cert;
+ cert = SSL_get_peer_certificate(tls->ssl);
+ tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate");
+ if (!cert)
+ return NULL;
+ return tor_cert_new(cert);
+}
+
/** Warn that a certificate lifetime extends through a certain range. */
static void
-log_cert_lifetime(X509 *cert, const char *problem)
+log_cert_lifetime(const X509 *cert, const char *problem)
{
BIO *bio = NULL;
BUF_MEM *buf;
@@ -1566,6 +2033,8 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
goto done;
}
+ tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate");
+
if (!(id_pkey = X509_get_pubkey(id_cert)) ||
X509_verify(cert, id_pkey) <= 0) {
log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
@@ -1602,25 +2071,14 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
int
tor_tls_check_lifetime(tor_tls_t *tls, int tolerance)
{
- time_t now, t;
X509 *cert;
int r = -1;
- now = time(NULL);
-
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
goto done;
- t = now + tolerance;
- if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) {
- log_cert_lifetime(cert, "not yet valid");
+ if (check_cert_lifetime_internal(cert, tolerance) < 0)
goto done;
- }
- t = now - tolerance;
- if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) {
- log_cert_lifetime(cert, "already expired");
- goto done;
- }
r = 0;
done:
@@ -1632,6 +2090,30 @@ tor_tls_check_lifetime(tor_tls_t *tls, int tolerance)
return r;
}
+/** Helper: check whether <b>cert</b> is currently live, give or take
+ * <b>tolerance</b> seconds. If it is live, return 0. If it is not live,
+ * log a message and return -1. */
+static int
+check_cert_lifetime_internal(const X509 *cert, int tolerance)
+{
+ time_t now, t;
+
+ now = time(NULL);
+
+ t = now + tolerance;
+ if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) {
+ log_cert_lifetime(cert, "not yet valid");
+ return -1;
+ }
+ t = now - tolerance;
+ if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) {
+ log_cert_lifetime(cert, "already expired");
+ return -1;
+ }
+
+ return 0;
+}
+
/** Return the number of bytes available for reading from <b>tls</b>.
*/
int
@@ -1715,6 +2197,138 @@ tor_tls_used_v1_handshake(tor_tls_t *tls)
return 1;
}
+/** Return true iff <b>name</b> is a DN of a kind that could only
+ * occur in a v3-handshake-indicating certificate */
+static int
+dn_indicates_v3_cert(X509_NAME *name)
+{
+#ifdef DISABLE_V3_LINKPROTO_CLIENTSIDE
+ (void)name;
+ return 0;
+#else
+ X509_NAME_ENTRY *entry;
+ int n_entries;
+ ASN1_OBJECT *obj;
+ ASN1_STRING *str;
+ unsigned char *s;
+ int len, r;
+
+ n_entries = X509_NAME_entry_count(name);
+ if (n_entries != 1)
+ return 1; /* More than one entry in the DN. */
+ entry = X509_NAME_get_entry(name, 0);
+
+ obj = X509_NAME_ENTRY_get_object(entry);
+ if (OBJ_obj2nid(obj) != OBJ_txt2nid("commonName"))
+ return 1; /* The entry isn't a commonName. */
+
+ str = X509_NAME_ENTRY_get_data(entry);
+ len = ASN1_STRING_to_UTF8(&s, str);
+ if (len < 0)
+ return 0;
+ r = fast_memneq(s + len - 4, ".net", 4);
+ OPENSSL_free(s);
+ return r;
+#endif
+}
+
+/** Return true iff the peer certificate we're received on <b>tls</b>
+ * indicates that this connection should use the v3 (in-protocol)
+ * authentication handshake.
+ *
+ * Only the connection initiator should use this, and only once the initial
+ * handshake is done; the responder detects a v1 handshake by cipher types,
+ * and a v3/v2 handshake by Versions cell vs renegotiation.
+ */
+int
+tor_tls_received_v3_certificate(tor_tls_t *tls)
+{
+ X509 *cert = SSL_get_peer_certificate(tls->ssl);
+ EVP_PKEY *key = NULL;
+ X509_NAME *issuer_name, *subject_name;
+ int is_v3 = 0;
+
+ if (!cert) {
+ log_warn(LD_BUG, "Called on a connection with no peer certificate");
+ goto done;
+ }
+
+ subject_name = X509_get_subject_name(cert);
+ issuer_name = X509_get_issuer_name(cert);
+
+ if (X509_name_cmp(subject_name, issuer_name) == 0) {
+ is_v3 = 1; /* purportedly self signed */
+ goto done;
+ }
+
+ if (dn_indicates_v3_cert(subject_name) ||
+ dn_indicates_v3_cert(issuer_name)) {
+ is_v3 = 1; /* DN is fancy */
+ goto done;
+ }
+
+ key = X509_get_pubkey(cert);
+ if (EVP_PKEY_bits(key) != 1024 ||
+ EVP_PKEY_type(key->type) != EVP_PKEY_RSA) {
+ is_v3 = 1; /* Key is fancy */
+ goto done;
+ }
+
+ done:
+ if (key)
+ EVP_PKEY_free(key);
+ if (cert)
+ X509_free(cert);
+
+ return is_v3;
+}
+
+/** Return the number of server handshakes that we've noticed doing on
+ * <b>tls</b>. */
+int
+tor_tls_get_num_server_handshakes(tor_tls_t *tls)
+{
+ return tls->server_handshake_count;
+}
+
+/** Return true iff the server TLS connection <b>tls</b> got the renegotiation
+ * request it was waiting for. */
+int
+tor_tls_server_got_renegotiate(tor_tls_t *tls)
+{
+ return tls->got_renegotiate;
+}
+
+/** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in
+ * the v3 handshake to prove that the client knows the TLS secrets for the
+ * connection <b>tls</b>. Return 0 on success, -1 on failure.
+ */
+int
+tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out)
+{
+#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
+ char buf[128];
+ size_t len;
+ tor_assert(tls);
+ tor_assert(tls->ssl);
+ tor_assert(tls->ssl->s3);
+ tor_assert(tls->ssl->session);
+ /*
+ The value is an HMAC, using the TLS master key as the HMAC key, of
+ client_random | server_random | TLSSECRET_MAGIC
+ */
+ memcpy(buf + 0, tls->ssl->s3->client_random, 32);
+ memcpy(buf + 32, tls->ssl->s3->server_random, 32);
+ memcpy(buf + 64, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1);
+ len = 64 + strlen(TLSSECRET_MAGIC) + 1;
+ crypto_hmac_sha256((char*)secrets_out,
+ (char*)tls->ssl->session->master_key,
+ tls->ssl->session->master_key_length,
+ buf, len);
+ memset(buf, 0, sizeof(buf));
+ return 0;
+}
+
/** Examine the amount of memory used and available for buffers in <b>tls</b>.
* Set *<b>rbuf_capacity</b> to the amount of storage allocated for the read
* buffer and *<b>rbuf_bytes</b> to the amount actually used.
@@ -1737,3 +2351,75 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
*wbuf_bytes = tls->ssl->s3->wbuf.left;
}
+#ifdef USE_BUFFEREVENTS
+/** Construct and return an TLS-encrypting bufferevent to send data over
+ * <b>socket</b>, which must match the socket of the underlying bufferevent
+ * <b>bufev_in</b>. The TLS object <b>tls</b> is used for encryption.
+ *
+ * This function will either create a filtering bufferevent that wraps around
+ * <b>bufev_in</b>, or it will free bufev_in and return a new bufferevent that
+ * uses the <b>tls</b> to talk to the network directly. Do not use
+ * <b>bufev_in</b> after calling this function.
+ *
+ * The connection will start out doing a server handshake if <b>receiving</b>
+ * is strue, and a client handshake otherwise.
+ *
+ * Returns NULL on failure.
+ */
+struct bufferevent *
+tor_tls_init_bufferevent(tor_tls_t *tls, struct bufferevent *bufev_in,
+ evutil_socket_t socket, int receiving,
+ int filter)
+{
+ struct bufferevent *out;
+ const enum bufferevent_ssl_state state = receiving ?
+ BUFFEREVENT_SSL_ACCEPTING : BUFFEREVENT_SSL_CONNECTING;
+
+ if (filter || tor_libevent_using_iocp_bufferevents()) {
+ /* Grab an extra reference to the SSL, since BEV_OPT_CLOSE_ON_FREE
+ means that the SSL will get freed too.
+
+ This increment makes our SSL usage not-threadsafe, BTW. We should
+ see if we're allowed to use CRYPTO_add from outside openssl. */
+ tls->ssl->references += 1;
+ out = bufferevent_openssl_filter_new(tor_libevent_get_base(),
+ bufev_in,
+ tls->ssl,
+ state,
+ BEV_OPT_DEFER_CALLBACKS|
+ BEV_OPT_CLOSE_ON_FREE);
+ /* Tell the underlying bufferevent when to accept more data from the SSL
+ filter (only when it's got less than 32K to write), and when to notify
+ the SSL filter that it could write more (when it drops under 24K). */
+ bufferevent_setwatermark(bufev_in, EV_WRITE, 24*1024, 32*1024);
+ } else {
+ if (bufev_in) {
+ evutil_socket_t s = bufferevent_getfd(bufev_in);
+ tor_assert(s == -1 || s == socket);
+ tor_assert(evbuffer_get_length(bufferevent_get_input(bufev_in)) == 0);
+ tor_assert(evbuffer_get_length(bufferevent_get_output(bufev_in)) == 0);
+ tor_assert(BIO_number_read(SSL_get_rbio(tls->ssl)) == 0);
+ tor_assert(BIO_number_written(SSL_get_rbio(tls->ssl)) == 0);
+ bufferevent_free(bufev_in);
+ }
+
+ /* Current versions (as of 2.0.x) of Libevent need to defer
+ * bufferevent_openssl callbacks, or else our callback functions will
+ * get called reentrantly, which is bad for us.
+ */
+ out = bufferevent_openssl_socket_new(tor_libevent_get_base(),
+ socket,
+ tls->ssl,
+ state,
+ BEV_OPT_DEFER_CALLBACKS);
+ }
+ tls->state = TOR_TLS_ST_BUFFEREVENT;
+
+ /* Unblock _after_ creating the bufferevent, since accept/connect tend to
+ * clear flags. */
+ tor_tls_unblock_renegotiation(tls);
+
+ return out;
+}
+#endif
+
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 55fee81ae..d0a13cd80 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -17,6 +17,9 @@
/* Opaque structure to hold a TLS connection. */
typedef struct tor_tls_t tor_tls_t;
+/* Opaque structure to hold an X509 certificate. */
+typedef struct tor_cert_t tor_cert_t;
+
/* Possible return values for most tor_tls_* functions. */
#define _MIN_TOR_TLS_ERROR_VAL -9
#define TOR_TLS_ERROR_MISC -9
@@ -48,6 +51,7 @@ typedef struct tor_tls_t tor_tls_t;
#define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE)
const char *tor_tls_err_to_string(int err);
+void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
void tor_tls_free_all(void);
int tor_tls_context_init(int is_public_server,
@@ -62,13 +66,17 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
int tor_tls_is_server(tor_tls_t *tls);
void tor_tls_free(tor_tls_t *tls);
int tor_tls_peer_has_cert(tor_tls_t *tls);
+tor_cert_t *tor_tls_get_peer_cert(tor_tls_t *tls);
int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity);
int tor_tls_check_lifetime(tor_tls_t *tls, int tolerance);
int tor_tls_read(tor_tls_t *tls, char *cp, size_t len);
int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n);
int tor_tls_handshake(tor_tls_t *tls);
+int tor_tls_finish_handshake(tor_tls_t *tls);
int tor_tls_renegotiate(tor_tls_t *tls);
+void tor_tls_unblock_renegotiation(tor_tls_t *tls);
void tor_tls_block_renegotiation(tor_tls_t *tls);
+void tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls);
int tor_tls_shutdown(tor_tls_t *tls);
int tor_tls_get_pending_bytes(tor_tls_t *tls);
size_t tor_tls_get_forced_write_size(tor_tls_t *tls);
@@ -81,12 +89,43 @@ void tor_tls_get_buffer_sizes(tor_tls_t *tls,
size_t *wbuf_capacity, size_t *wbuf_bytes);
int tor_tls_used_v1_handshake(tor_tls_t *tls);
+int tor_tls_received_v3_certificate(tor_tls_t *tls);
+int tor_tls_get_num_server_handshakes(tor_tls_t *tls);
+int tor_tls_server_got_renegotiate(tor_tls_t *tls);
+int tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out);
/* Log and abort if there are unhandled TLS errors in OpenSSL's error stack.
*/
#define check_no_tls_errors() _check_no_tls_errors(__FILE__,__LINE__)
void _check_no_tls_errors(const char *fname, int line);
+void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
+ int severity, int domain, const char *doing);
+
+#ifdef USE_BUFFEREVENTS
+int tor_tls_start_renegotiating(tor_tls_t *tls);
+struct bufferevent *tor_tls_init_bufferevent(tor_tls_t *tls,
+ struct bufferevent *bufev_in,
+ evutil_socket_t socket, int receiving,
+ int filter);
+#endif
+
+void tor_cert_free(tor_cert_t *cert);
+tor_cert_t *tor_cert_decode(const uint8_t *certificate,
+ size_t certificate_len);
+void tor_cert_get_der(const tor_cert_t *cert,
+ const uint8_t **encoded_out, size_t *size_out);
+const digests_t *tor_cert_get_id_digests(const tor_cert_t *cert);
+const digests_t *tor_cert_get_cert_digests(const tor_cert_t *cert);
+int tor_tls_get_my_certs(int server,
+ const tor_cert_t **link_cert_out,
+ const tor_cert_t **id_cert_out);
+crypto_pk_env_t *tor_tls_get_my_client_auth_key(void);
+crypto_pk_env_t *tor_tls_cert_get_key(tor_cert_t *cert);
+int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert);
+int tor_tls_cert_is_valid(const tor_cert_t *cert,
+ const tor_cert_t *signing_cert,
+ int check_rsa_1024);
#endif
diff --git a/src/common/util.c b/src/common/util.c
index de1ca3684..79e09e4f5 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -14,6 +14,10 @@
#define _GNU_SOURCE
#include "orconfig.h"
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#define UTIL_PRIVATE
#include "util.h"
#include "torlog.h"
#undef log
@@ -27,6 +31,7 @@
#include <direct.h>
#include <process.h>
#include <tchar.h>
+#include <Winbase.h>
#else
#include <dirent.h>
#include <pwd.h>
@@ -42,6 +47,7 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
+#include <signal.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
@@ -67,9 +73,6 @@
#ifdef HAVE_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
@@ -87,6 +90,9 @@
#ifdef HAVE_MALLOC_NP_H
#include <malloc_np.h>
#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
/* =====
* Memory management
@@ -417,6 +423,32 @@ round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor)
return number;
}
+/** Return the number of bits set in <b>v</b>. */
+int
+n_bits_set_u8(uint8_t v)
+{
+ static const int nybble_table[] = {
+ 0, /* 0000 */
+ 1, /* 0001 */
+ 1, /* 0010 */
+ 2, /* 0011 */
+ 1, /* 0100 */
+ 2, /* 0101 */
+ 2, /* 0110 */
+ 3, /* 0111 */
+ 1, /* 1000 */
+ 2, /* 1001 */
+ 2, /* 1010 */
+ 3, /* 1011 */
+ 2, /* 1100 */
+ 3, /* 1101 */
+ 3, /* 1110 */
+ 4, /* 1111 */
+ };
+
+ return nybble_table[v & 15] + nybble_table[v>>4];
+}
+
/* =====
* String manipulation
* ===== */
@@ -500,6 +532,23 @@ tor_strisnonupper(const char *s)
return 1;
}
+/** As strcmp, except that either string may be NULL. The NULL string is
+ * considered to be before any non-NULL string. */
+int
+strcmp_opt(const char *s1, const char *s2)
+{
+ if (!s1) {
+ if (!s2)
+ return 0;
+ else
+ return -1;
+ } else if (!s2) {
+ return 1;
+ } else {
+ return strcmp(s1, s2);
+ }
+}
+
/** Compares the first strlen(s2) characters of s1 with s2. Returns as for
* strcmp.
*/
@@ -722,6 +771,34 @@ find_str_at_start_of_line(const char *haystack, const char *needle)
return NULL;
}
+/** Returns true if <b>string</b> could be a C identifier.
+ A C identifier must begin with a letter or an underscore and the
+ rest of its characters can be letters, numbers or underscores. No
+ length limit is imposed. */
+int
+string_is_C_identifier(const char *string)
+{
+ size_t iter;
+ size_t length = strlen(string);
+ if (!length)
+ return 0;
+
+ for (iter = 0; iter < length ; iter++) {
+ if (iter == 0) {
+ if (!(TOR_ISALPHA(string[iter]) ||
+ string[iter] == '_'))
+ return 0;
+ } else {
+ if (!(TOR_ISALPHA(string[iter]) ||
+ TOR_ISDIGIT(string[iter]) ||
+ string[iter] == '_'))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
/** Return true iff the 'len' bytes at 'mem' are all zero. */
int
tor_mem_is_zero(const char *mem, size_t len)
@@ -958,7 +1035,7 @@ esc_for_log(const char *s)
char *result, *outp;
size_t len = 3;
if (!s) {
- return tor_strdup("");
+ return tor_strdup("(null)");
}
for (cp = s; *cp; ++cp) {
@@ -1698,6 +1775,8 @@ check_private_dir(const char *dirname, cpd_check_t check,
struct passwd *pw = NULL;
uid_t running_uid;
gid_t running_gid;
+#else
+ (void)effective_user;
#endif
tor_assert(dirname);
@@ -1897,7 +1976,7 @@ start_writing_to_file(const char *fname, int open_flags, int mode,
if (open_flags & O_BINARY)
new_file->binary = 1;
- new_file->fd = open(open_name, open_flags, mode);
+ new_file->fd = tor_open_cloexec(open_name, open_flags, mode);
if (new_file->fd < 0) {
log_warn(LD_FS, "Couldn't open \"%s\" (%s) for writing: %s",
open_name, fname, strerror(errno));
@@ -2118,7 +2197,7 @@ read_file_to_str(const char *filename, int flags, struct stat *stat_out)
tor_assert(filename);
- fd = open(filename,O_RDONLY|(bin?O_BINARY:O_TEXT),0);
+ fd = tor_open_cloexec(filename,O_RDONLY|(bin?O_BINARY:O_TEXT),0);
if (fd<0) {
int severity = LOG_WARN;
int save_errno = errno;
@@ -2504,18 +2583,21 @@ digit_to_num(char d)
* success, store the result in <b>out</b>, advance bufp to the next
* character, and return 0. On failure, return -1. */
static int
-scan_unsigned(const char **bufp, unsigned *out, int width)
+scan_unsigned(const char **bufp, unsigned *out, int width, int base)
{
unsigned result = 0;
int scanned_so_far = 0;
+ const int hex = base==16;
+ tor_assert(base == 10 || base == 16);
if (!bufp || !*bufp || !out)
return -1;
if (width<0)
width=MAX_SCANF_WIDTH;
- while (**bufp && TOR_ISDIGIT(**bufp) && scanned_so_far < width) {
- int digit = digit_to_num(*(*bufp)++);
- unsigned new_result = result * 10 + digit;
+ while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp))
+ && scanned_so_far < width) {
+ int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++);
+ unsigned new_result = result * base + digit;
if (new_result > UINT32_MAX || new_result < result)
return -1; /* over/underflow. */
result = new_result;
@@ -2577,11 +2659,12 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
if (!width) /* No zero-width things. */
return -1;
}
- if (*pattern == 'u') {
+ if (*pattern == 'u' || *pattern == 'x') {
unsigned *u = va_arg(ap, unsigned *);
+ const int base = (*pattern == 'u') ? 10 : 16;
if (!*buf)
return n_matched;
- if (scan_unsigned(&buf, u, width)<0)
+ if (scan_unsigned(&buf, u, width, base)<0)
return n_matched;
++pattern;
++n_matched;
@@ -2618,9 +2701,9 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
/** Minimal sscanf replacement: parse <b>buf</b> according to <b>pattern</b>
* and store the results in the corresponding argument fields. Differs from
- * sscanf in that it: Only handles %u and %Ns. Does not handle arbitrarily
- * long widths. %u does not consume any space. Is locale-independent.
- * Returns -1 on malformed patterns.
+ * sscanf in that it: Only handles %u and %x and %Ns. Does not handle
+ * arbitrarily long widths. %u and %x do not consume any space. Is
+ * locale-independent. Returns -1 on malformed patterns.
*
* (As with other locale-independent functions, we need this to parse data that
* is in ASCII without worrying that the C library's locale-handling will make
@@ -2637,6 +2720,30 @@ tor_sscanf(const char *buf, const char *pattern, ...)
return r;
}
+/** Append the string produced by tor_asprintf(<b>pattern</b>, <b>...</b>)
+ * to <b>sl</b>. */
+void
+smartlist_asprintf_add(struct smartlist_t *sl, const char *pattern, ...)
+{
+ va_list ap;
+ va_start(ap, pattern);
+ smartlist_vasprintf_add(sl, pattern, ap);
+ va_end(ap);
+}
+
+/** va_list-based backend of smartlist_asprintf_add. */
+void
+smartlist_vasprintf_add(struct smartlist_t *sl, const char *pattern,
+ va_list args)
+{
+ char *str = NULL;
+
+ tor_vasprintf(&str, pattern, args);
+ tor_assert(str != NULL);
+
+ smartlist_add(sl, str);
+}
+
/** Return a new list containing the filenames in the directory <b>dirname</b>.
* Return NULL on error or if <b>dirname</b> is not a directory.
*/
@@ -2814,7 +2921,7 @@ finish_daemon(const char *desired_cwd)
exit(1);
}
- nullfd = open("/dev/null", O_RDWR);
+ nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0);
if (nullfd < 0) {
log_err(LD_GENERAL,"/dev/null can't be opened. Exiting.");
exit(1);
@@ -2885,3 +2992,1070 @@ load_windows_system_library(const TCHAR *library_name)
}
#endif
+/** Format a single argument for being put on a Windows command line.
+ * Returns a newly allocated string */
+static char *
+format_win_cmdline_argument(const char *arg)
+{
+ char *formatted_arg;
+ char need_quotes;
+ const char *c;
+ int i;
+ int bs_counter = 0;
+ /* Backslash we can point to when one is inserted into the string */
+ const char backslash = '\\';
+
+ /* Smartlist of *char */
+ smartlist_t *arg_chars;
+ arg_chars = smartlist_create();
+
+ /* Quote string if it contains whitespace or is empty */
+ need_quotes = (strchr(arg, ' ') || strchr(arg, '\t') || '\0' == arg[0]);
+
+ /* Build up smartlist of *chars */
+ for (c=arg; *c != '\0'; c++) {
+ if ('"' == *c) {
+ /* Double up backslashes preceding a quote */
+ for (i=0; i<(bs_counter*2); i++)
+ smartlist_add(arg_chars, (void*)&backslash);
+ bs_counter = 0;
+ /* Escape the quote */
+ smartlist_add(arg_chars, (void*)&backslash);
+ smartlist_add(arg_chars, (void*)c);
+ } else if ('\\' == *c) {
+ /* Count backslashes until we know whether to double up */
+ bs_counter++;
+ } else {
+ /* Don't double up slashes preceding a non-quote */
+ for (i=0; i<bs_counter; i++)
+ smartlist_add(arg_chars, (void*)&backslash);
+ bs_counter = 0;
+ smartlist_add(arg_chars, (void*)c);
+ }
+ }
+ /* Don't double up trailing backslashes */
+ for (i=0; i<bs_counter; i++)
+ smartlist_add(arg_chars, (void*)&backslash);
+
+ /* Allocate space for argument, quotes (if needed), and terminator */
+ formatted_arg = tor_malloc(sizeof(char) *
+ (smartlist_len(arg_chars) + (need_quotes?2:0) + 1));
+
+ /* Add leading quote */
+ i=0;
+ if (need_quotes)
+ formatted_arg[i++] = '"';
+
+ /* Add characters */
+ SMARTLIST_FOREACH(arg_chars, char*, c,
+ {
+ formatted_arg[i++] = *c;
+ });
+
+ /* Add trailing quote */
+ if (need_quotes)
+ formatted_arg[i++] = '"';
+ formatted_arg[i] = '\0';
+
+ smartlist_free(arg_chars);
+ return formatted_arg;
+}
+
+/** Format a command line for use on Windows, which takes the command as a
+ * string rather than string array. Follows the rules from "Parsing C++
+ * Command-Line Arguments" in MSDN. Algorithm based on list2cmdline in the
+ * Python subprocess module. Returns a newly allocated string */
+char *
+tor_join_win_cmdline(const char *argv[])
+{
+ smartlist_t *argv_list;
+ char *joined_argv;
+ int i;
+
+ /* Format each argument and put the result in a smartlist */
+ argv_list = smartlist_create();
+ for (i=0; argv[i] != NULL; i++) {
+ smartlist_add(argv_list, (void *)format_win_cmdline_argument(argv[i]));
+ }
+
+ /* Join the arguments with whitespace */
+ joined_argv = smartlist_join_strings(argv_list, " ", 0, NULL);
+
+ /* Free the newly allocated arguments, and the smartlist */
+ SMARTLIST_FOREACH(argv_list, char *, arg,
+ {
+ tor_free(arg);
+ });
+ smartlist_free(argv_list);
+
+ return joined_argv;
+}
+
+/** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in
+ * <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler
+ * safe.
+ *
+ * <b>hex_errno</b> must have at least HEX_ERRNO_SIZE bytes available.
+ *
+ * The format of <b>hex_errno</b> is: "CHILD_STATE/ERRNO\n", left-padded
+ * with spaces. Note that there is no trailing \0. CHILD_STATE indicates where
+ * in the processs of starting the child process did the failure occur (see
+ * CHILD_STATE_* macros for definition), and SAVED_ERRNO is the value of
+ * errno when the failure occurred.
+ */
+
+void
+format_helper_exit_status(unsigned char child_state, int saved_errno,
+ char *hex_errno)
+{
+ unsigned int unsigned_errno;
+ char *cur;
+ size_t i;
+
+ /* Fill hex_errno with spaces, and a trailing newline (memset may
+ not be signal handler safe, so we can't use it) */
+ for (i = 0; i < (HEX_ERRNO_SIZE - 1); i++)
+ hex_errno[i] = ' ';
+ hex_errno[HEX_ERRNO_SIZE - 1] = '\n';
+
+ /* Convert errno to be unsigned for hex conversion */
+ if (saved_errno < 0) {
+ unsigned_errno = (unsigned int) -saved_errno;
+ } else {
+ unsigned_errno = (unsigned int) saved_errno;
+ }
+
+ /* Convert errno to hex (start before \n) */
+ cur = hex_errno + HEX_ERRNO_SIZE - 2;
+
+ /* Check for overflow on first iteration of the loop */
+ if (cur < hex_errno)
+ return;
+
+ do {
+ *cur-- = "0123456789ABCDEF"[unsigned_errno % 16];
+ unsigned_errno /= 16;
+ } while (unsigned_errno != 0 && cur >= hex_errno);
+
+ /* Prepend the minus sign if errno was negative */
+ if (saved_errno < 0 && cur >= hex_errno)
+ *cur-- = '-';
+
+ /* Leave a gap */
+ if (cur >= hex_errno)
+ *cur-- = '/';
+
+ /* Check for overflow on first iteration of the loop */
+ if (cur < hex_errno)
+ return;
+
+ /* Convert child_state to hex */
+ do {
+ *cur-- = "0123456789ABCDEF"[child_state % 16];
+ child_state /= 16;
+ } while (child_state != 0 && cur >= hex_errno);
+}
+
+/* Maximum number of file descriptors, if we cannot get it via sysconf() */
+#define DEFAULT_MAX_FD 256
+
+/** Terminate process running at PID <b>pid</b>.
+ * Code borrowed from Python's os.kill. */
+int
+tor_terminate_process(pid_t pid)
+{
+#ifdef MS_WINDOWS
+ HANDLE handle;
+ /* If the signal is outside of what GenerateConsoleCtrlEvent can use,
+ attempt to open and terminate the process. */
+ handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
+ if (!handle)
+ return -1;
+
+ if (!TerminateProcess(handle, 0))
+ return -1;
+ else
+ return 0;
+#else /* Unix */
+ return kill(pid, SIGTERM);
+#endif
+}
+
+#define CHILD_STATE_INIT 0
+#define CHILD_STATE_PIPE 1
+#define CHILD_STATE_MAXFD 2
+#define CHILD_STATE_FORK 3
+#define CHILD_STATE_DUPOUT 4
+#define CHILD_STATE_DUPERR 5
+#define CHILD_STATE_REDIRECT 6
+#define CHILD_STATE_CLOSEFD 7
+#define CHILD_STATE_EXEC 8
+#define CHILD_STATE_FAILEXEC 9
+
+/** Start a program in the background. If <b>filename</b> contains a '/', then
+ * it will be treated as an absolute or relative path. Otherwise, on
+ * non-Windows systems, the system path will be searched for <b>filename</b>.
+ * On Windows, only the current directory will be searched. Here, to search the
+ * system path (as well as the application directory, current working
+ * directory, and system directories), set filename to NULL.
+ *
+ * The strings in <b>argv</b> will be passed as the command line arguments of
+ * the child program (following convention, argv[0] should normally be the
+ * filename of the executable, and this must be the case if <b>filename</b> is
+ * NULL). The last element of argv must be NULL. A handle to the child process
+ * will be returned in process_handle (which must be non-NULL). Read
+ * process_handle.status to find out if the process was successfully launched.
+ * For convenience, process_handle.status is returned by this function.
+ *
+ * Some parts of this code are based on the POSIX subprocess module from
+ * Python, and example code from
+ * http://msdn.microsoft.com/en-us/library/ms682499%28v=vs.85%29.aspx.
+ */
+int
+tor_spawn_background(const char *const filename, const char **argv,
+ const char **envp,
+ process_handle_t *process_handle)
+{
+#ifdef MS_WINDOWS
+ HANDLE stdout_pipe_read = NULL;
+ HANDLE stdout_pipe_write = NULL;
+ HANDLE stderr_pipe_read = NULL;
+ HANDLE stderr_pipe_write = NULL;
+
+ STARTUPINFO siStartInfo;
+ BOOL retval = FALSE;
+
+ SECURITY_ATTRIBUTES saAttr;
+ char *joined_argv;
+
+ /* process_handle must not be NULL */
+ tor_assert(process_handle != NULL);
+
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ /* TODO: should we set explicit security attributes? (#2046, comment 5) */
+ saAttr.lpSecurityDescriptor = NULL;
+
+ /* Assume failure to start process */
+ memset(process_handle, 0, sizeof(process_handle_t));
+ process_handle->status = PROCESS_STATUS_ERROR;
+
+ /* Set up pipe for stdout */
+ if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, &saAttr, 0)) {
+ log_warn(LD_GENERAL,
+ "Failed to create pipe for stdout communication with child process: %s",
+ format_win32_error(GetLastError()));
+ return process_handle->status;
+ }
+ if (!SetHandleInformation(stdout_pipe_read, HANDLE_FLAG_INHERIT, 0)) {
+ log_warn(LD_GENERAL,
+ "Failed to configure pipe for stdout communication with child "
+ "process: %s", format_win32_error(GetLastError()));
+ return process_handle->status;
+ }
+
+ /* Set up pipe for stderr */
+ if (!CreatePipe(&stderr_pipe_read, &stderr_pipe_write, &saAttr, 0)) {
+ log_warn(LD_GENERAL,
+ "Failed to create pipe for stderr communication with child process: %s",
+ format_win32_error(GetLastError()));
+ return process_handle->status;
+ }
+ if (!SetHandleInformation(stderr_pipe_read, HANDLE_FLAG_INHERIT, 0)) {
+ log_warn(LD_GENERAL,
+ "Failed to configure pipe for stderr communication with child "
+ "process: %s", format_win32_error(GetLastError()));
+ return process_handle->status;
+ }
+
+ /* Create the child process */
+
+ /* Windows expects argv to be a whitespace delimited string, so join argv up
+ */
+ joined_argv = tor_join_win_cmdline(argv);
+
+ ZeroMemory(&(process_handle->pid), sizeof(PROCESS_INFORMATION));
+ ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.hStdError = stderr_pipe_write;
+ siStartInfo.hStdOutput = stdout_pipe_write;
+ siStartInfo.hStdInput = NULL;
+ siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ /* Create the child process */
+
+ retval = CreateProcess(filename, // module name
+ joined_argv, // command line
+ /* TODO: should we set explicit security attributes? (#2046, comment 5) */
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ /*(TODO: set CREATE_NEW CONSOLE/PROCESS_GROUP to make GetExitCodeProcess()
+ * work?) */
+ 0, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &siStartInfo, // STARTUPINFO pointer
+ &(process_handle->pid)); // receives PROCESS_INFORMATION
+
+ tor_free(joined_argv);
+
+ if (!retval) {
+ log_warn(LD_GENERAL,
+ "Failed to create child process %s: %s", filename?filename:argv[0],
+ format_win32_error(GetLastError()));
+ } else {
+ /* TODO: Close hProcess and hThread in process_handle->pid? */
+ process_handle->stdout_pipe = stdout_pipe_read;
+ process_handle->stderr_pipe = stderr_pipe_read;
+ process_handle->status = PROCESS_STATUS_RUNNING;
+ }
+
+ /* TODO: Close pipes on exit */
+
+ return process_handle->status;
+#else // MS_WINDOWS
+ pid_t pid;
+ int stdout_pipe[2];
+ int stderr_pipe[2];
+ int fd, retval;
+ ssize_t nbytes;
+
+ const char *error_message = SPAWN_ERROR_MESSAGE;
+ size_t error_message_length;
+
+ /* Represents where in the process of spawning the program is;
+ this is used for printing out the error message */
+ unsigned char child_state = CHILD_STATE_INIT;
+
+ char hex_errno[HEX_ERRNO_SIZE];
+
+ static int max_fd = -1;
+
+ /* Assume failure to start */
+ memset(process_handle, 0, sizeof(process_handle_t));
+ process_handle->status = PROCESS_STATUS_ERROR;
+
+ /* We do the strlen here because strlen() is not signal handler safe,
+ and we are not allowed to use unsafe functions between fork and exec */
+ error_message_length = strlen(error_message);
+
+ child_state = CHILD_STATE_PIPE;
+
+ /* Set up pipe for redirecting stdout and stderr of child */
+ retval = pipe(stdout_pipe);
+ if (-1 == retval) {
+ log_warn(LD_GENERAL,
+ "Failed to set up pipe for stdout communication with child process: %s",
+ strerror(errno));
+ return process_handle->status;
+ }
+
+ retval = pipe(stderr_pipe);
+ if (-1 == retval) {
+ log_warn(LD_GENERAL,
+ "Failed to set up pipe for stderr communication with child process: %s",
+ strerror(errno));
+ return process_handle->status;
+ }
+
+ child_state = CHILD_STATE_MAXFD;
+
+#ifdef _SC_OPEN_MAX
+ if (-1 != max_fd) {
+ max_fd = (int) sysconf(_SC_OPEN_MAX);
+ if (max_fd == -1)
+ max_fd = DEFAULT_MAX_FD;
+ log_warn(LD_GENERAL,
+ "Cannot find maximum file descriptor, assuming %d", max_fd);
+ }
+#else
+ max_fd = DEFAULT_MAX_FD;
+#endif
+
+ child_state = CHILD_STATE_FORK;
+
+ pid = fork();
+ if (0 == pid) {
+ /* In child */
+
+ child_state = CHILD_STATE_DUPOUT;
+
+ /* Link child stdout to the write end of the pipe */
+ retval = dup2(stdout_pipe[1], STDOUT_FILENO);
+ if (-1 == retval)
+ goto error;
+
+ child_state = CHILD_STATE_DUPERR;
+
+ /* Link child stderr to the write end of the pipe */
+ retval = dup2(stderr_pipe[1], STDERR_FILENO);
+ if (-1 == retval)
+ goto error;
+
+ child_state = CHILD_STATE_REDIRECT;
+
+ /* Link stdin to /dev/null */
+ fd = open("/dev/null", O_RDONLY); /* NOT cloexec, obviously. */
+ if (fd != -1)
+ dup2(fd, STDIN_FILENO);
+ else
+ goto error;
+
+ child_state = CHILD_STATE_CLOSEFD;
+
+ close(stderr_pipe[0]);
+ close(stderr_pipe[1]);
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+ close(fd);
+
+ /* Close all other fds, including the read end of the pipe */
+ /* XXX: We should now be doing enough FD_CLOEXEC setting to make
+ * this needless. */
+ for (fd = STDERR_FILENO + 1; fd < max_fd; fd++) {
+ close(fd);
+ }
+
+ child_state = CHILD_STATE_EXEC;
+
+ /* Call the requested program. We need the cast because
+ execvp doesn't define argv as const, even though it
+ does not modify the arguments */
+ if (envp)
+ execve(filename, (char *const *) argv, (char*const*)envp);
+ else
+ execvp(filename, (char *const *) argv);
+
+ /* If we got here, the exec or open(/dev/null) failed */
+
+ child_state = CHILD_STATE_FAILEXEC;
+
+ error:
+ /* XXX: are we leaking fds from the pipe? */
+
+ format_helper_exit_status(child_state, errno, hex_errno);
+
+ /* Write the error message. GCC requires that we check the return
+ value, but there is nothing we can do if it fails */
+ /* TODO: Don't use STDOUT, use a pipe set up just for this purpose */
+ nbytes = write(STDOUT_FILENO, error_message, error_message_length);
+ nbytes = write(STDOUT_FILENO, hex_errno, sizeof(hex_errno));
+
+ (void) nbytes;
+
+ _exit(255);
+ /* Never reached, but avoids compiler warning */
+ return process_handle->status;
+ }
+
+ /* In parent */
+
+ if (-1 == pid) {
+ log_warn(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+ close(stderr_pipe[0]);
+ close(stderr_pipe[1]);
+ return process_handle->status;
+ }
+
+ process_handle->pid = pid;
+
+ /* TODO: If the child process forked but failed to exec, waitpid it */
+
+ /* Return read end of the pipes to caller, and close write end */
+ process_handle->stdout_pipe = stdout_pipe[0];
+ retval = close(stdout_pipe[1]);
+
+ if (-1 == retval) {
+ log_warn(LD_GENERAL,
+ "Failed to close write end of stdout pipe in parent process: %s",
+ strerror(errno));
+ }
+
+ process_handle->stderr_pipe = stderr_pipe[0];
+ retval = close(stderr_pipe[1]);
+
+ if (-1 == retval) {
+ log_warn(LD_GENERAL,
+ "Failed to close write end of stderr pipe in parent process: %s",
+ strerror(errno));
+ }
+
+ process_handle->status = PROCESS_STATUS_RUNNING;
+ /* Set stdout/stderr pipes to be non-blocking */
+ fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK);
+ fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK);
+ /* Open the buffered IO streams */
+ process_handle->stdout_handle = fdopen(process_handle->stdout_pipe, "r");
+ process_handle->stderr_handle = fdopen(process_handle->stderr_pipe, "r");
+
+ return process_handle->status;
+#endif // MS_WINDOWS
+}
+
+/** Get the exit code of a process specified by <b>process_handle</b> and store
+ * it in <b>exit_code</b>, if set to a non-NULL value. If <b>block</b> is set
+ * to true, the call will block until the process has exited. Otherwise if
+ * the process is still running, the function will return
+ * PROCESS_EXIT_RUNNING, and exit_code will be left unchanged. Returns
+ * PROCESS_EXIT_EXITED if the process did exit. If there is a failure,
+ * PROCESS_EXIT_ERROR will be returned and the contents of exit_code (if
+ * non-NULL) will be undefined. N.B. Under *nix operating systems, this will
+ * probably not work in Tor, because waitpid() is called in main.c to reap any
+ * terminated child processes.*/
+int
+tor_get_exit_code(const process_handle_t process_handle,
+ int block, int *exit_code)
+{
+#ifdef MS_WINDOWS
+ DWORD retval;
+ BOOL success;
+
+ if (block) {
+ /* Wait for the process to exit */
+ retval = WaitForSingleObject(process_handle.pid.hProcess, INFINITE);
+ if (retval != WAIT_OBJECT_0) {
+ log_warn(LD_GENERAL, "WaitForSingleObject() failed (%d): %s",
+ (int)retval, format_win32_error(GetLastError()));
+ return PROCESS_EXIT_ERROR;
+ }
+ } else {
+ retval = WaitForSingleObject(process_handle.pid.hProcess, 0);
+ if (WAIT_TIMEOUT == retval) {
+ /* Process has not exited */
+ return PROCESS_EXIT_RUNNING;
+ } else if (retval != WAIT_OBJECT_0) {
+ log_warn(LD_GENERAL, "WaitForSingleObject() failed (%d): %s",
+ (int)retval, format_win32_error(GetLastError()));
+ return PROCESS_EXIT_ERROR;
+ }
+ }
+
+ if (exit_code != NULL) {
+ success = GetExitCodeProcess(process_handle.pid.hProcess,
+ (PDWORD)exit_code);
+ if (!success) {
+ log_warn(LD_GENERAL, "GetExitCodeProcess() failed: %s",
+ format_win32_error(GetLastError()));
+ return PROCESS_EXIT_ERROR;
+ }
+ }
+#else
+ int stat_loc;
+ int retval;
+
+ retval = waitpid(process_handle.pid, &stat_loc, block?0:WNOHANG);
+ if (!block && 0 == retval) {
+ /* Process has not exited */
+ return PROCESS_EXIT_RUNNING;
+ } else if (retval != process_handle.pid) {
+ log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s", process_handle.pid,
+ strerror(errno));
+ return PROCESS_EXIT_ERROR;
+ }
+
+ if (!WIFEXITED(stat_loc)) {
+ log_warn(LD_GENERAL, "Process %d did not exit normally",
+ process_handle.pid);
+ return PROCESS_EXIT_ERROR;
+ }
+
+ if (exit_code != NULL)
+ *exit_code = WEXITSTATUS(stat_loc);
+#endif // MS_WINDOWS
+
+ return PROCESS_EXIT_EXITED;
+}
+
+#ifdef MS_WINDOWS
+/** Read from a handle <b>h</b> into <b>buf</b>, up to <b>count</b> bytes. If
+ * <b>hProcess</b> is NULL, the function will return immediately if there is
+ * nothing more to read. Otherwise <b>hProcess</b> should be set to the handle
+ * to the process owning the <b>h</b>. In this case, the function will exit
+ * only once the process has exited, or <b>count</b> bytes are read. Returns
+ * the number of bytes read, or -1 on error. */
+ssize_t
+tor_read_all_handle(HANDLE h, char *buf, size_t count,
+ const process_handle_t *process)
+{
+ size_t numread = 0;
+ BOOL retval;
+ DWORD byte_count;
+ BOOL process_exited = FALSE;
+
+ if (count > SIZE_T_CEILING || count > SSIZE_T_MAX)
+ return -1;
+
+ while (numread != count) {
+ /* Check if there is anything to read */
+ retval = PeekNamedPipe(h, NULL, 0, NULL, &byte_count, NULL);
+ if (!retval) {
+ log_warn(LD_GENERAL,
+ "Failed to peek from handle: %s",
+ format_win32_error(GetLastError()));
+ return -1;
+ } else if (0 == byte_count) {
+ /* Nothing available: process exited or it is busy */
+
+ /* Exit if we don't know whether the process is running */
+ if (NULL == process)
+ break;
+
+ /* The process exited and there's nothing left to read from it */
+ if (process_exited)
+ break;
+
+ /* If process is not running, check for output one more time in case
+ it wrote something after the peek was performed. Otherwise keep on
+ waiting for output */
+ tor_assert(process != NULL);
+ byte_count = WaitForSingleObject(process->pid.hProcess, 0);
+ if (WAIT_TIMEOUT != byte_count)
+ process_exited = TRUE;
+
+ continue;
+ }
+
+ /* There is data to read; read it */
+ retval = ReadFile(h, buf+numread, count-numread, &byte_count, NULL);
+ tor_assert(byte_count + numread <= count);
+ if (!retval) {
+ log_warn(LD_GENERAL, "Failed to read from handle: %s",
+ format_win32_error(GetLastError()));
+ return -1;
+ } else if (0 == byte_count) {
+ /* End of file */
+ break;
+ }
+ numread += byte_count;
+ }
+ return (ssize_t)numread;
+}
+#else
+/** Read from a handle <b>h</b> into <b>buf</b>, up to <b>count</b> bytes. If
+ * <b>process</b> is NULL, the function will return immediately if there is
+ * nothing more to read. Otherwise data will be read until end of file, or
+ * <b>count</b> bytes are read. Returns the number of bytes read, or -1 on
+ * error. Sets <b>eof</b> to true if <b>eof</b> is not NULL and the end of the
+ * file has been reached. */
+ssize_t
+tor_read_all_handle(FILE *h, char *buf, size_t count,
+ const process_handle_t *process,
+ int *eof)
+{
+ size_t numread = 0;
+ char *retval;
+
+ if (eof)
+ *eof = 0;
+
+ if (count > SIZE_T_CEILING || count > SSIZE_T_MAX)
+ return -1;
+
+ while (numread != count) {
+ /* Use fgets because that is what we use in log_from_pipe() */
+ retval = fgets(buf+numread, (int)(count-numread), h);
+ if (NULL == retval) {
+ if (feof(h)) {
+ log_debug(LD_GENERAL, "fgets() reached end of file");
+ fclose(h);
+ if (eof)
+ *eof = 1;
+ break;
+ } else {
+ if (EAGAIN == errno) {
+ if (process)
+ continue;
+ else
+ break;
+ } else {
+ log_warn(LD_GENERAL, "fgets() from handle failed: %s",
+ strerror(errno));
+ fclose(h);
+ return -1;
+ }
+ }
+ }
+ tor_assert(retval != NULL);
+ tor_assert(strlen(retval) + numread <= count);
+ numread += strlen(retval);
+ }
+
+ log_debug(LD_GENERAL, "fgets() read %d bytes from handle", (int)numread);
+ return (ssize_t)numread;
+}
+#endif
+
+/** Read from stdout of a process until the process exits. */
+ssize_t
+tor_read_all_from_process_stdout(const process_handle_t *process_handle,
+ char *buf, size_t count)
+{
+#ifdef MS_WINDOWS
+ return tor_read_all_handle(process_handle->stdout_pipe, buf, count,
+ process_handle);
+#else
+ return tor_read_all_handle(process_handle->stdout_handle, buf, count,
+ process_handle, NULL);
+#endif
+}
+
+/** Read from stdout of a process until the process exits. */
+ssize_t
+tor_read_all_from_process_stderr(const process_handle_t *process_handle,
+ char *buf, size_t count)
+{
+#ifdef MS_WINDOWS
+ return tor_read_all_handle(process_handle->stderr_pipe, buf, count,
+ process_handle);
+#else
+ return tor_read_all_handle(process_handle->stderr_handle, buf, count,
+ process_handle, NULL);
+#endif
+}
+
+/** Split buf into lines, and add to smartlist. The buffer <b>buf</b> will be
+ * modified. The resulting smartlist will consist of pointers to buf, so there
+ * is no need to free the contents of sl. <b>buf</b> must be a NUL-terminated
+ * string. <b>len</b> should be set to the length of the buffer excluding the
+ * NUL. Non-printable characters (including NUL) will be replaced with "." */
+int
+tor_split_lines(smartlist_t *sl, char *buf, int len)
+{
+ /* Index in buf of the start of the current line */
+ int start = 0;
+ /* Index in buf of the current character being processed */
+ int cur = 0;
+ /* Are we currently in a line */
+ char in_line = 0;
+
+ /* Loop over string */
+ while (cur < len) {
+ /* Loop until end of line or end of string */
+ for (; cur < len; cur++) {
+ if (in_line) {
+ if ('\r' == buf[cur] || '\n' == buf[cur]) {
+ /* End of line */
+ buf[cur] = '\0';
+ /* Point cur to the next line */
+ cur++;
+ /* Line starts at start and ends with a nul */
+ break;
+ } else {
+ if (!TOR_ISPRINT(buf[cur]))
+ buf[cur] = '.';
+ }
+ } else {
+ if ('\r' == buf[cur] || '\n' == buf[cur]) {
+ /* Skip leading vertical space */
+ ;
+ } else {
+ in_line = 1;
+ start = cur;
+ if (!TOR_ISPRINT(buf[cur]))
+ buf[cur] = '.';
+ }
+ }
+ }
+ /* We are at the end of the line or end of string. If in_line is true there
+ * is a line which starts at buf+start and ends at a NUL. cur points to
+ * the character after the NUL. */
+ if (in_line)
+ smartlist_add(sl, (void *)(buf+start));
+ in_line = 0;
+ }
+ return smartlist_len(sl);
+}
+
+#ifdef MS_WINDOWS
+/** Read from stream, and send lines to log at the specified log level.
+ * Returns -1 if there is a error reading, and 0 otherwise.
+ * If the generated stream is flushed more often than on new lines, or
+ * a read exceeds 256 bytes, lines will be truncated. This should be fixed,
+ * along with the corresponding problem on *nix (see bug #2045).
+ */
+static int
+log_from_handle(HANDLE *pipe, int severity)
+{
+ char buf[256];
+ int pos;
+ smartlist_t *lines;
+
+ pos = tor_read_all_handle(pipe, buf, sizeof(buf) - 1, NULL);
+ if (pos < 0) {
+ /* Error */
+ log_warn(LD_GENERAL, "Failed to read data from subprocess");
+ return -1;
+ }
+
+ if (0 == pos) {
+ /* There's nothing to read (process is busy or has exited) */
+ log_debug(LD_GENERAL, "Subprocess had nothing to say");
+ return 0;
+ }
+
+ /* End with a null even if there isn't a \r\n at the end */
+ /* TODO: What if this is a partial line? */
+ buf[pos] = '\0';
+ log_debug(LD_GENERAL, "Subprocess had %d bytes to say", pos);
+
+ /* Split up the buffer */
+ lines = smartlist_create();
+ tor_split_lines(lines, buf, pos);
+
+ /* Log each line */
+ SMARTLIST_FOREACH(lines, char *, line,
+ {
+ log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", line);
+ });
+ smartlist_free(lines);
+
+ return 0;
+}
+
+#else
+
+/** Read from stream, and send lines to log at the specified log level.
+ * Returns 1 if stream is closed normally, -1 if there is a error reading, and
+ * 0 otherwise. Handles lines from tor-fw-helper and
+ * tor_spawn_background() specially.
+ */
+static int
+log_from_pipe(FILE *stream, int severity, const char *executable,
+ int *child_status)
+{
+ char buf[256];
+ enum stream_status r;
+
+ for (;;) {
+ r = get_string_from_pipe(stream, buf, sizeof(buf) - 1);
+
+ if (r == IO_STREAM_CLOSED) {
+ fclose(stream);
+ return 1;
+ } else if (r == IO_STREAM_EAGAIN) {
+ return 0;
+ } else if (r == IO_STREAM_TERM) {
+ fclose(stream);
+ return -1;
+ }
+
+ tor_assert(r == IO_STREAM_OKAY);
+
+ /* Check if buf starts with SPAWN_ERROR_MESSAGE */
+ if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) {
+ /* Parse error message */
+ int retval, child_state, saved_errno;
+ retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x",
+ &child_state, &saved_errno);
+ if (retval == 2) {
+ log_warn(LD_GENERAL,
+ "Failed to start child process \"%s\" in state %d: %s",
+ executable, child_state, strerror(saved_errno));
+ if (child_status)
+ *child_status = 1;
+ } else {
+ /* Failed to parse message from child process, log it as a
+ warning */
+ log_warn(LD_GENERAL,
+ "Unexpected message from port forwarding helper \"%s\": %s",
+ executable, buf);
+ }
+ } else {
+ log_fn(severity, LD_GENERAL, "Port forwarding helper says: %s", buf);
+ }
+ }
+
+ /* We should never get here */
+ return -1;
+}
+#endif
+
+/** Reads from <b>stream</b> and stores input in <b>buf_out</b> making
+ * sure it's below <b>count</b> bytes.
+ * If the string has a trailing newline, we strip it off.
+ *
+ * This function is specifically created to handle input from managed
+ * proxies, according to the pluggable transports spec. Make sure it
+ * fits your needs before using it.
+ *
+ * Returns:
+ * IO_STREAM_CLOSED: If the stream is closed.
+ * IO_STREAM_EAGAIN: If there is nothing to read and we should check back
+ * later.
+ * IO_STREAM_TERM: If something is wrong with the stream.
+ * IO_STREAM_OKAY: If everything went okay and we got a string
+ * in <b>buf_out</b>. */
+enum stream_status
+get_string_from_pipe(FILE *stream, char *buf_out, size_t count)
+{
+ char *retval;
+ size_t len;
+
+ tor_assert(count <= INT_MAX);
+
+ retval = fgets(buf_out, (int)count, stream);
+
+ if (!retval) {
+ if (feof(stream)) {
+ /* Program has closed stream (probably it exited) */
+ /* TODO: check error */
+ return IO_STREAM_CLOSED;
+ } else {
+ if (EAGAIN == errno) {
+ /* Nothing more to read, try again next time */
+ return IO_STREAM_EAGAIN;
+ } else {
+ /* There was a problem, abandon this child process */
+ return IO_STREAM_TERM;
+ }
+ }
+ } else {
+ len = strlen(buf_out);
+ tor_assert(len>0);
+
+ if (buf_out[len - 1] == '\n') {
+ /* Remove the trailing newline */
+ buf_out[len - 1] = '\0';
+ } else {
+ /* No newline; check whether we overflowed the buffer */
+ if (!feof(stream))
+ log_info(LD_GENERAL,
+ "Line from stream was truncated: %s", buf_out);
+ /* TODO: What to do with this error? */
+ }
+
+ return IO_STREAM_OKAY;
+ }
+
+ /* We should never get here */
+ return IO_STREAM_TERM;
+}
+
+void
+tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
+ time_t now)
+{
+/* When fw-helper succeeds, how long do we wait until running it again */
+#define TIME_TO_EXEC_FWHELPER_SUCCESS 300
+/* When fw-helper failed to start, how long do we wait until running it again
+ */
+#define TIME_TO_EXEC_FWHELPER_FAIL 60
+
+ /* Static variables are initialized to zero, so child_handle.status=0
+ * which corresponds to it not running on startup */
+ static process_handle_t child_handle;
+
+ static time_t time_to_run_helper = 0;
+ int stdout_status, stderr_status, retval;
+ const char *argv[10];
+ char s_dirport[6], s_orport[6];
+
+ tor_assert(filename);
+
+ /* Set up command line for tor-fw-helper */
+ snprintf(s_dirport, sizeof s_dirport, "%d", dir_port);
+ snprintf(s_orport, sizeof s_orport, "%d", or_port);
+
+ /* TODO: Allow different internal and external ports */
+ argv[0] = filename;
+ argv[1] = "--internal-or-port";
+ argv[2] = s_orport;
+ argv[3] = "--external-or-port";
+ argv[4] = s_orport;
+ argv[5] = "--internal-dir-port";
+ argv[6] = s_dirport;
+ argv[7] = "--external-dir-port";
+ argv[8] = s_dirport;
+ argv[9] = NULL;
+
+ /* Start the child, if it is not already running */
+ if (child_handle.status != PROCESS_STATUS_RUNNING &&
+ time_to_run_helper < now) {
+ /* Assume tor-fw-helper will succeed, start it later*/
+ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_SUCCESS;
+
+#ifdef MS_WINDOWS
+ /* Passing NULL as lpApplicationName makes Windows search for the .exe */
+ tor_spawn_background(NULL, argv, NULL, &child_handle);
+#else
+ tor_spawn_background(filename, argv, NULL, &child_handle);
+#endif
+ if (PROCESS_STATUS_ERROR == child_handle.status) {
+ log_warn(LD_GENERAL, "Failed to start port forwarding helper %s",
+ filename);
+ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL;
+ return;
+ }
+#ifdef MS_WINDOWS
+ log_info(LD_GENERAL,
+ "Started port forwarding helper (%s)", filename);
+#else
+ log_info(LD_GENERAL,
+ "Started port forwarding helper (%s) with pid %d", filename,
+ child_handle.pid);
+#endif
+ }
+
+ /* If child is running, read from its stdout and stderr) */
+ if (PROCESS_STATUS_RUNNING == child_handle.status) {
+ /* Read from stdout/stderr and log result */
+ retval = 0;
+#ifdef MS_WINDOWS
+ stdout_status = log_from_handle(child_handle.stdout_pipe, LOG_INFO);
+ stderr_status = log_from_handle(child_handle.stderr_pipe, LOG_WARN);
+ /* If we got this far (on Windows), the process started */
+ retval = 0;
+#else
+ stdout_status = log_from_pipe(child_handle.stdout_handle,
+ LOG_INFO, filename, &retval);
+ stderr_status = log_from_pipe(child_handle.stderr_handle,
+ LOG_WARN, filename, &retval);
+#endif
+ if (retval) {
+ /* There was a problem in the child process */
+ time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL;
+ }
+
+ /* Combine the two statuses in order of severity */
+ if (-1 == stdout_status || -1 == stderr_status)
+ /* There was a failure */
+ retval = -1;
+#ifdef MS_WINDOWS
+ else if (tor_get_exit_code(child_handle, 0, NULL) !=
+ PROCESS_EXIT_RUNNING) {
+ /* process has exited or there was an error */
+ /* TODO: Do something with the process return value */
+ /* TODO: What if the process output something since
+ * between log_from_handle and tor_get_exit_code? */
+ retval = 1;
+ }
+#else
+ else if (1 == stdout_status || 1 == stderr_status)
+ /* stdout or stderr was closed, the process probably
+ * exited. It will be reaped by waitpid() in main.c */
+ /* TODO: Do something with the process return value */
+ retval = 1;
+#endif
+ else
+ /* Both are fine */
+ retval = 0;
+
+ /* If either pipe indicates a failure, act on it */
+ if (0 != retval) {
+ if (1 == retval) {
+ log_info(LD_GENERAL, "Port forwarding helper terminated");
+ child_handle.status = PROCESS_STATUS_NOTRUNNING;
+ } else {
+ log_warn(LD_GENERAL, "Failed to read from port forwarding helper");
+ child_handle.status = PROCESS_STATUS_ERROR;
+ }
+
+ /* TODO: The child might not actually be finished (maybe it failed or
+ closed stdout/stderr), so maybe we shouldn't start another? */
+ }
+ }
+}
+
diff --git a/src/common/util.h b/src/common/util.h
index b9db25ca7..77ed1ca5e 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -160,6 +160,7 @@ uint64_t round_to_power_of_2(uint64_t u64);
unsigned round_to_next_multiple_of(unsigned number, unsigned divisor);
uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor);
uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor);
+int n_bits_set_u8(uint8_t v);
/* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b>
* and positive <b>b</b>. Works on integer types only. Not defined if a+b can
@@ -174,6 +175,7 @@ void tor_strlower(char *s) ATTR_NONNULL((1));
void tor_strupper(char *s) ATTR_NONNULL((1));
int tor_strisprint(const char *s) ATTR_PURE ATTR_NONNULL((1));
int tor_strisnonupper(const char *s) ATTR_PURE ATTR_NONNULL((1));
+int strcmp_opt(const char *s1, const char *s2) ATTR_PURE;
int strcmpstart(const char *s1, const char *s2) ATTR_PURE ATTR_NONNULL((1,2));
int strcmp_len(const char *s1, const char *s2, size_t len)
ATTR_PURE ATTR_NONNULL((1,2));
@@ -203,6 +205,8 @@ const char *find_whitespace(const char *s) ATTR_PURE;
const char *find_whitespace_eos(const char *s, const char *eos) ATTR_PURE;
const char *find_str_at_start_of_line(const char *haystack, const char *needle)
ATTR_PURE;
+int string_is_C_identifier(const char *string);
+
int tor_mem_is_zero(const char *mem, size_t len) ATTR_PURE;
int tor_digest_is_zero(const char *digest) ATTR_PURE;
int tor_digest256_is_zero(const char *digest) ATTR_PURE;
@@ -218,6 +222,11 @@ int tor_sscanf(const char *buf, const char *pattern, ...)
#endif
;
+void smartlist_asprintf_add(struct smartlist_t *sl, const char *pattern, ...)
+ CHECK_PRINTF(2, 3);
+void smartlist_vasprintf_add(struct smartlist_t *sl, const char *pattern,
+ va_list args);
+
int hex_decode_digit(char c);
void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen);
@@ -279,6 +288,16 @@ char *rate_limit_log(ratelim_t *lim, time_t now);
ssize_t write_all(tor_socket_t fd, const char *buf, size_t count,int isSocket);
ssize_t read_all(tor_socket_t fd, char *buf, size_t count, int isSocket);
+/** Status of an I/O stream. */
+enum stream_status {
+ IO_STREAM_OKAY,
+ IO_STREAM_EAGAIN,
+ IO_STREAM_TERM,
+ IO_STREAM_CLOSED
+};
+
+enum stream_status get_string_from_pipe(FILE *stream, char *buf, size_t count);
+
/** Return values from file_status(); see that function's documentation
* for details. */
typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR } file_status_t;
@@ -337,10 +356,75 @@ void start_daemon(void);
void finish_daemon(const char *desired_cwd);
void write_pidfile(char *filename);
+/* Port forwarding */
+void tor_check_port_forwarding(const char *filename,
+ int dir_port, int or_port, time_t now);
+
+int tor_terminate_process(pid_t pid);
+typedef struct process_handle_s process_handle_t;
+int tor_spawn_background(const char *const filename, const char **argv,
+ const char **envp, process_handle_t *process_handle);
+
+#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
+
#ifdef MS_WINDOWS
HANDLE load_windows_system_library(const TCHAR *library_name);
#endif
+#ifdef UTIL_PRIVATE
+/* Prototypes for private functions only used by util.c (and unit tests) */
+
+/* Values of process_handle_t.status. PROCESS_STATUS_NOTRUNNING must be
+ * 0 because tor_check_port_forwarding depends on this being the initial
+ * statue of the static instance of process_handle_t */
+#define PROCESS_STATUS_NOTRUNNING 0
+#define PROCESS_STATUS_RUNNING 1
+#define PROCESS_STATUS_ERROR -1
+struct process_handle_s {
+ int status;
+#ifdef MS_WINDOWS
+ HANDLE stdout_pipe;
+ HANDLE stderr_pipe;
+ PROCESS_INFORMATION pid;
+#else
+ int stdout_pipe;
+ int stderr_pipe;
+ FILE *stdout_handle;
+ FILE *stderr_handle;
+ pid_t pid;
+#endif // MS_WINDOWS
+};
+
+/* Return values of tor_get_exit_code() */
+#define PROCESS_EXIT_RUNNING 1
+#define PROCESS_EXIT_EXITED 0
+#define PROCESS_EXIT_ERROR -1
+int tor_get_exit_code(const process_handle_t process_handle,
+ int block, int *exit_code);
+int tor_split_lines(struct smartlist_t *sl, char *buf, int len);
+#ifdef MS_WINDOWS
+ssize_t tor_read_all_handle(HANDLE h, char *buf, size_t count,
+ const process_handle_t *process);
+#else
+ssize_t tor_read_all_handle(FILE *h, char *buf, size_t count,
+ const process_handle_t *process,
+ int *eof);
+#endif
+ssize_t tor_read_all_from_process_stdout(
+ const process_handle_t *process_handle, char *buf, size_t count);
+ssize_t tor_read_all_from_process_stderr(
+ const process_handle_t *process_handle, char *buf, size_t count);
+char *tor_join_win_cmdline(const char *argv[]);
+
+void format_helper_exit_status(unsigned char child_state,
+ int saved_errno, char *hex_errno);
+
+/* Space for hex values of child state, a slash, saved_errno (with
+ leading minus) and newline (no null) */
+#define HEX_ERRNO_SIZE (sizeof(char) * 2 + 1 + \
+ 1 + sizeof(int) * 2 + 1)
+#endif
+
const char *libor_get_digests(void);
#endif
diff --git a/src/or/Makefile.am b/src/or/Makefile.am
index a9ac3cdee..67adf504d 100644
--- a/src/or/Makefile.am
+++ b/src/or/Makefile.am
@@ -7,7 +7,7 @@ else
tor_platform_source=
endif
-EXTRA_DIST=ntmain.c or_sha1.i
+EXTRA_DIST=ntmain.c or_sha1.i Makefile.nmake
if USE_EXTERNAL_EVDNS
evdns_source=
@@ -15,16 +15,46 @@ else
evdns_source=eventdns.c
endif
-libtor_a_SOURCES = buffers.c circuitbuild.c circuitlist.c \
- circuituse.c command.c config.c \
- connection.c connection_edge.c connection_or.c control.c \
- cpuworker.c directory.c dirserv.c dirvote.c \
- dns.c dnsserv.c geoip.c hibernate.c main.c $(tor_platform_source) \
- microdesc.c \
- networkstatus.c onion.c policies.c \
- reasons.c relay.c rendcommon.c rendclient.c rendmid.c \
- rendservice.c rephist.c router.c routerlist.c routerparse.c \
- $(evdns_source) config_codedigest.c
+libtor_a_SOURCES = \
+ buffers.c \
+ circuitbuild.c \
+ circuitlist.c \
+ circuituse.c \
+ command.c \
+ config.c \
+ connection.c \
+ connection_edge.c \
+ connection_or.c \
+ control.c \
+ cpuworker.c \
+ directory.c \
+ dirserv.c \
+ dirvote.c \
+ dns.c \
+ dnsserv.c \
+ geoip.c \
+ hibernate.c \
+ main.c \
+ microdesc.c \
+ networkstatus.c \
+ nodelist.c \
+ onion.c \
+ transports.c \
+ policies.c \
+ reasons.c \
+ relay.c \
+ rendclient.c \
+ rendcommon.c \
+ rendmid.c \
+ rendservice.c \
+ rephist.c \
+ router.c \
+ routerlist.c \
+ routerparse.c \
+ status.c \
+ $(evdns_source) \
+ $(tor_platform_source) \
+ config_codedigest.c
#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \
# ../common/libor-event.a
@@ -40,18 +70,55 @@ AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
# This seems to matter nowhere but on windows, but I assure you that it
# matters a lot there, and is quite hard to debug if you forget to do it.
+
tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
tor_LDADD = ./libtor.a ../common/libor.a ../common/libor-crypto.a \
../common/libor-event.a \
- @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
-
-noinst_HEADERS = buffers.h circuitbuild.h circuitlist.h circuituse.h \
- command.h config.h connection_edge.h connection.h connection_or.h \
- control.h cpuworker.h directory.h dirserv.h dirvote.h dns.h \
- dnsserv.h geoip.h hibernate.h main.h microdesc.h networkstatus.h \
- ntmain.h onion.h policies.h reasons.h relay.h rendclient.h \
- rendcommon.h rendmid.h rendservice.h rephist.h router.h routerlist.h \
- routerparse.h or.h eventdns.h eventdns_tor.h micro-revision.i
+ @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+
+noinst_HEADERS = \
+ buffers.h \
+ circuitbuild.h \
+ circuitlist.h \
+ circuituse.h \
+ command.h \
+ config.h \
+ connection.h \
+ connection_edge.h \
+ connection_or.h \
+ control.h \
+ cpuworker.h \
+ directory.h \
+ dirserv.h \
+ dirvote.h \
+ dns.h \
+ dnsserv.h \
+ eventdns.h \
+ eventdns_tor.h \
+ geoip.h \
+ hibernate.h \
+ main.h \
+ microdesc.h \
+ networkstatus.h \
+ nodelist.h \
+ ntmain.h \
+ onion.h \
+ or.h \
+ transports.h \
+ policies.h \
+ reasons.h \
+ relay.h \
+ rendclient.h \
+ rendcommon.h \
+ rendmid.h \
+ rendservice.h \
+ rephist.h \
+ router.h \
+ routerlist.h \
+ routerparse.h \
+ status.h \
+ micro-revision.i
config_codedigest.o: or_sha1.i
@@ -72,11 +139,13 @@ micro-revision.i: FORCE
mv micro-revision.tmp micro-revision.i; \
fi; true
-or_sha1.i: $(tor_SOURCES)
+or_sha1.i: $(tor_SOURCES) $(libtor_a_SOURCES)
if test "@SHA1SUM@" != none; then \
- @SHA1SUM@ $(tor_SOURCES) | @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \
+ @SHA1SUM@ $(tor_SOURCES) $(libtor_a_SOURCES) | \
+ @SED@ -n 's/^\(.*\)$$/"\1\\n"/p' > or_sha1.i; \
elif test "@OPENSSL@" != none; then \
- @OPENSSL@ sha1 $(tor_SOURCES) | @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > or_sha1.i; \
+ @OPENSSL@ sha1 $(tor_SOURCES) $(libtor_a_SOURCES) | \
+ @SED@ -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > or_sha1.i; \
else \
rm or_sha1.i; \
touch or_sha1.i; \
diff --git a/src/or/Makefile.nmake b/src/or/Makefile.nmake
new file mode 100644
index 000000000..919edbbf2
--- /dev/null
+++ b/src/or/Makefile.nmake
@@ -0,0 +1,28 @@
+all: tor.exe
+
+CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common
+
+LIBS = ..\..\..\build-alpha\lib\libevent.a \
+ ..\..\..\build-alpha\lib\libcrypto.a \
+ ..\..\..\build-alpha\lib\libssl.a \
+ ..\..\..\build-alpha\lib\libz.a \
+ ws2_32.lib advapi32.lib shell32.lib
+
+LIBTOR_OBJECTS = buffers.obj circuitbuild.obj circuitlist.obj circuituse.obj \
+ command.obj config.obj connection.obj connection_edge.obj \
+ connection_or.obj control.obj cpuworker.obj directory.obj \
+ dirserv.obj dirvote.obj dns.obj dnsserv.obj geoip.obj \
+ hibernate.obj main.obj microdesc.obj networkstatus.obj \
+ nodelist.obj onion.obj policies.obj reasons.obj relay.obj \
+ rendclient.obj rendcommon.obj rendmid.obj rendservice.obj \
+ rephist.obj router.obj routerlist.obj routerparse.obj status.obj \
+ config_codedigest.obj ntmain.obj
+
+libtor.lib: $(LIBTOR_OBJECTS)
+ lib $(LIBTOR_OBJECTS) /out:libtor.lib
+
+tor.exe: libtor.lib tor_main.obj
+ $(CC) $(CFLAGS) $(LIBS) libtor.lib ..\common\*.lib tor_main.obj
+
+clean:
+ del $(LIBTOR_OBJECTS) *.lib tor.exe
diff --git a/src/or/buffers.c b/src/or/buffers.c
index 05163637f..f4aac0f0e 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -23,9 +23,6 @@
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
-#ifdef HAVE_SYS_UIO_H
-#include <sys/uio.h>
-#endif
//#define PARANOIA
@@ -56,6 +53,13 @@
* forever.
*/
+static int parse_socks(const char *data, size_t datalen, socks_request_t *req,
+ int log_sockstype, int safe_socks, ssize_t *drain_out,
+ size_t *want_length_out);
+static int parse_socks_client(const uint8_t *data, size_t datalen,
+ int state, char **reason,
+ ssize_t *drain_out);
+
/* Chunk manipulation functions */
/** A single chunk on a buffer or in a freelist. */
@@ -64,8 +68,8 @@ typedef struct chunk_t {
size_t datalen; /**< The number of bytes stored in this chunk */
size_t memlen; /**< The number of usable bytes of storage in <b>mem</b>. */
char *data; /**< A pointer to the first byte of data stored in <b>mem</b>. */
- char mem[1]; /**< The actual memory used for storage in this chunk. May be
- * more than one byte long. */
+ char mem[FLEXIBLE_ARRAY_MEMBER]; /**< The actual memory used for storage in
+ * this chunk. */
} chunk_t;
#define CHUNK_HEADER_LEN STRUCT_OFFSET(chunk_t, mem[0])
@@ -547,11 +551,45 @@ buf_free(buf_t *buf)
{
if (!buf)
return;
+
buf_clear(buf);
buf->magic = 0xdeadbeef;
tor_free(buf);
}
+/** Return a new copy of <b>in_chunk</b> */
+static chunk_t *
+chunk_copy(const chunk_t *in_chunk)
+{
+ chunk_t *newch = tor_memdup(in_chunk, CHUNK_ALLOC_SIZE(in_chunk->memlen));
+ newch->next = NULL;
+ if (in_chunk->data) {
+ off_t offset = in_chunk->data - in_chunk->mem;
+ newch->data = newch->mem + offset;
+ }
+ return newch;
+}
+
+/** Return a new copy of <b>buf</b> */
+buf_t *
+buf_copy(const buf_t *buf)
+{
+ chunk_t *ch;
+ buf_t *out = buf_new();
+ out->default_chunk_size = buf->default_chunk_size;
+ for (ch = buf->head; ch; ch = ch->next) {
+ chunk_t *newch = chunk_copy(ch);
+ if (out->tail) {
+ out->tail->next = newch;
+ out->tail = newch;
+ } else {
+ out->head = out->tail = newch;
+ }
+ }
+ out->datalen = buf->datalen;
+ return out;
+}
+
/** Append a new chunk with enough capacity to hold <b>capacity</b> bytes to
* the tail of <b>buf</b>. If <b>capped</b>, don't allocate a chunk bigger
* than MAX_CHUNK_ALLOC. */
@@ -578,10 +616,6 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
return chunk;
}
-/** If we're using readv and writev, how many chunks are we willing to
- * read/write at a time? */
-#define N_IOV 3
-
/** Read up to <b>at_most</b> bytes from the socket <b>fd</b> into
* <b>chunk</b> (which must be on <b>buf</b>). If we get an EOF, set
* *<b>reached_eof</b> to 1. Return -1 on error, 0 on eof or blocking,
@@ -591,25 +625,9 @@ read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most,
int *reached_eof, int *socket_error)
{
ssize_t read_result;
-#if 0 && defined(HAVE_READV) && !defined(WIN32)
- struct iovec iov[N_IOV];
- int i;
- size_t remaining = at_most;
- for (i=0; chunk && i < N_IOV && remaining; ++i) {
- iov[i].iov_base = CHUNK_WRITE_PTR(chunk);
- if (remaining > CHUNK_REMAINING_CAPACITY(chunk))
- iov[i].iov_len = CHUNK_REMAINING_CAPACITY(chunk);
- else
- iov[i].iov_len = remaining;
- remaining -= iov[i].iov_len;
- chunk = chunk->next;
- }
- read_result = readv(fd, iov, i);
-#else
if (at_most > CHUNK_REMAINING_CAPACITY(chunk))
at_most = CHUNK_REMAINING_CAPACITY(chunk);
read_result = tor_socket_recv(fd, CHUNK_WRITE_PTR(chunk), at_most, 0);
-#endif
if (read_result < 0) {
int e = tor_socket_errno(fd);
@@ -628,14 +646,6 @@ read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most,
return 0;
} else { /* actually got bytes. */
buf->datalen += read_result;
-#if 0 && defined(HAVE_READV) && !defined(WIN32)
- while ((size_t)read_result > CHUNK_REMAINING_CAPACITY(chunk)) {
- chunk->datalen += CHUNK_REMAINING_CAPACITY(chunk);
- read_result -= CHUNK_REMAINING_CAPACITY(chunk);
- chunk = chunk->next;
- tor_assert(chunk);
- }
-#endif
chunk->datalen += read_result;
log_debug(LD_NET,"Read %ld bytes. %d on inbuf.", (long)read_result,
(int)buf->datalen);
@@ -771,25 +781,10 @@ flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz,
size_t *buf_flushlen)
{
ssize_t write_result;
-#if 0 && defined(HAVE_WRITEV) && !defined(WIN32)
- struct iovec iov[N_IOV];
- int i;
- size_t remaining = sz;
- for (i=0; chunk && i < N_IOV && remaining; ++i) {
- iov[i].iov_base = chunk->data;
- if (remaining > chunk->datalen)
- iov[i].iov_len = chunk->datalen;
- else
- iov[i].iov_len = remaining;
- remaining -= iov[i].iov_len;
- chunk = chunk->next;
- }
- write_result = writev(s, iov, i);
-#else
+
if (sz > chunk->datalen)
sz = chunk->datalen;
write_result = tor_socket_send(s, chunk->data, sz, 0);
-#endif
if (write_result < 0) {
int e = tor_socket_errno(s);
@@ -1010,6 +1005,33 @@ fetch_from_buf(char *string, size_t string_len, buf_t *buf)
return (int)buf->datalen;
}
+/** True iff the cell command <b>command</b> is one that implies a
+ * variable-length cell in Tor link protocol <b>linkproto</b>. */
+static inline int
+cell_command_is_var_length(uint8_t command, int linkproto)
+{
+ /* If linkproto is v2 (2), CELL_VERSIONS is the only variable-length cells
+ * work as implemented here. If it's 1, there are no variable-length cells.
+ * Tor does not support other versions right now, and so can't negotiate
+ * them.
+ */
+ switch (linkproto) {
+ case 1:
+ /* Link protocol version 1 has no variable-length cells. */
+ return 0;
+ case 2:
+ /* In link protocol version 2, VERSIONS is the only variable-length cell */
+ return command == CELL_VERSIONS;
+ case 0:
+ case 3:
+ default:
+ /* In link protocol version 3 and later, and in version "unknown",
+ * commands 128 and higher indicate variable-length. VERSIONS is
+ * grandfathered in. */
+ return command == CELL_VERSIONS || command >= 128;
+ }
+}
+
/** Check <b>buf</b> for a variable-length cell according to the rules of link
* protocol version <b>linkproto</b>. If one is found, pull it off the buffer
* and assign a newly allocated var_cell_t to *<b>out</b>, and return 1.
@@ -1024,12 +1046,6 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
var_cell_t *result;
uint8_t command;
uint16_t length;
- /* If linkproto is unknown (0) or v2 (2), variable-length cells work as
- * implemented here. If it's 1, there are no variable-length cells. Tor
- * does not support other versions right now, and so can't negotiate them.
- */
- if (linkproto == 1)
- return 0;
check();
*out = NULL;
if (buf->datalen < VAR_CELL_HEADER_SIZE)
@@ -1037,7 +1053,7 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
peek_from_buf(hdr, sizeof(hdr), buf);
command = get_uint8(hdr+2);
- if (!(CELL_COMMAND_IS_VAR_LENGTH(command)))
+ if (!(cell_command_is_var_length(command, linkproto)))
return 0;
length = ntohs(get_uint16(hdr+3));
@@ -1056,6 +1072,91 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
return 1;
}
+#ifdef USE_BUFFEREVENTS
+/** Try to read <b>n</b> bytes from <b>buf</b> at <b>pos</b> (which may be
+ * NULL for the start of the buffer), copying the data only if necessary. Set
+ * *<b>data_out</b> to a pointer to the desired bytes. Set <b>free_out</b>
+ * to 1 if we needed to malloc *<b>data</b> because the original bytes were
+ * noncontiguous; 0 otherwise. Return the number of bytes actually available
+ * at *<b>data_out</b>.
+ */
+static ssize_t
+inspect_evbuffer(struct evbuffer *buf, char **data_out, size_t n,
+ int *free_out, struct evbuffer_ptr *pos)
+{
+ int n_vecs, i;
+
+ if (evbuffer_get_length(buf) < n)
+ n = evbuffer_get_length(buf);
+ if (n == 0)
+ return 0;
+ n_vecs = evbuffer_peek(buf, n, pos, NULL, 0);
+ tor_assert(n_vecs > 0);
+ if (n_vecs == 1) {
+ struct evbuffer_iovec v;
+ i = evbuffer_peek(buf, n, pos, &v, 1);
+ tor_assert(i == 1);
+ *data_out = v.iov_base;
+ *free_out = 0;
+ return v.iov_len;
+ } else {
+ ev_ssize_t copied;
+ *data_out = tor_malloc(n);
+ *free_out = 1;
+ copied = evbuffer_copyout(buf, *data_out, n);
+ tor_assert(copied >= 0 && (size_t)copied == n);
+ return copied;
+ }
+}
+
+/** As fetch_var_cell_from_buf, buf works on an evbuffer. */
+int
+fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out,
+ int linkproto)
+{
+ char *hdr = NULL;
+ int free_hdr = 0;
+ size_t n;
+ size_t buf_len;
+ uint8_t command;
+ uint16_t cell_length;
+ var_cell_t *cell;
+ int result = 0;
+
+ *out = NULL;
+ buf_len = evbuffer_get_length(buf);
+ if (buf_len < VAR_CELL_HEADER_SIZE)
+ return 0;
+
+ n = inspect_evbuffer(buf, &hdr, VAR_CELL_HEADER_SIZE, &free_hdr, NULL);
+ tor_assert(n >= VAR_CELL_HEADER_SIZE);
+
+ command = get_uint8(hdr+2);
+ if (!(cell_command_is_var_length(command, linkproto))) {
+ goto done;
+ }
+
+ cell_length = ntohs(get_uint16(hdr+3));
+ if (buf_len < (size_t)(VAR_CELL_HEADER_SIZE+cell_length)) {
+ result = 1; /* Not all here yet. */
+ goto done;
+ }
+
+ cell = var_cell_new(cell_length);
+ cell->command = command;
+ cell->circ_id = ntohs(get_uint16(hdr));
+ evbuffer_drain(buf, VAR_CELL_HEADER_SIZE);
+ evbuffer_remove(buf, cell->payload, cell_length);
+ *out = cell;
+ result = 1;
+
+ done:
+ if (free_hdr && hdr)
+ tor_free(hdr);
+ return result;
+}
+#endif
+
/** Move up to *<b>buf_flushlen</b> bytes from <b>buf_in</b> to
* <b>buf_out</b>, and modify *<b>buf_flushlen</b> appropriately.
* Return the number of bytes actually copied.
@@ -1063,8 +1164,7 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
int
move_buf_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen)
{
- /* XXXX we can do way better here, but this doesn't turn up in any
- * profiles. */
+ /* We can do way better here, but this doesn't turn up in any profiles. */
char b[4096];
size_t cp, len;
len = *buf_flushlen;
@@ -1299,6 +1399,94 @@ fetch_from_buf_http(buf_t *buf,
return 1;
}
+#ifdef USE_BUFFEREVENTS
+/** As fetch_from_buf_http, buf works on an evbuffer. */
+int
+fetch_from_evbuffer_http(struct evbuffer *buf,
+ char **headers_out, size_t max_headerlen,
+ char **body_out, size_t *body_used, size_t max_bodylen,
+ int force_complete)
+{
+ struct evbuffer_ptr crlf, content_length;
+ size_t headerlen, bodylen, contentlen;
+
+ /* Find the first \r\n\r\n in the buffer */
+ crlf = evbuffer_search(buf, "\r\n\r\n", 4, NULL);
+ if (crlf.pos < 0) {
+ /* We didn't find one. */
+ if (evbuffer_get_length(buf) > max_headerlen)
+ return -1; /* Headers too long. */
+ return 0; /* Headers not here yet. */
+ } else if (crlf.pos > (int)max_headerlen) {
+ return -1; /* Headers too long. */
+ }
+
+ headerlen = crlf.pos + 4; /* Skip over the \r\n\r\n */
+ bodylen = evbuffer_get_length(buf) - headerlen;
+ if (bodylen > max_bodylen)
+ return -1; /* body too long */
+
+ /* Look for the first occurrence of CONTENT_LENGTH insize buf before the
+ * crlfcrlf */
+ content_length = evbuffer_search_range(buf, CONTENT_LENGTH,
+ strlen(CONTENT_LENGTH), NULL, &crlf);
+
+ if (content_length.pos >= 0) {
+ /* We found a content_length: parse it and figure out if the body is here
+ * yet. */
+ struct evbuffer_ptr eol;
+ char *data = NULL;
+ int free_data = 0;
+ int n, i;
+ n = evbuffer_ptr_set(buf, &content_length, strlen(CONTENT_LENGTH),
+ EVBUFFER_PTR_ADD);
+ tor_assert(n == 0);
+ eol = evbuffer_search_eol(buf, &content_length, NULL, EVBUFFER_EOL_CRLF);
+ tor_assert(eol.pos > content_length.pos);
+ tor_assert(eol.pos <= crlf.pos);
+ inspect_evbuffer(buf, &data, eol.pos - content_length.pos, &free_data,
+ &content_length);
+
+ i = atoi(data);
+ if (free_data)
+ tor_free(data);
+ if (i < 0) {
+ log_warn(LD_PROTOCOL, "Content-Length is less than zero; it looks like "
+ "someone is trying to crash us.");
+ return -1;
+ }
+ contentlen = i;
+ /* if content-length is malformed, then our body length is 0. fine. */
+ log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen);
+ if (bodylen < contentlen) {
+ if (!force_complete) {
+ log_debug(LD_HTTP,"body not all here yet.");
+ return 0; /* not all there yet */
+ }
+ }
+ if (bodylen > contentlen) {
+ bodylen = contentlen;
+ log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen);
+ }
+ }
+
+ if (headers_out) {
+ *headers_out = tor_malloc(headerlen+1);
+ evbuffer_remove(buf, *headers_out, headerlen);
+ (*headers_out)[headerlen] = '\0';
+ }
+ if (body_out) {
+ tor_assert(headers_out);
+ tor_assert(body_used);
+ *body_used = bodylen;
+ *body_out = tor_malloc(bodylen+1);
+ evbuffer_remove(buf, *body_out, bodylen);
+ (*body_out)[bodylen] = '\0';
+ }
+ return 1;
+}
+#endif
+
/**
* Wait this many seconds before warning the user about using SOCKS unsafely
* again (requires that WarnUnsafeSocks is turned on). */
@@ -1313,7 +1501,7 @@ log_unsafe_socks_warning(int socks_protocol, const char *address,
{
static ratelim_t socks_ratelim = RATELIM_INIT(SOCKS_WARN_INTERVAL);
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
char *m = NULL;
if (! options->WarnUnsafeSocks)
return;
@@ -1340,6 +1528,31 @@ log_unsafe_socks_warning(int socks_protocol, const char *address,
* actually significantly higher than the longest possible socks message. */
#define MAX_SOCKS_MESSAGE_LEN 512
+/** Return a new socks_request_t. */
+socks_request_t *
+socks_request_new(void)
+{
+ return tor_malloc_zero(sizeof(socks_request_t));
+}
+
+/** Free all storage held in the socks_request_t <b>req</b>. */
+void
+socks_request_free(socks_request_t *req)
+{
+ if (!req)
+ return;
+ if (req->username) {
+ memset(req->username, 0x10, req->usernamelen);
+ tor_free(req->username);
+ }
+ if (req->password) {
+ memset(req->password, 0x04, req->passwordlen);
+ tor_free(req->password);
+ }
+ memset(req, 0xCC, sizeof(socks_request_t));
+ tor_free(req);
+}
+
/** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one
* of the forms
* - socks4: "socksheader username\\0"
@@ -1369,59 +1582,241 @@ int
fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
int log_sockstype, int safe_socks)
{
+ int res;
+ ssize_t n_drain;
+ size_t want_length = 128;
+
+ if (buf->datalen < 2) /* version and another byte */
+ return 0;
+
+ do {
+ n_drain = 0;
+ buf_pullup(buf, want_length, 0);
+ tor_assert(buf->head && buf->head->datalen >= 2);
+ want_length = 0;
+
+ res = parse_socks(buf->head->data, buf->head->datalen, req, log_sockstype,
+ safe_socks, &n_drain, &want_length);
+
+ if (n_drain < 0)
+ buf_clear(buf);
+ else if (n_drain > 0)
+ buf_remove_from_front(buf, n_drain);
+
+ } while (res == 0 && buf->head && want_length < buf->datalen &&
+ buf->datalen >= 2);
+
+ return res;
+}
+
+#ifdef USE_BUFFEREVENTS
+/* As fetch_from_buf_socks(), but targets an evbuffer instead. */
+int
+fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req,
+ int log_sockstype, int safe_socks)
+{
+ char *data;
+ ssize_t n_drain;
+ size_t datalen, buflen, want_length;
+ int res;
+
+ buflen = evbuffer_get_length(buf);
+ if (buflen < 2)
+ return 0;
+
+ {
+ /* See if we can find the socks request in the first chunk of the buffer.
+ */
+ struct evbuffer_iovec v;
+ int i;
+ n_drain = 0;
+ i = evbuffer_peek(buf, -1, NULL, &v, 1);
+ tor_assert(i == 1);
+ data = v.iov_base;
+ datalen = v.iov_len;
+ want_length = 0;
+
+ res = parse_socks(data, datalen, req, log_sockstype,
+ safe_socks, &n_drain, &want_length);
+
+ if (n_drain < 0)
+ evbuffer_drain(buf, evbuffer_get_length(buf));
+ else if (n_drain > 0)
+ evbuffer_drain(buf, n_drain);
+
+ if (res)
+ return res;
+ }
+
+ /* Okay, the first chunk of the buffer didn't have a complete socks request.
+ * That means that either we don't have a whole socks request at all, or
+ * it's gotten split up. We're going to try passing parse_socks() bigger
+ * and bigger chunks until either it says "Okay, I got it", or it says it
+ * will need more data than we currently have. */
+
+ /* Loop while we have more data that we haven't given parse_socks() yet. */
+ do {
+ int free_data = 0;
+ const size_t last_wanted = want_length;
+ n_drain = 0;
+ data = NULL;
+ datalen = inspect_evbuffer(buf, &data, want_length, &free_data, NULL);
+
+ want_length = 0;
+ res = parse_socks(data, datalen, req, log_sockstype,
+ safe_socks, &n_drain, &want_length);
+
+ if (free_data)
+ tor_free(data);
+
+ if (n_drain < 0)
+ evbuffer_drain(buf, evbuffer_get_length(buf));
+ else if (n_drain > 0)
+ evbuffer_drain(buf, n_drain);
+
+ if (res == 0 && n_drain == 0 && want_length <= last_wanted) {
+ /* If we drained nothing, and we didn't ask for more than last time,
+ * then we probably wanted more data than the buffer actually had,
+ * and we're finding out that we're not satisified with it. It's
+ * time to break until we have more data. */
+ break;
+ }
+
+ buflen = evbuffer_get_length(buf);
+ } while (res == 0 && want_length <= buflen && buflen >= 2);
+
+ return res;
+}
+#endif
+
+/** Implementation helper to implement fetch_from_*_socks. Instead of looking
+ * at a buffer's contents, we look at the <b>datalen</b> bytes of data in
+ * <b>data</b>. Instead of removing data from the buffer, we set
+ * <b>drain_out</b> to the amount of data that should be removed (or -1 if the
+ * buffer should be cleared). Instead of pulling more data into the first
+ * chunk of the buffer, we set *<b>want_length_out</b> to the number of bytes
+ * we'd like to see in the input buffer, if they're available. */
+static int
+parse_socks(const char *data, size_t datalen, socks_request_t *req,
+ int log_sockstype, int safe_socks, ssize_t *drain_out,
+ size_t *want_length_out)
+{
unsigned int len;
char tmpbuf[TOR_ADDR_BUF_LEN+1];
tor_addr_t destaddr;
uint32_t destip;
uint8_t socksver;
- enum {socks4, socks4a} socks4_prot = socks4a;
char *next, *startaddr;
+ unsigned char usernamelen, passlen;
struct in_addr in;
- if (buf->datalen < 2) /* version and another byte */
+ if (datalen < 2) {
+ /* We always need at least 2 bytes. */
+ *want_length_out = 2;
return 0;
+ }
- buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, 0);
- tor_assert(buf->head && buf->head->datalen >= 2);
+ if (req->socks_version == 5 && !req->got_auth) {
+ /* See if we have received authentication. Strictly speaking, we should
+ also check whether we actually negotiated username/password
+ authentication. But some broken clients will send us authentication
+ even if we negotiated SOCKS_NO_AUTH. */
+ if (*data == 1) { /* username/pass version 1 */
+ /* Format is: authversion [1 byte] == 1
+ usernamelen [1 byte]
+ username [usernamelen bytes]
+ passlen [1 byte]
+ password [passlen bytes] */
+ usernamelen = (unsigned char)*(data + 1);
+ if (datalen < 2u + usernamelen + 1u) {
+ *want_length_out = 2u + usernamelen + 1u;
+ return 0;
+ }
+ passlen = (unsigned char)*(data + 2u + usernamelen);
+ if (datalen < 2u + usernamelen + 1u + passlen) {
+ *want_length_out = 2u + usernamelen + 1u + passlen;
+ return 0;
+ }
+ req->replylen = 2; /* 2 bytes of response */
+ req->reply[0] = 5;
+ req->reply[1] = 0; /* authentication successful */
+ log_debug(LD_APP,
+ "socks5: Accepted username/password without checking.");
+ if (usernamelen) {
+ req->username = tor_memdup(data+2u, usernamelen);
+ req->usernamelen = usernamelen;
+ }
+ if (passlen) {
+ req->password = tor_memdup(data+3u+usernamelen, passlen);
+ req->passwordlen = passlen;
+ }
+ *drain_out = 2u + usernamelen + 1u + passlen;
+ req->got_auth = 1;
+ *want_length_out = 7; /* Minimal socks5 sommand. */
+ return 0;
+ } else if (req->auth_type == SOCKS_USER_PASS) {
+ /* unknown version byte */
+ log_warn(LD_APP, "Socks5 username/password version %d not recognized; "
+ "rejecting.", (int)*data);
+ return -1;
+ }
+ }
- socksver = *buf->head->data;
+ socksver = *data;
switch (socksver) { /* which version of socks? */
-
case 5: /* socks5 */
if (req->socks_version != 5) { /* we need to negotiate a method */
- unsigned char nummethods = (unsigned char)*(buf->head->data+1);
+ unsigned char nummethods = (unsigned char)*(data+1);
+ int r=0;
tor_assert(!req->socks_version);
- if (buf->datalen < 2u+nummethods)
+ if (datalen < 2u+nummethods) {
+ *want_length_out = 2u+nummethods;
return 0;
- buf_pullup(buf, 2u+nummethods, 0);
- if (!nummethods || !memchr(buf->head->data+2, 0, nummethods)) {
+ }
+ if (!nummethods)
+ return -1;
+ req->replylen = 2; /* 2 bytes of response */
+ req->reply[0] = 5; /* socks5 reply */
+ if (memchr(data+2, SOCKS_NO_AUTH, nummethods)) {
+ req->reply[1] = SOCKS_NO_AUTH; /* tell client to use "none" auth
+ method */
+ req->socks_version = 5; /* remember we've already negotiated auth */
+ log_debug(LD_APP,"socks5: accepted method 0 (no authentication)");
+ r=0;
+ } else if (memchr(data+2, SOCKS_USER_PASS, nummethods)) {
+ req->auth_type = SOCKS_USER_PASS;
+ req->reply[1] = SOCKS_USER_PASS; /* tell client to use "user/pass"
+ auth method */
+ req->socks_version = 5; /* remember we've already negotiated auth */
+ log_debug(LD_APP,"socks5: accepted method 2 (username/password)");
+ r=0;
+ } else {
log_warn(LD_APP,
- "socks5: offered methods don't include 'no auth'. "
- "Rejecting.");
- req->replylen = 2; /* 2 bytes of response */
- req->reply[0] = 5;
+ "socks5: offered methods don't include 'no auth' or "
+ "username/password. Rejecting.");
req->reply[1] = '\xFF'; /* reject all methods */
- return -1;
+ r=-1;
}
- /* remove packet from buf. also remove any other extraneous
- * bytes, to support broken socks clients. */
- buf_clear(buf);
+ /* Remove packet from buf. Some SOCKS clients will have sent extra
+ * junk at this point; let's hope it's an authentication message. */
+ *drain_out = 2u + nummethods;
- req->replylen = 2; /* 2 bytes of response */
- req->reply[0] = 5; /* socks5 reply */
- req->reply[1] = 0; /* tell client to use "none" auth method */
- req->socks_version = 5; /* remember we've already negotiated auth */
- log_debug(LD_APP,"socks5: accepted method 0");
- return 0;
+ return r;
+ }
+ if (req->auth_type != SOCKS_NO_AUTH && !req->got_auth) {
+ log_warn(LD_APP,
+ "socks5: negotiated authentication, but none provided");
+ return -1;
}
/* we know the method; read in the request */
log_debug(LD_APP,"socks5: checking request");
- if (buf->datalen < 8) /* basic info plus >=2 for addr plus 2 for port */
+ if (datalen < 7) {/* basic info plus >=1 for addr plus 2 for port */
+ *want_length_out = 7;
return 0; /* not yet */
- tor_assert(buf->head->datalen >= 8);
- req->command = (unsigned char) *(buf->head->data+1);
+ }
+ req->command = (unsigned char) *(data+1);
if (req->command != SOCKS_COMMAND_CONNECT &&
req->command != SOCKS_COMMAND_RESOLVE &&
req->command != SOCKS_COMMAND_RESOLVE_PTR) {
@@ -1430,19 +1825,21 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
req->command);
return -1;
}
- switch (*(buf->head->data+3)) { /* address type */
+ switch (*(data+3)) { /* address type */
case 1: /* IPv4 address */
case 4: /* IPv6 address */ {
- const int is_v6 = *(buf->head->data+3) == 4;
+ const int is_v6 = *(data+3) == 4;
const unsigned addrlen = is_v6 ? 16 : 4;
log_debug(LD_APP,"socks5: ipv4 address type");
- if (buf->datalen < 6+addrlen) /* ip/port there? */
+ if (datalen < 6+addrlen) {/* ip/port there? */
+ *want_length_out = 6+addrlen;
return 0; /* not yet */
+ }
if (is_v6)
- tor_addr_from_ipv6_bytes(&destaddr, buf->head->data+4);
+ tor_addr_from_ipv6_bytes(&destaddr, data+4);
else
- tor_addr_from_ipv4n(&destaddr, get_uint32(buf->head->data+4));
+ tor_addr_from_ipv4n(&destaddr, get_uint32(data+4));
tor_addr_to_str(tmpbuf, &destaddr, sizeof(tmpbuf), 1);
@@ -1454,8 +1851,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
return -1;
}
strlcpy(req->address,tmpbuf,sizeof(req->address));
- req->port = ntohs(get_uint16(buf->head->data+4+addrlen));
- buf_remove_from_front(buf, 6+addrlen);
+ req->port = ntohs(get_uint16(data+4+addrlen));
+ *drain_out = 6+addrlen;
if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
!addressmap_have_mapping(req->address,0)) {
log_unsafe_socks_warning(5, req->address, req->port, safe_socks);
@@ -1471,21 +1868,21 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
"hostname type. Rejecting.");
return -1;
}
- len = (unsigned char)*(buf->head->data+4);
- if (buf->datalen < 7+len) /* addr/port there? */
+ len = (unsigned char)*(data+4);
+ if (datalen < 7+len) { /* addr/port there? */
+ *want_length_out = 7+len;
return 0; /* not yet */
- buf_pullup(buf, 7+len, 0);
- tor_assert(buf->head->datalen >= 7+len);
+ }
if (len+1 > MAX_SOCKS_ADDR_LEN) {
log_warn(LD_APP,
"socks5 hostname is %d bytes, which doesn't fit in "
"%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN);
return -1;
}
- memcpy(req->address,buf->head->data+5,len);
+ memcpy(req->address,data+5,len);
req->address[len] = 0;
- req->port = ntohs(get_uint16(buf->head->data+5+len));
- buf_remove_from_front(buf, 5+len+2);
+ req->port = ntohs(get_uint16(data+5+len));
+ *drain_out = 5+len+2;
if (!tor_strisprint(req->address) || strchr(req->address,'\"')) {
log_warn(LD_PROTOCOL,
"Your application (using socks5 to port %d) gave Tor "
@@ -1495,25 +1892,29 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
}
if (log_sockstype)
log_notice(LD_APP,
- "Your application (using socks5 to port %d) gave "
- "Tor a hostname, which means Tor will do the DNS resolve "
- "for you. This is good.", req->port);
+ "Your application (using socks5 to port %d) instructed "
+ "Tor to take care of the DNS resolution itself if "
+ "necessary. This is good.", req->port);
return 1;
default: /* unsupported */
log_warn(LD_APP,"socks5: unsupported address type %d. Rejecting.",
- (int) *(buf->head->data+3));
+ (int) *(data+3));
return -1;
}
tor_assert(0);
- case 4: /* socks4 */
- /* http://archive.socks.permeo.com/protocol/socks4.protocol */
- /* http://archive.socks.permeo.com/protocol/socks4a.protocol */
+ case 4: { /* socks4 */
+ enum {socks4, socks4a} socks4_prot = socks4a;
+ const char *authstart, *authend;
+ /* http://ss5.sourceforge.net/socks4.protocol.txt */
+ /* http://ss5.sourceforge.net/socks4A.protocol.txt */
req->socks_version = 4;
- if (buf->datalen < SOCKS4_NETWORK_LEN) /* basic info available? */
+ if (datalen < SOCKS4_NETWORK_LEN) {/* basic info available? */
+ *want_length_out = SOCKS4_NETWORK_LEN;
return 0; /* not yet */
- buf_pullup(buf, 1280, 0);
- req->command = (unsigned char) *(buf->head->data+1);
+ }
+ // buf_pullup(buf, 1280, 0);
+ req->command = (unsigned char) *(data+1);
if (req->command != SOCKS_COMMAND_CONNECT &&
req->command != SOCKS_COMMAND_RESOLVE) {
/* not a connect or resolve? we don't support it. (No resolve_ptr with
@@ -1523,8 +1924,8 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
return -1;
}
- req->port = ntohs(get_uint16(buf->head->data+2));
- destip = ntohl(get_uint32(buf->head->data+4));
+ req->port = ntohs(get_uint16(data+2));
+ destip = ntohl(get_uint32(data+4));
if ((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) {
log_warn(LD_APP,"socks4: Port or DestIP is zero. Rejecting.");
return -1;
@@ -1544,17 +1945,20 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
socks4_prot = socks4;
}
- next = memchr(buf->head->data+SOCKS4_NETWORK_LEN, 0,
- buf->head->datalen-SOCKS4_NETWORK_LEN);
+ authstart = data + SOCKS4_NETWORK_LEN;
+ next = memchr(authstart, 0,
+ datalen-SOCKS4_NETWORK_LEN);
if (!next) {
- if (buf->head->datalen >= 1024) {
+ if (datalen >= 1024) {
log_debug(LD_APP, "Socks4 user name too long; rejecting.");
return -1;
}
log_debug(LD_APP,"socks4: Username not here yet.");
+ *want_length_out = datalen+1024; /* More than we need, but safe */
return 0;
}
- tor_assert(next < CHUNK_WRITE_PTR(buf->head));
+ authend = next;
+ tor_assert(next < data+datalen);
startaddr = NULL;
if (socks4_prot != socks4a &&
@@ -1565,18 +1969,20 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
return -1;
}
if (socks4_prot == socks4a) {
- if (next+1 == CHUNK_WRITE_PTR(buf->head)) {
+ if (next+1 == data+datalen) {
log_debug(LD_APP,"socks4: No part of destaddr here yet.");
+ *want_length_out = datalen + 1024; /* More than we need, but safe */
return 0;
}
startaddr = next+1;
- next = memchr(startaddr, 0, CHUNK_WRITE_PTR(buf->head)-startaddr);
+ next = memchr(startaddr, 0, data + datalen - startaddr);
if (!next) {
- if (buf->head->datalen >= 1024) {
+ if (datalen >= 1024) {
log_debug(LD_APP,"socks4: Destaddr too long.");
return -1;
}
log_debug(LD_APP,"socks4: Destaddr not all here yet.");
+ *want_length_out = datalen + 1024; /* More than we need, but safe */
return 0;
}
if (MAX_SOCKS_ADDR_LEN <= next-startaddr) {
@@ -1587,9 +1993,9 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
if (log_sockstype)
log_notice(LD_APP,
- "Your application (using socks4a to port %d) gave "
- "Tor a hostname, which means Tor will do the DNS resolve "
- "for you. This is good.", req->port);
+ "Your application (using socks4a to port %d) instructed "
+ "Tor to take care of the DNS resolution itself if "
+ "necessary. This is good.", req->port);
}
log_debug(LD_APP,"socks4: Everything is here. Success.");
strlcpy(req->address, startaddr ? startaddr : tmpbuf,
@@ -1601,15 +2007,20 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
req->port, escaped(req->address));
return -1;
}
+ if (authend != authstart) {
+ req->got_auth = 1;
+ req->usernamelen = authend - authstart;
+ req->username = tor_memdup(authstart, authend - authstart);
+ }
/* next points to the final \0 on inbuf */
- buf_remove_from_front(buf, next - buf->head->data + 1);
+ *drain_out = next - data + 1;
return 1;
-
+ }
case 'G': /* get */
case 'H': /* head */
case 'P': /* put/post */
case 'C': /* connect */
- strlcpy(req->reply,
+ strlcpy((char*)req->reply,
"HTTP/1.0 501 Tor is not an HTTP Proxy\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n\r\n"
"<html>\n"
@@ -1635,14 +2046,15 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
"</body>\n"
"</html>\n"
, MAX_SOCKS_REPLY_LEN);
- req->replylen = strlen(req->reply)+1;
+ req->replylen = strlen((char*)req->reply)+1;
/* fall through */
default: /* version is not socks4 or socks5 */
log_warn(LD_APP,
"Socks version %d not recognized. (Tor is not an http proxy.)",
- *(buf->head->data));
+ *(data));
{
- char *tmp = tor_strndup(buf->head->data, 8); /*XXXX what if longer?*/
+ /* Tell the controller the first 8 bytes. */
+ char *tmp = tor_strndup(data, datalen < 8 ? datalen : 8);
control_event_client_status(LOG_WARN,
"SOCKS_UNKNOWN_PROTOCOL DATA=\"%s\"",
escaped(tmp));
@@ -1664,21 +2076,67 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
int
fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
{
- unsigned char *data;
- size_t addrlen;
-
+ ssize_t drain = 0;
+ int r;
if (buf->datalen < 2)
return 0;
buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, 0);
tor_assert(buf->head && buf->head->datalen >= 2);
- data = (unsigned char *) buf->head->data;
+ r = parse_socks_client((uint8_t*)buf->head->data, buf->head->datalen,
+ state, reason, &drain);
+ if (drain > 0)
+ buf_remove_from_front(buf, drain);
+ else if (drain < 0)
+ buf_clear(buf);
+
+ return r;
+}
+
+#ifdef USE_BUFFEREVENTS
+/** As fetch_from_buf_socks_client, buf works on an evbuffer */
+int
+fetch_from_evbuffer_socks_client(struct evbuffer *buf, int state,
+ char **reason)
+{
+ ssize_t drain = 0;
+ uint8_t *data;
+ size_t datalen;
+ int r;
+
+ /* Linearize the SOCKS response in the buffer, up to 128 bytes.
+ * (parse_socks_client shouldn't need to see anything beyond that.) */
+ datalen = evbuffer_get_length(buf);
+ if (datalen > MAX_SOCKS_MESSAGE_LEN)
+ datalen = MAX_SOCKS_MESSAGE_LEN;
+ data = evbuffer_pullup(buf, datalen);
+
+ r = parse_socks_client(data, datalen, state, reason, &drain);
+ if (drain > 0)
+ evbuffer_drain(buf, drain);
+ else if (drain < 0)
+ evbuffer_drain(buf, evbuffer_get_length(buf));
+
+ return r;
+}
+#endif
+
+/** Implementation logic for fetch_from_*_socks_client. */
+static int
+parse_socks_client(const uint8_t *data, size_t datalen,
+ int state, char **reason,
+ ssize_t *drain_out)
+{
+ unsigned int addrlen;
+ *drain_out = 0;
+ if (datalen < 2)
+ return 0;
switch (state) {
case PROXY_SOCKS4_WANT_CONNECT_OK:
/* Wait for the complete response */
- if (buf->head->datalen < 8)
+ if (datalen < 8)
return 0;
if (data[1] != 0x5a) {
@@ -1687,7 +2145,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
}
/* Success */
- buf_remove_from_front(buf, 8);
+ *drain_out = 8;
return 1;
case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
@@ -1699,7 +2157,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
}
log_info(LD_NET, "SOCKS 5 client: continuing without authentication");
- buf_clear(buf);
+ *drain_out = -1;
return 1;
case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
@@ -1709,11 +2167,11 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
case 0x00:
log_info(LD_NET, "SOCKS 5 client: we have auth details but server "
"doesn't require authentication.");
- buf_clear(buf);
+ *drain_out = -1;
return 1;
case 0x02:
log_info(LD_NET, "SOCKS 5 client: need authentication.");
- buf_clear(buf);
+ *drain_out = -1;
return 2;
/* fall through */
}
@@ -1730,7 +2188,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
}
log_info(LD_NET, "SOCKS 5 client: authentication successful.");
- buf_clear(buf);
+ *drain_out = -1;
return 1;
case PROXY_SOCKS5_WANT_CONNECT_OK:
@@ -1739,7 +2197,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
* the data used */
/* wait for address type field to arrive */
- if (buf->datalen < 4)
+ if (datalen < 4)
return 0;
switch (data[3]) {
@@ -1750,7 +2208,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
addrlen = 16;
break;
case 0x03: /* fqdn (can this happen here?) */
- if (buf->datalen < 5)
+ if (datalen < 5)
return 0;
addrlen = 1 + data[4];
break;
@@ -1760,7 +2218,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
}
/* wait for address and port */
- if (buf->datalen < 6 + addrlen)
+ if (datalen < 6 + addrlen)
return 0;
if (data[1] != 0x00) {
@@ -1768,7 +2226,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
return -1;
}
- buf_remove_from_front(buf, 6 + addrlen);
+ *drain_out = 6 + addrlen;
return 1;
}
@@ -1794,6 +2252,27 @@ peek_buf_has_control0_command(buf_t *buf)
return 0;
}
+#ifdef USE_BUFFEREVENTS
+int
+peek_evbuffer_has_control0_command(struct evbuffer *buf)
+{
+ int result = 0;
+ if (evbuffer_get_length(buf) >= 4) {
+ int free_out = 0;
+ char *data = NULL;
+ size_t n = inspect_evbuffer(buf, &data, 4, &free_out, NULL);
+ uint16_t cmd;
+ tor_assert(n >= 4);
+ cmd = ntohs(get_uint16(data+2));
+ if (cmd <= 0x14)
+ result = 1;
+ if (free_out)
+ tor_free(data);
+ }
+ return result;
+}
+#endif
+
/** Return the index within <b>buf</b> at which <b>ch</b> first appears,
* or -1 if <b>ch</b> does not appear on buf. */
static off_t
@@ -1811,12 +2290,12 @@ buf_find_offset_of_char(buf_t *buf, char ch)
return -1;
}
-/** Try to read a single LF-terminated line from <b>buf</b>, and write it,
- * NUL-terminated, into the *<b>data_len</b> byte buffer at <b>data_out</b>.
- * Set *<b>data_len</b> to the number of bytes in the line, not counting the
- * terminating NUL. Return 1 if we read a whole line, return 0 if we don't
- * have a whole line yet, and return -1 if the line length exceeds
- * *<b>data_len</b>.
+/** Try to read a single LF-terminated line from <b>buf</b>, and write it
+ * (including the LF), NUL-terminated, into the *<b>data_len</b> byte buffer
+ * at <b>data_out</b>. Set *<b>data_len</b> to the number of bytes in the
+ * line, not counting the terminating NUL. Return 1 if we read a whole line,
+ * return 0 if we don't have a whole line yet, and return -1 if the line
+ * length exceeds *<b>data_len</b>.
*/
int
fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len)
@@ -1890,6 +2369,96 @@ write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state,
return 0;
}
+#ifdef USE_BUFFEREVENTS
+int
+write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
+ const char *data, size_t data_len,
+ int done)
+{
+ char *next;
+ size_t old_avail, avail;
+ int over = 0, n;
+ struct evbuffer_iovec vec[1];
+ do {
+ {
+ size_t cap = data_len / 4;
+ if (cap < 128)
+ cap = 128;
+ /* XXXX NM this strategy is fragmentation-prone. We should really have
+ * two iovecs, and write first into the one, and then into the
+ * second if the first gets full. */
+ n = evbuffer_reserve_space(buf, cap, vec, 1);
+ tor_assert(n == 1);
+ }
+
+ next = vec[0].iov_base;
+ avail = old_avail = vec[0].iov_len;
+
+ switch (tor_zlib_process(state, &next, &avail, &data, &data_len, done)) {
+ case TOR_ZLIB_DONE:
+ over = 1;
+ break;
+ case TOR_ZLIB_ERR:
+ return -1;
+ case TOR_ZLIB_OK:
+ if (data_len == 0)
+ over = 1;
+ break;
+ case TOR_ZLIB_BUF_FULL:
+ if (avail) {
+ /* Zlib says we need more room (ZLIB_BUF_FULL). Start a new chunk
+ * automatically, whether were going to or not. */
+ }
+ break;
+ }
+
+ /* XXXX possible infinite loop on BUF_FULL. */
+ vec[0].iov_len = old_avail - avail;
+ evbuffer_commit_space(buf, vec, 1);
+
+ } while (!over);
+ check();
+ return 0;
+}
+#endif
+
+/** Set *<b>output</b> to contain a copy of the data in *<b>input</b> */
+int
+generic_buffer_set_to_copy(generic_buffer_t **output,
+ const generic_buffer_t *input)
+{
+#ifdef USE_BUFFEREVENTS
+ struct evbuffer_ptr ptr;
+ size_t remaining = evbuffer_get_length(input);
+ if (*output) {
+ evbuffer_drain(*output, evbuffer_get_length(*output));
+ } else {
+ if (!(*output = evbuffer_new()))
+ return -1;
+ }
+ evbuffer_ptr_set((struct evbuffer*)input, &ptr, 0, EVBUFFER_PTR_SET);
+ while (remaining) {
+ struct evbuffer_iovec v[4];
+ int n_used, i;
+ n_used = evbuffer_peek((struct evbuffer*)input, -1, &ptr, v, 4);
+ if (n_used < 0)
+ return -1;
+ for (i=0;i<n_used;++i) {
+ evbuffer_add(*output, v[i].iov_base, v[i].iov_len);
+ tor_assert(v[i].iov_len <= remaining);
+ remaining -= v[i].iov_len;
+ evbuffer_ptr_set((struct evbuffer*)input,
+ &ptr, v[i].iov_len, EVBUFFER_PTR_ADD);
+ }
+ }
+#else
+ if (*output)
+ buf_free(*output);
+ *output = buf_copy(input);
+#endif
+ return 0;
+}
+
/** Log an error and exit if <b>buf</b> is corrupted.
*/
void
diff --git a/src/or/buffers.h b/src/or/buffers.h
index 63fab4957..7b2a2acc3 100644
--- a/src/or/buffers.h
+++ b/src/or/buffers.h
@@ -16,6 +16,7 @@ buf_t *buf_new(void);
buf_t *buf_new_with_capacity(size_t size);
void buf_free(buf_t *buf);
void buf_clear(buf_t *buf);
+buf_t *buf_copy(const buf_t *buf);
void buf_shrink(buf_t *buf);
void buf_shrink_freelists(int free_all);
void buf_dump_freelist_sizes(int severity);
@@ -41,6 +42,8 @@ int fetch_from_buf_http(buf_t *buf,
char **headers_out, size_t max_headerlen,
char **body_out, size_t *body_used, size_t max_bodylen,
int force_complete);
+socks_request_t *socks_request_new(void);
+void socks_request_free(socks_request_t *req);
int fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
int log_sockstype, int safe_socks);
int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason);
@@ -48,6 +51,41 @@ int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len);
int peek_buf_has_control0_command(buf_t *buf);
+#ifdef USE_BUFFEREVENTS
+int fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out,
+ int linkproto);
+int fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req,
+ int log_sockstype, int safe_socks);
+int fetch_from_evbuffer_socks_client(struct evbuffer *buf, int state,
+ char **reason);
+int fetch_from_evbuffer_http(struct evbuffer *buf,
+ char **headers_out, size_t max_headerlen,
+ char **body_out, size_t *body_used, size_t max_bodylen,
+ int force_complete);
+int peek_evbuffer_has_control0_command(struct evbuffer *buf);
+int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
+ const char *data, size_t data_len,
+ int done);
+#endif
+
+#ifdef USE_BUFFEREVENTS
+#define generic_buffer_new() evbuffer_new()
+#define generic_buffer_len(b) evbuffer_get_length((b))
+#define generic_buffer_add(b,dat,len) evbuffer_add((b),(dat),(len))
+#define generic_buffer_get(b,buf,buflen) evbuffer_remove((b),(buf),(buflen))
+#define generic_buffer_clear(b) evbuffer_drain((b), evbuffer_get_length((b)))
+#define generic_buffer_free(b) evbuffer_free((b))
+#else
+#define generic_buffer_new() buf_new()
+#define generic_buffer_len(b) buf_datalen((b))
+#define generic_buffer_add(b,dat,len) write_to_buf((dat),(len),(b))
+#define generic_buffer_get(b,buf,buflen) fetch_from_buf((buf),(buflen),(b))
+#define generic_buffer_clear(b) buf_clear((b))
+#define generic_buffer_free(b) buf_free((b))
+#endif
+int generic_buffer_set_to_copy(generic_buffer_t **output,
+ const generic_buffer_t *input);
+
void assert_buf_ok(buf_t *buf);
#ifdef BUFFERS_PRIVATE
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index c864fd249..c4b697c9a 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -23,8 +23,10 @@
#include "directory.h"
#include "main.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "onion.h"
#include "policies.h"
+#include "transports.h"
#include "relay.h"
#include "rephist.h"
#include "router.h"
@@ -55,8 +57,8 @@ extern circuit_t *global_circuitlist;
/** An entry_guard_t represents our information about a chosen long-term
* first hop, known as a "helper" node in the literature. We can't just
- * use a routerinfo_t, since we want to remember these even when we
- * don't have a directory. */
+ * use a node_t, since we want to remember these even when we
+ * don't have any directory info. */
typedef struct {
char nickname[MAX_NICKNAME_LEN+1];
char identity[DIGEST_LEN];
@@ -78,6 +80,28 @@ typedef struct {
* at which we last failed to connect to it. */
} entry_guard_t;
+/** Information about a configured bridge. Currently this just matches the
+ * ones in the torrc file, but one day we may be able to learn about new
+ * bridges on our own, and remember them in the state file. */
+typedef struct {
+ /** Address of the bridge. */
+ tor_addr_t addr;
+ /** TLS port for the bridge. */
+ uint16_t port;
+ /** Boolean: We are re-parsing our bridge list, and we are going to remove
+ * this one if we don't find it in the list of configured bridges. */
+ unsigned marked_for_removal : 1;
+ /** Expected identity digest, or all zero bytes if we don't know what the
+ * digest should be. */
+ char identity[DIGEST_LEN];
+
+ /** Name of pluggable transport protocol taken from its config line. */
+ char *transport_name;
+
+ /** When should we next try to fetch a descriptor for this bridge? */
+ download_status_t fetch_status;
+} bridge_info_t;
+
/** A list of our chosen entry guards. */
static smartlist_t *entry_guards = NULL;
/** A value of 1 means that the entry_guards list has changed
@@ -95,11 +119,13 @@ static int circuit_deliver_create_cell(circuit_t *circ,
static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit);
static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
static int onion_extend_cpath(origin_circuit_t *circ);
-static int count_acceptable_routers(smartlist_t *routers);
+static int count_acceptable_nodes(smartlist_t *routers);
static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
static void entry_guards_changed(void);
+static void bridge_free(bridge_info_t *bridge);
+
/**
* This function decides if CBT learning should be disabled. It returns
* true if one or more of the following four conditions are met:
@@ -1532,10 +1558,9 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
hop = circ->cpath;
do {
- routerinfo_t *ri;
- routerstatus_t *rs;
char *elt;
const char *id;
+ const node_t *node;
if (!hop)
break;
if (!verbose && hop->state != CPATH_STATE_OPEN)
@@ -1545,10 +1570,8 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
id = hop->extend_info->identity_digest;
if (verbose_names) {
elt = tor_malloc(MAX_VERBOSE_NICKNAME_LEN+1);
- if ((ri = router_get_by_digest(id))) {
- router_get_verbose_nickname(elt, ri);
- } else if ((rs = router_get_consensus_status_by_id(id))) {
- routerstatus_get_verbose_nickname(elt, rs);
+ if ((node = node_get_by_id(id))) {
+ node_get_verbose_nickname(node, elt);
} else if (is_legal_nickname(hop->extend_info->nickname)) {
elt[0] = '$';
base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
@@ -1560,9 +1583,9 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
}
} else { /* ! verbose_names */
- if ((ri = router_get_by_digest(id)) &&
- ri->is_named) {
- elt = tor_strdup(hop->extend_info->nickname);
+ node = node_get_by_id(id);
+ if (node && node_is_named(node)) {
+ elt = tor_strdup(node_get_nickname(node));
} else {
elt = tor_malloc(HEX_DIGEST_LEN+2);
elt[0] = '$';
@@ -1631,31 +1654,28 @@ void
circuit_rep_hist_note_result(origin_circuit_t *circ)
{
crypt_path_t *hop;
- char *prev_digest = NULL;
- routerinfo_t *router;
+ const char *prev_digest = NULL;
hop = circ->cpath;
if (!hop) /* circuit hasn't started building yet. */
return;
if (server_mode(get_options())) {
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
if (!me)
return;
prev_digest = me->cache_info.identity_digest;
}
do {
- router = router_get_by_digest(hop->extend_info->identity_digest);
- if (router) {
+ const node_t *node = node_get_by_id(hop->extend_info->identity_digest);
+ if (node) { /* Why do we check this? We know the identity. -NM XXXX */
if (prev_digest) {
if (hop->state == CPATH_STATE_OPEN)
- rep_hist_note_extend_succeeded(prev_digest,
- router->cache_info.identity_digest);
+ rep_hist_note_extend_succeeded(prev_digest, node->identity);
else {
- rep_hist_note_extend_failed(prev_digest,
- router->cache_info.identity_digest);
+ rep_hist_note_extend_failed(prev_digest, node->identity);
break;
}
}
- prev_digest = router->cache_info.identity_digest;
+ prev_digest = node->identity;
} else {
prev_digest = NULL;
}
@@ -1924,7 +1944,7 @@ int
inform_testing_reachability(void)
{
char dirbuf[128];
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
if (!me)
return 0;
control_event_server_status(LOG_NOTICE,
@@ -1953,7 +1973,7 @@ inform_testing_reachability(void)
static INLINE int
should_use_create_fast_for_circuit(origin_circuit_t *circ)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
tor_assert(circ->cpath);
tor_assert(circ->cpath->extend_info);
@@ -1997,7 +2017,7 @@ int
circuit_send_next_onion_skin(origin_circuit_t *circ)
{
crypt_path_t *hop;
- routerinfo_t *router;
+ const node_t *node;
char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN];
char *onionskin;
size_t payload_len;
@@ -2013,7 +2033,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
else
control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0);
- router = router_get_by_digest(circ->_base.n_conn->identity_digest);
+ node = node_get_by_id(circ->_base.n_conn->identity_digest);
fast = should_use_create_fast_for_circuit(circ);
if (!fast) {
/* We are an OR and we know the right onion key: we should
@@ -2047,7 +2067,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
log_info(LD_CIRC,"First hop: finished sending %s cell to '%s'",
fast ? "CREATE_FAST" : "CREATE",
- router ? router_describe(router) : "<unnamed>");
+ node ? node_describe(node) : "<unnamed>");
} else {
tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
tor_assert(circ->_base.state == CIRCUIT_STATE_BUILDING);
@@ -2089,7 +2109,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
if (circ->build_state->onehop_tunnel)
control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_STATUS, 0);
if (!can_complete_circuit && !circ->build_state->onehop_tunnel) {
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
can_complete_circuit=1;
/* FFFF Log a count of known routers here */
log_notice(LD_GENERAL,
@@ -2097,6 +2117,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
"Looks like client functionality is working.");
control_event_bootstrap(BOOTSTRAP_STATUS_DONE, 0);
control_event_client_status(LOG_NOTICE, "CIRCUIT_ESTABLISHED");
+ clear_broken_connection_map(1);
if (server_mode(options) && !check_whether_orport_reachable()) {
inform_testing_reachability();
consider_testing_reachability(1, 1);
@@ -2524,12 +2545,12 @@ onionskin_answer(or_circuit_t *circ, uint8_t cell_type, const char *payload,
*/
static int
new_route_len(uint8_t purpose, extend_info_t *exit,
- smartlist_t *routers)
+ smartlist_t *nodes)
{
int num_acceptable_routers;
int routelen;
- tor_assert(routers);
+ tor_assert(nodes);
routelen = DEFAULT_ROUTE_LEN;
if (exit &&
@@ -2537,10 +2558,10 @@ new_route_len(uint8_t purpose, extend_info_t *exit,
purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)
routelen++;
- num_acceptable_routers = count_acceptable_routers(routers);
+ num_acceptable_routers = count_acceptable_nodes(nodes);
log_debug(LD_CIRC,"Chosen route length %d (%d/%d routers suitable).",
- routelen, num_acceptable_routers, smartlist_len(routers));
+ routelen, num_acceptable_routers, smartlist_len(nodes));
if (num_acceptable_routers < 2) {
log_info(LD_CIRC,
@@ -2558,24 +2579,12 @@ new_route_len(uint8_t purpose, extend_info_t *exit,
return routelen;
}
-/** Fetch the list of predicted ports, dup it into a smartlist of
- * uint16_t's, remove the ones that are already handled by an
- * existing circuit, and return it.
- */
+/** Return a newly allocated list of uint16_t * for each predicted port not
+ * handled by a current circuit. */
static smartlist_t *
circuit_get_unhandled_ports(time_t now)
{
- smartlist_t *source = rep_hist_get_predicted_ports(now);
- smartlist_t *dest = smartlist_create();
- uint16_t *tmp;
- int i;
-
- for (i = 0; i < smartlist_len(source); ++i) {
- tmp = tor_malloc(sizeof(uint16_t));
- memcpy(tmp, smartlist_get(source, i), sizeof(uint16_t));
- smartlist_add(dest, tmp);
- }
-
+ smartlist_t *dest = rep_hist_get_predicted_ports(now);
circuit_remove_handled_ports(dest);
return dest;
}
@@ -2609,12 +2618,12 @@ circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
return enough;
}
-/** Return 1 if <b>router</b> can handle one or more of the ports in
+/** Return 1 if <b>node</b> can handle one or more of the ports in
* <b>needed_ports</b>, else return 0.
*/
static int
-router_handles_some_port(routerinfo_t *router, smartlist_t *needed_ports)
-{
+node_handles_some_port(const node_t *node, smartlist_t *needed_ports)
+{ /* XXXX MOVE */
int i;
uint16_t port;
@@ -2624,7 +2633,10 @@ router_handles_some_port(routerinfo_t *router, smartlist_t *needed_ports)
needed_ports is explicitly a smartlist of uint16_t's */
port = *(uint16_t *)smartlist_get(needed_ports, i);
tor_assert(port);
- r = compare_addr_to_addr_policy(0, port, router->exit_policy);
+ if (node)
+ r = compare_tor_addr_to_node_policy(NULL, port, node);
+ else
+ continue;
if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED)
return 1;
}
@@ -2636,14 +2648,18 @@ router_handles_some_port(routerinfo_t *router, smartlist_t *needed_ports)
static int
ap_stream_wants_exit_attention(connection_t *conn)
{
- if (conn->type == CONN_TYPE_AP &&
- conn->state == AP_CONN_STATE_CIRCUIT_WAIT &&
+ entry_connection_t *entry;
+ if (conn->type != CONN_TYPE_AP)
+ return 0;
+ entry = TO_ENTRY_CONN(conn);
+
+ if (conn->state == AP_CONN_STATE_CIRCUIT_WAIT &&
!conn->marked_for_close &&
- !(TO_EDGE_CONN(conn)->want_onehop) && /* ignore one-hop streams */
- !(TO_EDGE_CONN(conn)->use_begindir) && /* ignore targeted dir fetches */
- !(TO_EDGE_CONN(conn)->chosen_exit_name) && /* ignore defined streams */
+ !(entry->want_onehop) && /* ignore one-hop streams */
+ !(entry->use_begindir) && /* ignore targeted dir fetches */
+ !(entry->chosen_exit_name) && /* ignore defined streams */
!connection_edge_is_rendezvous_stream(TO_EDGE_CONN(conn)) &&
- !circuit_stream_is_being_handled(TO_EDGE_CONN(conn), 0,
+ !circuit_stream_is_being_handled(TO_ENTRY_CONN(conn), 0,
MIN_CIRCUITS_HANDLING_STREAM))
return 1;
return 0;
@@ -2657,18 +2673,17 @@ ap_stream_wants_exit_attention(connection_t *conn)
*
* Return NULL if we can't find any suitable routers.
*/
-static routerinfo_t *
-choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
- int need_capacity)
+static const node_t *
+choose_good_exit_server_general(int need_uptime, int need_capacity)
{
int *n_supported;
- int i;
int n_pending_connections = 0;
smartlist_t *connections;
int best_support = -1;
int n_best_support=0;
- routerinfo_t *router;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
+ const smartlist_t *the_nodes;
+ const node_t *node=NULL;
connections = get_connection_array();
@@ -2689,10 +2704,11 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
*
* -1 means "Don't use this router at all."
*/
- n_supported = tor_malloc(sizeof(int)*smartlist_len(dir->routers));
- for (i = 0; i < smartlist_len(dir->routers); ++i) {/* iterate over routers */
- router = smartlist_get(dir->routers, i);
- if (router_is_me(router)) {
+ the_nodes = nodelist_get_list();
+ n_supported = tor_malloc(sizeof(int)*smartlist_len(the_nodes));
+ SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) {
+ const int i = node_sl_idx;
+ if (router_digest_is_me(node->identity)) {
n_supported[i] = -1;
// log_fn(LOG_DEBUG,"Skipping node %s -- it's me.", router->nickname);
/* XXX there's probably a reverse predecessor attack here, but
@@ -2700,41 +2716,44 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
*/
continue;
}
- if (!router->is_running || router->is_bad_exit) {
+ if (!node_has_descriptor(node)) {
+ n_supported[i] = -1;
+ continue;
+ }
+ if (!node->is_running || node->is_bad_exit) {
n_supported[i] = -1;
continue; /* skip routers that are known to be down or bad exits */
}
-
- if (options->_ExcludeExitNodesUnion &&
- routerset_contains_router(options->_ExcludeExitNodesUnion, router)) {
+ if (routerset_contains_node(options->_ExcludeExitNodesUnion, node)) {
n_supported[i] = -1;
continue; /* user asked us not to use it, no matter what */
}
if (options->ExitNodes &&
- !routerset_contains_router(options->ExitNodes, router)) {
+ !routerset_contains_node(options->ExitNodes, node)) {
n_supported[i] = -1;
continue; /* not one of our chosen exit nodes */
}
- if (router_is_unreliable(router, need_uptime, need_capacity, 0)) {
+ if (node_is_unreliable(node, need_uptime, need_capacity, 0)) {
n_supported[i] = -1;
continue; /* skip routers that are not suitable. Don't worry if
* this makes us reject all the possible routers: if so,
* we'll retry later in this function with need_update and
* need_capacity set to 0. */
}
- if (!(router->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) {
+ if (!(node->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) {
/* if it's invalid and we don't want it */
n_supported[i] = -1;
// log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- invalid router.",
// router->nickname, i);
continue; /* skip invalid routers */
}
- if (options->ExcludeSingleHopRelays && router->allow_single_hop_exits) {
+ if (options->ExcludeSingleHopRelays &&
+ node_allows_single_hop_exits(node)) {
n_supported[i] = -1;
continue;
}
- if (router_exit_policy_rejects_all(router)) {
+ if (node_exit_policy_rejects_all(node)) {
n_supported[i] = -1;
// log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.",
// router->nickname, i);
@@ -2742,11 +2761,10 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
}
n_supported[i] = 0;
/* iterate over connections */
- SMARTLIST_FOREACH(connections, connection_t *, conn,
- {
+ SMARTLIST_FOREACH_BEGIN(connections, connection_t *, conn) {
if (!ap_stream_wants_exit_attention(conn))
continue; /* Skip everything but APs in CIRCUIT_WAIT */
- if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), router)) {
+ if (connection_ap_can_use_exit(TO_ENTRY_CONN(conn), node)) {
++n_supported[i];
// log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.",
// router->nickname, i, n_supported[i]);
@@ -2754,7 +2772,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
// log_fn(LOG_DEBUG,"%s (index %d) would reject this stream.",
// router->nickname, i);
}
- }); /* End looping over connections. */
+ } SMARTLIST_FOREACH_END(conn);
if (n_pending_connections > 0 && n_supported[i] == 0) {
/* Leave best_support at -1 if that's where it is, so we can
* distinguish it later. */
@@ -2771,7 +2789,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
* count of equally good routers.*/
++n_best_support;
}
- }
+ } SMARTLIST_FOREACH_END(node);
log_info(LD_CIRC,
"Found %d servers that might support %d/%d pending connections.",
n_best_support, best_support >= 0 ? best_support : 0,
@@ -2782,11 +2800,12 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
if (best_support > 0) {
smartlist_t *supporting = smartlist_create();
- for (i = 0; i < smartlist_len(dir->routers); i++)
- if (n_supported[i] == best_support)
- smartlist_add(supporting, smartlist_get(dir->routers, i));
+ SMARTLIST_FOREACH(the_nodes, const node_t *, node, {
+ if (n_supported[node_sl_idx] == best_support)
+ smartlist_add(supporting, (void*)node);
+ });
- router = routerlist_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
+ node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
smartlist_free(supporting);
} else {
/* Either there are no pending connections, or no routers even seem to
@@ -2804,7 +2823,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
need_capacity?", fast":"",
need_uptime?", stable":"");
tor_free(n_supported);
- return choose_good_exit_server_general(dir, 0, 0);
+ return choose_good_exit_server_general(0, 0);
}
log_notice(LD_CIRC, "All routers are down or won't exit%s -- "
"choosing a doomed exit at random.",
@@ -2815,18 +2834,17 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
for (attempt = 0; attempt < 2; attempt++) {
/* try once to pick only from routers that satisfy a needed port,
* then if there are none, pick from any that support exiting. */
- for (i = 0; i < smartlist_len(dir->routers); i++) {
- router = smartlist_get(dir->routers, i);
- if (n_supported[i] != -1 &&
- (attempt || router_handles_some_port(router, needed_ports))) {
+ SMARTLIST_FOREACH_BEGIN(the_nodes, const node_t *, node) {
+ if (n_supported[node_sl_idx] != -1 &&
+ (attempt || node_handles_some_port(node, needed_ports))) {
// log_fn(LOG_DEBUG,"Try %d: '%s' is a possibility.",
// try, router->nickname);
- smartlist_add(supporting, router);
+ smartlist_add(supporting, (void*)node);
}
- }
+ } SMARTLIST_FOREACH_END(node);
- router = routerlist_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
- if (router)
+ node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
+ if (node)
break;
smartlist_clear(supporting);
}
@@ -2836,9 +2854,9 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
}
tor_free(n_supported);
- if (router) {
- log_info(LD_CIRC, "Chose exit server '%s'", router_describe(router));
- return router;
+ if (node) {
+ log_info(LD_CIRC, "Chose exit server '%s'", node_describe(node));
+ return node;
}
if (options->ExitNodes) {
log_warn(LD_CIRC,
@@ -2859,12 +2877,12 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime,
* For client-side rendezvous circuits, choose a random node, weighted
* toward the preferences in 'options'.
*/
-static routerinfo_t *
-choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
+static const node_t *
+choose_good_exit_server(uint8_t purpose,
int need_uptime, int need_capacity, int is_internal)
{
- or_options_t *options = get_options();
- router_crn_flags_t flags = 0;
+ const or_options_t *options = get_options();
+ router_crn_flags_t flags = CRN_NEED_DESC;
if (need_uptime)
flags |= CRN_NEED_UPTIME;
if (need_capacity)
@@ -2877,7 +2895,7 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
if (is_internal) /* pick it like a middle hop */
return router_choose_random_node(NULL, options->ExcludeNodes, flags);
else
- return choose_good_exit_server_general(dir,need_uptime,need_capacity);
+ return choose_good_exit_server_general(need_uptime,need_capacity);
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
if (options->_AllowInvalid & ALLOW_INVALID_RENDEZVOUS)
flags |= CRN_ALLOW_INVALID;
@@ -2893,7 +2911,7 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
static void
warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
routerset_t *rs = options->ExcludeNodes;
const char *description;
uint8_t purpose = circ->_base.purpose;
@@ -2970,13 +2988,12 @@ static int
onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
{
cpath_build_state_t *state = circ->build_state;
- routerlist_t *rl = router_get_routerlist();
if (state->onehop_tunnel) {
log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel.");
state->desired_path_len = 1;
} else {
- int r = new_route_len(circ->_base.purpose, exit, rl->routers);
+ int r = new_route_len(circ->_base.purpose, exit, nodelist_get_list());
if (r < 1) /* must be at least 1 */
return -1;
state->desired_path_len = r;
@@ -2988,14 +3005,15 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
extend_info_describe(exit));
exit = extend_info_dup(exit);
} else { /* we have to decide one */
- routerinfo_t *router =
- choose_good_exit_server(circ->_base.purpose, rl, state->need_uptime,
+ const node_t *node =
+ choose_good_exit_server(circ->_base.purpose, state->need_uptime,
state->need_capacity, state->is_internal);
- if (!router) {
+ if (!node) {
log_warn(LD_CIRC,"failed to choose an exit server");
return -1;
}
- exit = extend_info_from_router(router);
+ exit = extend_info_from_node(node);
+ tor_assert(exit);
}
state->chosen_exit = exit;
return 0;
@@ -3046,35 +3064,30 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit)
* and available for building circuits through.
*/
static int
-count_acceptable_routers(smartlist_t *routers)
+count_acceptable_nodes(smartlist_t *nodes)
{
- int i, n;
int num=0;
- routerinfo_t *r;
- n = smartlist_len(routers);
- for (i=0;i<n;i++) {
- r = smartlist_get(routers, i);
-// log_debug(LD_CIRC,
+ SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) {
+ // log_debug(LD_CIRC,
// "Contemplating whether router %d (%s) is a new option.",
// i, r->nickname);
- if (r->is_running == 0) {
+ if (! node->is_running)
// log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i);
- goto next_i_loop;
- }
- if (r->is_valid == 0) {
+ continue;
+ if (! node->is_valid)
// log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
- goto next_i_loop;
+ continue;
+ if (! node_has_descriptor(node))
+ continue;
/* XXX This clause makes us count incorrectly: if AllowInvalidRouters
* allows this node in some places, then we're getting an inaccurate
* count. For now, be conservative and don't count it. But later we
* should try to be smarter. */
- }
- num++;
+ ++num;
+ } SMARTLIST_FOREACH_END(node);
+
// log_debug(LD_CIRC,"I like %d. num_acceptable_routers now %d.",i, num);
- next_i_loop:
- ; /* C requires an explicit statement after the label */
- }
return num;
}
@@ -3102,31 +3115,29 @@ onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop)
* circuit. In particular, make sure we don't pick the exit node or its
* family, and make sure we don't duplicate any previous nodes or their
* families. */
-static routerinfo_t *
+static const node_t *
choose_good_middle_server(uint8_t purpose,
cpath_build_state_t *state,
crypt_path_t *head,
int cur_len)
{
int i;
- routerinfo_t *r, *choice;
+ const node_t *r, *choice;
crypt_path_t *cpath;
smartlist_t *excluded;
- or_options_t *options = get_options();
- router_crn_flags_t flags = 0;
+ const or_options_t *options = get_options();
+ router_crn_flags_t flags = CRN_NEED_DESC;
tor_assert(_CIRCUIT_PURPOSE_MIN <= purpose &&
purpose <= _CIRCUIT_PURPOSE_MAX);
log_debug(LD_CIRC, "Contemplating intermediate hop: random choice.");
excluded = smartlist_create();
- if ((r = build_state_get_exit_router(state))) {
- smartlist_add(excluded, r);
- routerlist_add_family(excluded, r);
+ if ((r = build_state_get_exit_node(state))) {
+ nodelist_add_node_and_family(excluded, r);
}
for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) {
- if ((r = router_get_by_digest(cpath->extend_info->identity_digest))) {
- smartlist_add(excluded, r);
- routerlist_add_family(excluded, r);
+ if ((r = node_get_by_id(cpath->extend_info->identity_digest))) {
+ nodelist_add_node_and_family(excluded, r);
}
}
@@ -3149,44 +3160,43 @@ choose_good_middle_server(uint8_t purpose,
* If <b>state</b> is NULL, we're choosing a router to serve as an entry
* guard, not for any particular circuit.
*/
-static routerinfo_t *
+static const node_t *
choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
{
- routerinfo_t *r, *choice;
+ const node_t *choice;
smartlist_t *excluded;
- or_options_t *options = get_options();
- router_crn_flags_t flags = CRN_NEED_GUARD;
+ const or_options_t *options = get_options();
+ router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC;
+ const node_t *node;
if (state && options->UseEntryGuards &&
(purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) {
+ /* This is request for an entry server to use for a regular circuit,
+ * and we use entry guard nodes. Just return one of the guard nodes. */
return choose_random_entry(state);
}
excluded = smartlist_create();
- if (state && (r = build_state_get_exit_router(state))) {
- smartlist_add(excluded, r);
- routerlist_add_family(excluded, r);
+ if (state && (node = build_state_get_exit_node(state))) {
+ /* Exclude the exit node from the state, if we have one. Also exclude its
+ * family. */
+ nodelist_add_node_and_family(excluded, node);
}
if (firewall_is_fascist_or()) {
- /*XXXX This could slow things down a lot; use a smarter implementation */
- /* exclude all ORs that listen on the wrong port, if anybody notices. */
- routerlist_t *rl = router_get_routerlist();
- int i;
-
- for (i=0; i < smartlist_len(rl->routers); i++) {
- r = smartlist_get(rl->routers, i);
- if (!fascist_firewall_allows_or(r))
- smartlist_add(excluded, r);
- }
+ /* Exclude all ORs that we can't reach through our firewall */
+ smartlist_t *nodes = nodelist_get_list();
+ SMARTLIST_FOREACH(nodes, const node_t *, node, {
+ if (!fascist_firewall_allows_node(node))
+ smartlist_add(excluded, (void*)node);
+ });
}
- /* and exclude current entry guards, if applicable */
+ /* and exclude current entry guards and their families, if applicable */
if (options->UseEntryGuards && entry_guards) {
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
{
- if ((r = router_get_by_digest(entry->identity))) {
- smartlist_add(excluded, r);
- routerlist_add_family(excluded, r);
+ if ((node = node_get_by_id(entry->identity))) {
+ nodelist_add_node_and_family(excluded, node);
}
});
}
@@ -3242,14 +3252,18 @@ onion_extend_cpath(origin_circuit_t *circ)
if (cur_len == state->desired_path_len - 1) { /* Picking last node */
info = extend_info_dup(state->chosen_exit);
} else if (cur_len == 0) { /* picking first node */
- routerinfo_t *r = choose_good_entry_server(purpose, state);
- if (r)
- info = extend_info_from_router(r);
+ const node_t *r = choose_good_entry_server(purpose, state);
+ if (r) {
+ info = extend_info_from_node(r);
+ tor_assert(info);
+ }
} else {
- routerinfo_t *r =
+ const node_t *r =
choose_good_middle_server(purpose, state, circ->cpath, cur_len);
- if (r)
- info = extend_info_from_router(r);
+ if (r) {
+ info = extend_info_from_node(r);
+ tor_assert(info);
+ }
}
if (!info) {
@@ -3309,7 +3323,7 @@ extend_info_alloc(const char *nickname, const char *digest,
/** Allocate and return a new extend_info_t that can be used to build a
* circuit to or through the router <b>r</b>. */
extend_info_t *
-extend_info_from_router(routerinfo_t *r)
+extend_info_from_router(const routerinfo_t *r)
{
tor_addr_t addr;
tor_assert(r);
@@ -3318,6 +3332,29 @@ extend_info_from_router(routerinfo_t *r)
r->onion_pkey, &addr, r->or_port);
}
+/** Allocate and return a new extend_info that can be used to build a ircuit
+ * to or through the node <b>node</b>. May return NULL if there is not
+ * enough info about <b>node</b> to extend to it--for example, if there
+ * is no routerinfo_t or microdesc_t.
+ **/
+extend_info_t *
+extend_info_from_node(const node_t *node)
+{
+ if (node->ri) {
+ return extend_info_from_router(node->ri);
+ } else if (node->rs && node->md) {
+ tor_addr_t addr;
+ tor_addr_from_ipv4h(&addr, node->rs->addr);
+ return extend_info_alloc(node->rs->nickname,
+ node->identity,
+ node->md->onion_pkey,
+ &addr,
+ node->rs->or_port);
+ } else {
+ return NULL;
+ }
+}
+
/** Release storage held by an extend_info_t struct. */
void
extend_info_free(extend_info_t *info)
@@ -3348,12 +3385,12 @@ extend_info_dup(extend_info_t *info)
* If there is no chosen exit, or if we don't know the routerinfo_t for
* the chosen exit, return NULL.
*/
-routerinfo_t *
-build_state_get_exit_router(cpath_build_state_t *state)
+const node_t *
+build_state_get_exit_node(cpath_build_state_t *state)
{
if (!state || !state->chosen_exit)
return NULL;
- return router_get_by_digest(state->chosen_exit->identity_digest);
+ return node_get_by_id(state->chosen_exit->identity_digest);
}
/** Return the nickname for the chosen exit router in <b>state</b>. If
@@ -3375,10 +3412,10 @@ build_state_get_exit_nickname(cpath_build_state_t *state)
*
* If it's not usable, set *<b>reason</b> to a static string explaining why.
*/
-/*XXXX take a routerstatus, not a routerinfo. */
static int
-entry_guard_set_status(entry_guard_t *e, routerinfo_t *ri,
- time_t now, or_options_t *options, const char **reason)
+entry_guard_set_status(entry_guard_t *e, const node_t *node,
+ time_t now, const or_options_t *options,
+ const char **reason)
{
char buf[HEX_DIGEST_LEN+1];
int changed = 0;
@@ -3386,18 +3423,19 @@ entry_guard_set_status(entry_guard_t *e, routerinfo_t *ri,
*reason = NULL;
/* Do we want to mark this guard as bad? */
- if (!ri)
+ if (!node)
*reason = "unlisted";
- else if (!ri->is_running)
+ else if (!node->is_running)
*reason = "down";
- else if (options->UseBridges && ri->purpose != ROUTER_PURPOSE_BRIDGE)
+ else if (options->UseBridges && (!node->ri ||
+ node->ri->purpose != ROUTER_PURPOSE_BRIDGE))
*reason = "not a bridge";
- else if (options->UseBridges && !routerinfo_is_a_configured_bridge(ri))
+ else if (options->UseBridges && !node_is_a_configured_bridge(node))
*reason = "not a configured bridge";
- else if (!options->UseBridges && !ri->is_possible_guard &&
- !routerset_contains_router(options->EntryNodes,ri))
+ else if (!options->UseBridges && !node->is_possible_guard &&
+ !routerset_contains_node(options->EntryNodes,node))
*reason = "not recommended as a guard";
- else if (routerset_contains_router(options->ExcludeNodes, ri))
+ else if (routerset_contains_node(options->ExcludeNodes, node))
*reason = "excluded";
if (*reason && ! e->bad_since) {
@@ -3441,7 +3479,7 @@ entry_is_time_to_retry(entry_guard_t *e, time_t now)
return now > (e->last_attempted + 36*60*60);
}
-/** Return the router corresponding to <b>e</b>, if <b>e</b> is
+/** Return the node corresponding to <b>e</b>, if <b>e</b> is
* working well enough that we are willing to use it as an entry
* right now. (Else return NULL.) In particular, it must be
* - Listed as either up or never yet contacted;
@@ -3455,12 +3493,12 @@ entry_is_time_to_retry(entry_guard_t *e, time_t now)
*
* If the answer is no, set *<b>msg</b> to an explanation of why.
*/
-static INLINE routerinfo_t *
+static INLINE const node_t *
entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
int assume_reachable, const char **msg)
{
- routerinfo_t *r;
- or_options_t *options = get_options();
+ const node_t *node;
+ const or_options_t *options = get_options();
tor_assert(msg);
if (e->bad_since) {
@@ -3473,38 +3511,39 @@ entry_is_live(entry_guard_t *e, int need_uptime, int need_capacity,
*msg = "unreachable";
return NULL;
}
- r = router_get_by_digest(e->identity);
- if (!r) {
+ node = node_get_by_id(e->identity);
+ if (!node || !node_has_descriptor(node)) {
*msg = "no descriptor";
return NULL;
}
- if (options->UseBridges) {
- if (r->purpose != ROUTER_PURPOSE_BRIDGE) {
+ if (get_options()->UseBridges) {
+ if (node_get_purpose(node) != ROUTER_PURPOSE_BRIDGE) {
*msg = "not a bridge";
return NULL;
}
- if (!routerinfo_is_a_configured_bridge(r)) {
+ if (!node_is_a_configured_bridge(node)) {
*msg = "not a configured bridge";
return NULL;
}
- } else if (r->purpose != ROUTER_PURPOSE_GENERAL) {
- *msg = "not general-purpose";
- return NULL;
+ } else { /* !get_options()->UseBridges */
+ if (node_get_purpose(node) != ROUTER_PURPOSE_GENERAL) {
+ *msg = "not general-purpose";
+ return NULL;
+ }
}
- if (options->EntryNodes &&
- routerset_contains_router(options->EntryNodes, r)) {
+ if (routerset_contains_node(options->EntryNodes, node)) {
/* they asked for it, they get it */
need_uptime = need_capacity = 0;
}
- if (router_is_unreliable(r, need_uptime, need_capacity, 0)) {
+ if (node_is_unreliable(node, need_uptime, need_capacity, 0)) {
*msg = "not fast/stable";
return NULL;
}
- if (!fascist_firewall_allows_or(r)) {
+ if (!fascist_firewall_allows_node(node)) {
*msg = "unreachable by config";
return NULL;
}
- return r;
+ return node;
}
/** Return the number of entry guards that we think are usable. */
@@ -3584,7 +3623,7 @@ control_event_guard_deferred(void)
#if 0
int n = 0;
const char *msg;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (!entry_guards)
return;
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry,
@@ -3606,15 +3645,15 @@ control_event_guard_deferred(void)
* If <b>chosen</b> is defined, use that one, and if it's not
* already in our entry_guards list, put it at the *beginning*.
* Else, put the one we pick at the end of the list. */
-static routerinfo_t *
-add_an_entry_guard(routerinfo_t *chosen, int reset_status)
+static const node_t *
+add_an_entry_guard(const node_t *chosen, int reset_status, int prepend)
{
- routerinfo_t *router;
+ const node_t *node;
entry_guard_t *entry;
if (chosen) {
- router = chosen;
- entry = is_an_entry_guard(router->cache_info.identity_digest);
+ node = chosen;
+ entry = is_an_entry_guard(node->identity);
if (entry) {
if (reset_status) {
entry->bad_since = 0;
@@ -3623,15 +3662,15 @@ add_an_entry_guard(routerinfo_t *chosen, int reset_status)
return NULL;
}
} else {
- router = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
- if (!router)
+ node = choose_good_entry_server(CIRCUIT_PURPOSE_C_GENERAL, NULL);
+ if (!node)
return NULL;
}
entry = tor_malloc_zero(sizeof(entry_guard_t));
- log_info(LD_CIRC, "Chose '%s' as new entry guard.",
- router_describe(router));
- strlcpy(entry->nickname, router->nickname, sizeof(entry->nickname));
- memcpy(entry->identity, router->cache_info.identity_digest, DIGEST_LEN);
+ log_info(LD_CIRC, "Chose %s as new entry guard.",
+ node_describe(node));
+ strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname));
+ memcpy(entry->identity, node->identity, DIGEST_LEN);
/* Choose expiry time smudged over the past month. The goal here
* is to a) spread out when Tor clients rotate their guards, so they
* don't all select them on the same day, and b) avoid leaving a
@@ -3639,27 +3678,27 @@ add_an_entry_guard(routerinfo_t *chosen, int reset_status)
* this guard. For details, see the Jan 2010 or-dev thread. */
entry->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30);
entry->chosen_by_version = tor_strdup(VERSION);
- if (chosen) /* prepend */
+ if (prepend)
smartlist_insert(entry_guards, 0, entry);
- else /* append */
+ else
smartlist_add(entry_guards, entry);
control_event_guard(entry->nickname, entry->identity, "NEW");
control_event_guard_deferred();
log_entry_guards(LOG_INFO);
- return router;
+ return node;
}
/** If the use of entry guards is configured, choose more entry guards
* until we have enough in the list. */
static void
-pick_entry_guards(or_options_t *options)
+pick_entry_guards(const or_options_t *options)
{
int changed = 0;
tor_assert(entry_guards);
while (num_live_entry_guards() < options->NumEntryGuards) {
- if (!add_an_entry_guard(NULL, 0))
+ if (!add_an_entry_guard(NULL, 0, 0))
break;
changed = 1;
}
@@ -3785,7 +3824,7 @@ remove_dead_entry_guards(time_t now)
* think that things are unlisted.
*/
void
-entry_guards_compute_status(or_options_t *options, time_t now)
+entry_guards_compute_status(const or_options_t *options, time_t now)
{
int changed = 0;
digestmap_t *reasons;
@@ -3799,7 +3838,7 @@ entry_guards_compute_status(or_options_t *options, time_t now)
reasons = digestmap_new();
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry)
{
- routerinfo_t *r = router_get_by_digest(entry->identity);
+ const node_t *r = node_get_by_id(entry->identity);
const char *reason = NULL;
if (entry_guard_set_status(entry, r, now, options, &reason))
changed = 1;
@@ -3818,7 +3857,7 @@ entry_guards_compute_status(or_options_t *options, time_t now)
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
const char *reason = digestmap_get(reasons, entry->identity);
const char *live_msg = "";
- routerinfo_t *r = entry_is_live(entry, 0, 1, 0, &live_msg);
+ const node_t *r = entry_is_live(entry, 0, 1, 0, &live_msg);
log_info(LD_CIRC, "Summary: Entry %s [%s] is %s, %s%s%s, and %s%s.",
entry->nickname,
hex_str(entry->identity, DIGEST_LEN),
@@ -3936,7 +3975,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded,
break;
if (e->made_contact) {
const char *msg;
- routerinfo_t *r = entry_is_live(e, 0, 1, 1, &msg);
+ const node_t *r = entry_is_live(e, 0, 1, 1, &msg);
if (r && e->unreachable_since) {
refuse_conn = 1;
e->can_retry = 1;
@@ -3972,12 +4011,12 @@ entry_nodes_should_be_added(void)
should_add_entry_nodes = 1;
}
-/** Add all nodes in EntryNodes that aren't currently guard nodes to the list
- * of guard nodes, at the front. */
+/** Adjust the entry guards list so that it only contains entries from
+ * EntryNodes, adding new entries from EntryNodes to the list as needed. */
static void
-entry_guards_prepend_from_config(or_options_t *options)
+entry_guards_set_from_config(const or_options_t *options)
{
- smartlist_t *entry_routers, *entry_fps;
+ smartlist_t *entry_nodes, *worse_entry_nodes, *entry_fps;
smartlist_t *old_entry_guards_on_list, *old_entry_guards_not_on_list;
tor_assert(entry_guards);
@@ -3997,23 +4036,19 @@ entry_guards_prepend_from_config(or_options_t *options)
tor_free(string);
}
- entry_routers = smartlist_create();
+ entry_nodes = smartlist_create();
+ worse_entry_nodes = smartlist_create();
entry_fps = smartlist_create();
old_entry_guards_on_list = smartlist_create();
old_entry_guards_not_on_list = smartlist_create();
/* Split entry guards into those on the list and those not. */
- /* XXXX023 Now that we allow countries and IP ranges in EntryNodes, this is
- * potentially an enormous list. For now, we disable such values for
- * EntryNodes in options_validate(); really, this wants a better solution.
- * Perhaps we should do this calculation once whenever the list of routers
- * changes or the entrynodes setting changes.
- */
- routerset_get_all_routers(entry_routers, options->EntryNodes,
- options->ExcludeNodes, 0);
- SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri,
- smartlist_add(entry_fps,ri->cache_info.identity_digest));
+ routerset_get_all_nodes(entry_nodes, options->EntryNodes,
+ options->ExcludeNodes, 0);
+ SMARTLIST_FOREACH(entry_nodes, const node_t *,node,
+ smartlist_add(entry_fps, (void*)node->identity));
+
SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, {
if (smartlist_digest_isin(entry_fps, e->identity))
smartlist_add(old_entry_guards_on_list, e);
@@ -4021,27 +4056,47 @@ entry_guards_prepend_from_config(or_options_t *options)
smartlist_add(old_entry_guards_not_on_list, e);
});
- /* Remove all currently configured entry guards from entry_routers. */
- SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, {
- if (is_an_entry_guard(ri->cache_info.identity_digest)) {
- SMARTLIST_DEL_CURRENT(entry_routers, ri);
+ /* Remove all currently configured guard nodes, excluded nodes, unreachable
+ * nodes, or non-Guard nodes from entry_nodes. */
+ SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) {
+ if (is_an_entry_guard(node->identity)) {
+ SMARTLIST_DEL_CURRENT(entry_nodes, node);
+ continue;
+ } else if (routerset_contains_node(options->ExcludeNodes, node)) {
+ SMARTLIST_DEL_CURRENT(entry_nodes, node);
+ continue;
+ } else if (!fascist_firewall_allows_node(node)) {
+ SMARTLIST_DEL_CURRENT(entry_nodes, node);
+ continue;
+ } else if (! node->is_possible_guard) {
+ smartlist_add(worse_entry_nodes, (node_t*)node);
+ SMARTLIST_DEL_CURRENT(entry_nodes, node);
}
- });
+ } SMARTLIST_FOREACH_END(node);
/* Now build the new entry_guards list. */
smartlist_clear(entry_guards);
/* First, the previously configured guards that are in EntryNodes. */
smartlist_add_all(entry_guards, old_entry_guards_on_list);
+ /* Next, scramble the rest of EntryNodes, putting the guards first. */
+ smartlist_shuffle(entry_nodes);
+ smartlist_shuffle(worse_entry_nodes);
+ smartlist_add_all(entry_nodes, worse_entry_nodes);
+
/* Next, the rest of EntryNodes */
- SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, {
- add_an_entry_guard(ri, 0);
- });
+ SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) {
+ add_an_entry_guard(node, 0, 0);
+ if (smartlist_len(entry_guards) > options->NumEntryGuards * 10)
+ break;
+ } SMARTLIST_FOREACH_END(node);
+ log_notice(LD_GENERAL, "%d entries in guards", smartlist_len(entry_guards));
/* Finally, free the remaining previously configured guards that are not in
* EntryNodes. */
SMARTLIST_FOREACH(old_entry_guards_not_on_list, entry_guard_t *, e,
entry_guard_free(e));
- smartlist_free(entry_routers);
+ smartlist_free(entry_nodes);
+ smartlist_free(worse_entry_nodes);
smartlist_free(entry_fps);
smartlist_free(old_entry_guards_on_list);
smartlist_free(old_entry_guards_not_on_list);
@@ -4053,7 +4108,7 @@ entry_guards_prepend_from_config(or_options_t *options)
* list already and we must stick to it.
*/
int
-entry_list_is_constrained(or_options_t *options)
+entry_list_is_constrained(const or_options_t *options)
{
if (options->EntryNodes)
return 1;
@@ -4067,20 +4122,21 @@ entry_list_is_constrained(or_options_t *options)
* make sure not to pick this circuit's exit or any node in the
* exit's family. If <b>state</b> is NULL, we're looking for a random
* guard (likely a bridge). */
-routerinfo_t *
+const node_t *
choose_random_entry(cpath_build_state_t *state)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
smartlist_t *live_entry_guards = smartlist_create();
smartlist_t *exit_family = smartlist_create();
- routerinfo_t *chosen_exit = state?build_state_get_exit_router(state) : NULL;
- routerinfo_t *r = NULL;
+ const node_t *chosen_exit =
+ state?build_state_get_exit_node(state) : NULL;
+ const node_t *node = NULL;
int need_uptime = state ? state->need_uptime : 0;
int need_capacity = state ? state->need_capacity : 0;
int preferred_min, consider_exit_family = 0;
if (chosen_exit) {
- routerlist_add_family(exit_family, chosen_exit);
+ nodelist_add_node_and_family(exit_family, chosen_exit);
consider_exit_family = 1;
}
@@ -4088,7 +4144,7 @@ choose_random_entry(cpath_build_state_t *state)
entry_guards = smartlist_create();
if (should_add_entry_nodes)
- entry_guards_prepend_from_config(options);
+ entry_guards_set_from_config(options);
if (!entry_list_is_constrained(options) &&
smartlist_len(entry_guards) < options->NumEntryGuards)
@@ -4096,25 +4152,24 @@ choose_random_entry(cpath_build_state_t *state)
retry:
smartlist_clear(live_entry_guards);
- SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry)
- {
+ SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) {
const char *msg;
- r = entry_is_live(entry, need_uptime, need_capacity, 0, &msg);
- if (!r)
+ node = entry_is_live(entry, need_uptime, need_capacity, 0, &msg);
+ if (!node)
continue; /* down, no point */
- if (r == chosen_exit)
+ if (node == chosen_exit)
continue; /* don't pick the same node for entry and exit */
- if (consider_exit_family && smartlist_isin(exit_family, r))
+ if (consider_exit_family && smartlist_isin(exit_family, node))
continue; /* avoid relays that are family members of our exit */
#if 0 /* since EntryNodes is always strict now, this clause is moot */
if (options->EntryNodes &&
- !routerset_contains_router(options->EntryNodes, r)) {
+ !routerset_contains_node(options->EntryNodes, node)) {
/* We've come to the end of our preferred entry nodes. */
if (smartlist_len(live_entry_guards))
goto choose_and_finish; /* only choose from the ones we like */
if (options->StrictNodes) {
/* in theory this case should never happen, since
- * entry_guards_prepend_from_config() drops unwanted relays */
+ * entry_guards_set_from_config() drops unwanted relays */
tor_fragile_assert();
} else {
log_info(LD_CIRC,
@@ -4122,7 +4177,7 @@ choose_random_entry(cpath_build_state_t *state)
}
}
#endif
- smartlist_add(live_entry_guards, r);
+ smartlist_add(live_entry_guards, (void*)node);
if (!entry->made_contact) {
/* Always start with the first not-yet-contacted entry
* guard. Otherwise we might add several new ones, pick
@@ -4131,9 +4186,8 @@ choose_random_entry(cpath_build_state_t *state)
goto choose_and_finish;
}
if (smartlist_len(live_entry_guards) >= options->NumEntryGuards)
- break; /* we have enough */
- }
- SMARTLIST_FOREACH_END(entry);
+ goto choose_and_finish; /* we have enough */
+ } SMARTLIST_FOREACH_END(entry);
if (entry_list_is_constrained(options)) {
/* If we prefer the entry nodes we've got, and we have at least
@@ -4153,8 +4207,8 @@ choose_random_entry(cpath_build_state_t *state)
/* XXX if guard doesn't imply fast and stable, then we need
* to tell add_an_entry_guard below what we want, or it might
* be a long time til we get it. -RD */
- r = add_an_entry_guard(NULL, 0);
- if (r) {
+ node = add_an_entry_guard(NULL, 0, 0);
+ if (node) {
entry_guards_changed();
/* XXX we start over here in case the new node we added shares
* a family with our exit node. There's a chance that we'll just
@@ -4164,11 +4218,11 @@ choose_random_entry(cpath_build_state_t *state)
goto retry;
}
}
- if (!r && need_uptime) {
+ if (!node && need_uptime) {
need_uptime = 0; /* try without that requirement */
goto retry;
}
- if (!r && need_capacity) {
+ if (!node && need_capacity) {
/* still no? last attempt, try without requiring capacity */
need_capacity = 0;
goto retry;
@@ -4177,10 +4231,10 @@ choose_random_entry(cpath_build_state_t *state)
/* Removing this retry logic: if we only allow one exit, and it is in the
same family as all our entries, then we are just plain not going to win
here. */
- if (!r && entry_list_is_constrained(options) && consider_exit_family) {
- /* still no? if we're using bridges,
- * and our chosen exit is in the same family as all our
- * bridges, then be flexible about families. */
+ if (!node && entry_list_is_constrained(options) && consider_exit_family) {
+ /* still no? if we're using bridges or have strictentrynodes
+ * set, and our chosen exit is in the same family as all our
+ * bridges/entry guards, then be flexible about families. */
consider_exit_family = 0;
goto retry;
}
@@ -4192,16 +4246,16 @@ choose_random_entry(cpath_build_state_t *state)
if (entry_list_is_constrained(options)) {
/* We need to weight by bandwidth, because our bridges or entryguards
* were not already selected proportional to their bandwidth. */
- r = routerlist_sl_choose_by_bandwidth(live_entry_guards, WEIGHT_FOR_GUARD);
+ node = node_sl_choose_by_bandwidth(live_entry_guards, WEIGHT_FOR_GUARD);
} else {
/* We choose uniformly at random here, because choose_good_entry_server()
* already weights its choices by bandwidth, so we don't want to
* *double*-weight our guard selection. */
- r = smartlist_choose(live_entry_guards);
+ node = smartlist_choose(live_entry_guards);
}
smartlist_free(live_entry_guards);
smartlist_free(exit_family);
- return r;
+ return node;
}
/** Parse <b>state</b> and learn about the entry guards it describes.
@@ -4448,7 +4502,7 @@ getinfo_helper_entry_guards(control_connection_t *conn,
char *c = tor_malloc(len);
const char *status = NULL;
time_t when = 0;
- routerinfo_t *ri;
+ const node_t *node;
if (!e->made_contact) {
status = "never-connected";
@@ -4459,9 +4513,9 @@ getinfo_helper_entry_guards(control_connection_t *conn,
status = "up";
}
- ri = router_get_by_digest(e->identity);
- if (ri) {
- router_get_verbose_nickname(nbuf, ri);
+ node = node_get_by_id(e->identity);
+ if (node) {
+ node_get_verbose_nickname(node, nbuf);
} else {
nbuf[0] = '$';
base16_encode(nbuf+1, sizeof(nbuf)-1, e->identity, DIGEST_LEN);
@@ -4484,24 +4538,6 @@ getinfo_helper_entry_guards(control_connection_t *conn,
return 0;
}
-/** Information about a configured bridge. Currently this just matches the
- * ones in the torrc file, but one day we may be able to learn about new
- * bridges on our own, and remember them in the state file. */
-typedef struct {
- /** Address of the bridge. */
- tor_addr_t addr;
- /** TLS port for the bridge. */
- uint16_t port;
- /** Boolean: We are re-parsing our bridge list, and we are going to remove
- * this one if we don't find it in the list of configured bridges. */
- unsigned marked_for_removal : 1;
- /** Expected identity digest, or all zero bytes if we don't know what the
- * digest should be. */
- char identity[DIGEST_LEN];
- /** When should we next try to fetch a descriptor for this bridge? */
- download_status_t fetch_status;
-} bridge_info_t;
-
/** A list of configured bridges. Whenever we actually get a descriptor
* for one, we add it as an entry guard. Note that the order of bridges
* in this list does not necessarily correspond to the order of bridges
@@ -4529,7 +4565,7 @@ sweep_bridge_list(void)
SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) {
if (b->marked_for_removal) {
SMARTLIST_DEL_CURRENT(bridge_list, b);
- tor_free(b);
+ bridge_free(b);
}
} SMARTLIST_FOREACH_END(b);
}
@@ -4540,10 +4576,243 @@ clear_bridge_list(void)
{
if (!bridge_list)
bridge_list = smartlist_create();
- SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, tor_free(b));
+ SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, bridge_free(b));
smartlist_clear(bridge_list);
}
+/** Free the bridge <b>bridge</b>. */
+static void
+bridge_free(bridge_info_t *bridge)
+{
+ if (!bridge)
+ return;
+
+ tor_free(bridge->transport_name);
+ tor_free(bridge);
+}
+
+/** A list of pluggable transports found in torrc. */
+static smartlist_t *transport_list = NULL;
+
+/** Mark every entry of the transport list to be removed on our next call to
+ * sweep_transport_list unless it has first been un-marked. */
+void
+mark_transport_list(void)
+{
+ if (!transport_list)
+ transport_list = smartlist_create();
+ SMARTLIST_FOREACH(transport_list, transport_t *, t,
+ t->marked_for_removal = 1);
+}
+
+/** Remove every entry of the transport list that was marked with
+ * mark_transport_list if it has not subsequently been un-marked. */
+void
+sweep_transport_list(void)
+{
+ if (!transport_list)
+ transport_list = smartlist_create();
+ SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, t) {
+ if (t->marked_for_removal) {
+ SMARTLIST_DEL_CURRENT(transport_list, t);
+ transport_free(t);
+ }
+ } SMARTLIST_FOREACH_END(t);
+}
+
+/** Initialize the pluggable transports list to empty, creating it if
+ * needed. */
+void
+clear_transport_list(void)
+{
+ if (!transport_list)
+ transport_list = smartlist_create();
+ SMARTLIST_FOREACH(transport_list, transport_t *, t, transport_free(t));
+ smartlist_clear(transport_list);
+}
+
+/** Free the pluggable transport struct <b>transport</b>. */
+void
+transport_free(transport_t *transport)
+{
+ if (!transport)
+ return;
+
+ tor_free(transport->name);
+ tor_free(transport);
+}
+
+/** Returns the transport in our transport list that has the name <b>name</b>.
+ * Else returns NULL. */
+transport_t *
+transport_get_by_name(const char *name)
+{
+ tor_assert(name);
+
+ if (!transport_list)
+ return NULL;
+
+ SMARTLIST_FOREACH_BEGIN(transport_list, transport_t *, transport) {
+ if (!strcmp(transport->name, name))
+ return transport;
+ } SMARTLIST_FOREACH_END(transport);
+
+ return NULL;
+}
+
+/** Returns a transport_t struct for a transport proxy supporting the
+ protocol <b>name</b> listening at <b>addr</b>:<b>port</b> using
+ SOCKS version <b>socks_ver</b>. */
+transport_t *
+transport_create(const tor_addr_t *addr, uint16_t port,
+ const char *name, int socks_ver)
+{
+ transport_t *t = tor_malloc_zero(sizeof(transport_t));
+
+ tor_addr_copy(&t->addr, addr);
+ t->port = port;
+ t->name = tor_strdup(name);
+ t->socks_version = socks_ver;
+
+ return t;
+}
+
+/** Resolve any conflicts that the insertion of transport <b>t</b>
+ * might cause.
+ * Return 0 if <b>t</b> is OK and should be registered, 1 if there is
+ * a transport identical to <b>t</b> already registered and -1 if
+ * <b>t</b> cannot be added due to conflicts. */
+static int
+transport_resolve_conflicts(transport_t *t)
+{
+ /* This is how we resolve transport conflicts:
+
+ If there is already a transport with the same name and addrport,
+ we either have duplicate torrc lines OR we are here post-HUP and
+ this transport was here pre-HUP as well. In any case, mark the
+ old transport so that it doesn't get removed and ignore the new
+ one. Our caller has to free the new transport so we return '1' to
+ signify this.
+
+ If there is already a transport with the same name but different
+ addrport:
+ * if it's marked for removal, it means that it either has a lower
+ priority than 't' in torrc (otherwise the mark would have been
+ cleared by the paragraph above), or it doesn't exist at all in
+ the post-HUP torrc. We destroy the old transport and register 't'.
+ * if it's *not* marked for removal, it means that it was newly
+ added in the post-HUP torrc or that it's of higher priority, in
+ this case we ignore 't'. */
+ transport_t *t_tmp = transport_get_by_name(t->name);
+ if (t_tmp) { /* same name */
+ if (tor_addr_eq(&t->addr, &t_tmp->addr) && (t->port == t_tmp->port)) {
+ /* same name *and* addrport */
+ t_tmp->marked_for_removal = 0;
+ return 1;
+ } else { /* same name but different addrport */
+ if (t_tmp->marked_for_removal) { /* marked for removal */
+ log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' "
+ "but there was already a transport marked for deletion at "
+ "'%s:%u'. We deleted the old transport and registered the "
+ "new one.", t->name, fmt_addr(&t->addr), t->port,
+ fmt_addr(&t_tmp->addr), t_tmp->port);
+ smartlist_remove(transport_list, t_tmp);
+ transport_free(t_tmp);
+ } else { /* *not* marked for removal */
+ log_notice(LD_GENERAL, "You tried to add transport '%s' at '%s:%u' "
+ "but the same transport already exists at '%s:%u'. "
+ "Skipping.", t->name, fmt_addr(&t->addr), t->port,
+ fmt_addr(&t_tmp->addr), t_tmp->port);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/** Add transport <b>t</b> to the internal list of pluggable
+ * transports.
+ * Returns 0 if the transport was added correctly, 1 if the same
+ * transport was already registered (in this case the caller must
+ * free the transport) and -1 if there was an error. */
+int
+transport_add(transport_t *t)
+{
+ int r;
+ tor_assert(t);
+
+ r = transport_resolve_conflicts(t);
+
+ switch (r) {
+ case 0: /* should register transport */
+ if (!transport_list)
+ transport_list = smartlist_create();
+ smartlist_add(transport_list, t);
+ return 0;
+ default: /* let our caller know the return code */
+ return r;
+ }
+}
+
+/** Remember a new pluggable transport proxy at <b>addr</b>:<b>port</b>.
+ * <b>name</b> is set to the name of the protocol this proxy uses.
+ * <b>socks_ver</b> is set to the SOCKS version of the proxy. */
+int
+transport_add_from_config(const tor_addr_t *addr, uint16_t port,
+ const char *name, int socks_ver)
+{
+ transport_t *t = transport_create(addr, port, name, socks_ver);
+
+ int r = transport_add(t);
+
+ switch (r) {
+ case -1:
+ default:
+ log_notice(LD_GENERAL, "Could not add transport %s at %s:%u. Skipping.",
+ t->name, fmt_addr(&t->addr), t->port);
+ transport_free(t);
+ return -1;
+ case 1:
+ log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.",
+ t->name, fmt_addr(&t->addr), t->port);
+ transport_free(t); /* falling */
+ return 0;
+ case 0:
+ log_info(LD_GENERAL, "Succesfully registered transport %s at %s:%u.",
+ t->name, fmt_addr(&t->addr), t->port);
+ return 0;
+ }
+}
+
+/** Warn the user of possible pluggable transport misconfiguration.
+ * Return 0 if the validation happened, -1 if we should postpone the
+ * validation. */
+int
+validate_pluggable_transports_config(void)
+{
+ /* Don't validate if managed proxies are not yet fully configured. */
+ if (bridge_list && !pt_proxies_configuration_pending()) {
+ SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) {
+ /* Skip bridges without transports. */
+ if (!b->transport_name)
+ continue;
+ /* See if the user has Bridges that specify nonexistent
+ pluggable transports. We should warn the user in such case,
+ since it's probably misconfiguration. */
+ if (!transport_get_by_name(b->transport_name))
+ log_warn(LD_CONFIG, "You have a Bridge line using the %s "
+ "pluggable transport, but there doesn't seem to be a "
+ "corresponding ClientTransportPlugin line.",
+ b->transport_name);
+ } SMARTLIST_FOREACH_END(b);
+
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
/** Return a bridge pointer if <b>ri</b> is one of our known bridges
* (either by comparing keys if possible, else by comparing addr/port).
* Else return NULL. */
@@ -4570,7 +4839,7 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr,
/** Wrapper around get_configured_bridge_by_addr_port_digest() to look
* it up via router descriptor <b>ri</b>. */
static bridge_info_t *
-get_configured_bridge_by_routerinfo(routerinfo_t *ri)
+get_configured_bridge_by_routerinfo(const routerinfo_t *ri)
{
tor_addr_t addr;
tor_addr_from_ipv4h(&addr, ri->addr);
@@ -4580,11 +4849,29 @@ get_configured_bridge_by_routerinfo(routerinfo_t *ri)
/** Return 1 if <b>ri</b> is one of our known bridges, else 0. */
int
-routerinfo_is_a_configured_bridge(routerinfo_t *ri)
+routerinfo_is_a_configured_bridge(const routerinfo_t *ri)
{
return get_configured_bridge_by_routerinfo(ri) ? 1 : 0;
}
+/** Return 1 if <b>node</b> is one of our configured bridges, else 0. */
+int
+node_is_a_configured_bridge(const node_t *node)
+{
+ tor_addr_t addr;
+ uint16_t orport;
+ if (!node)
+ return 0;
+ if (node_get_addr(node, &addr) < 0)
+ return 0;
+ orport = node_get_orport(node);
+ if (orport == 0)
+ return 0;
+
+ return get_configured_bridge_by_addr_port_digest(
+ &addr, orport, node->identity) != NULL;
+}
+
/** We made a connection to a router at <b>addr</b>:<b>port</b>
* without knowing its digest. Its digest turned out to be <b>digest</b>.
* If it was a bridge, and we still don't know its digest, record it.
@@ -4604,10 +4891,12 @@ learned_router_identity(const tor_addr_t *addr, uint16_t port,
/** Remember a new bridge at <b>addr</b>:<b>port</b>. If <b>digest</b>
* is set, it tells us the identity key too. If we already had the
- * bridge in our list, unmark it, and don't actually add anything new. */
+ * bridge in our list, unmark it, and don't actually add anything new.
+ * If <b>transport_name</b> is non-NULL - the bridge is associated with a
+ * pluggable transport - we assign the transport to the bridge. */
void
bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
- const char *digest)
+ const char *digest, const char *transport_name)
{
bridge_info_t *b;
@@ -4621,6 +4910,8 @@ bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
b->port = port;
if (digest)
memcpy(b->identity, digest, DIGEST_LEN);
+ if (transport_name)
+ b->transport_name = tor_strdup(transport_name);
b->fetch_status.schedule = DL_SCHED_BRIDGE;
if (!bridge_list)
bridge_list = smartlist_create();
@@ -4658,12 +4949,48 @@ find_bridge_by_digest(const char *digest)
return NULL;
}
+/** If <b>addr</b> and <b>port</b> match the address and port of a
+ * bridge of ours that uses pluggable transports, place its transport
+ * in <b>transport</b>.
+ *
+ * Return 0 on success (found a transport, or found a bridge with no
+ * transport, or found no bridge); return -1 if we should be using a
+ * transport, but the transport could not be found.
+ */
+int
+find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
+ const transport_t **transport)
+{
+ *transport = NULL;
+ if (!bridge_list)
+ return 0;
+
+ SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) {
+ if (tor_addr_eq(&bridge->addr, addr) &&
+ (bridge->port == port)) { /* bridge matched */
+ if (bridge->transport_name) { /* it also uses pluggable transports */
+ *transport = transport_get_by_name(bridge->transport_name);
+ if (*transport == NULL) { /* it uses pluggable transports, but
+ the transport could not be found! */
+ return -1;
+ }
+ return 0;
+ } else { /* bridge matched, but it doesn't use transports. */
+ break;
+ }
+ }
+ } SMARTLIST_FOREACH_END(bridge);
+
+ *transport = NULL;
+ return 0;
+}
+
/** We need to ask <b>bridge</b> for its server descriptor. */
static void
launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)
{
char *address;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (connection_get_by_type_addr_port_purpose(
CONN_TYPE_DIR, &bridge->addr, bridge->port,
@@ -4704,15 +5031,20 @@ retry_bridge_descriptor_fetch_directly(const char *digest)
* descriptor, fetch a new copy of its descriptor -- either directly
* from the bridge or via a bridge authority. */
void
-fetch_bridge_descriptors(or_options_t *options, time_t now)
+fetch_bridge_descriptors(const or_options_t *options, time_t now)
{
- int num_bridge_auths = get_n_authorities(BRIDGE_AUTHORITY);
+ int num_bridge_auths = get_n_authorities(BRIDGE_DIRINFO);
int ask_bridge_directly;
int can_use_bridge_authority;
if (!bridge_list)
return;
+ /* If we still have unconfigured managed proxies, don't go and
+ connect to a bridge. */
+ if (pt_proxies_configuration_pending())
+ return;
+
SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge)
{
if (!download_status_is_ready(&bridge->fetch_status, now,
@@ -4771,26 +5103,55 @@ fetch_bridge_descriptors(or_options_t *options, time_t now)
}
/** If our <b>bridge</b> is configured to be a different address than
- * the bridge gives in its routerinfo <b>ri</b>, rewrite the routerinfo
+ * the bridge gives in <b>node</b>, rewrite the routerinfo
* we received to use the address we meant to use. Now we handle
* multihomed bridges better.
*/
static void
-rewrite_routerinfo_address_for_bridge(bridge_info_t *bridge, routerinfo_t *ri)
+rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node)
{
+ /* XXXX move this function. */
+ /* XXXX overridden addresses should really live in the node_t, so that the
+ * routerinfo_t and the microdesc_t can be immutable. But we can only
+ * do that safely if we know that no function that connects to an OR
+ * does so through an address from any source other than node_get_addr().
+ */
tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, ri->addr);
- if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
- bridge->port == ri->or_port)
- return; /* they match, so no need to do anything */
+ if (node->ri) {
+ routerinfo_t *ri = node->ri;
+ tor_addr_from_ipv4h(&addr, ri->addr);
+
+ if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
+ bridge->port == ri->or_port) {
+ /* they match, so no need to do anything */
+ } else {
+ ri->addr = tor_addr_to_ipv4h(&bridge->addr);
+ tor_free(ri->address);
+ ri->address = tor_dup_ip(ri->addr);
+ ri->or_port = bridge->port;
+ log_info(LD_DIR,
+ "Adjusted bridge routerinfo for '%s' to match configured "
+ "address %s:%d.",
+ ri->nickname, ri->address, ri->or_port);
+ }
+ }
+ if (node->rs) {
+ routerstatus_t *rs = node->rs;
+ tor_addr_from_ipv4h(&addr, rs->addr);
- ri->addr = tor_addr_to_ipv4h(&bridge->addr);
- tor_free(ri->address);
- ri->address = tor_dup_ip(ri->addr);
- ri->or_port = bridge->port;
- log_info(LD_DIR, "Adjusted bridge '%s' to match configured address %s:%d.",
- ri->nickname, ri->address, ri->or_port);
+ if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) &&
+ bridge->port == rs->or_port) {
+ /* they match, so no need to do anything */
+ } else {
+ rs->addr = tor_addr_to_ipv4h(&bridge->addr);
+ rs->or_port = bridge->port;
+ log_info(LD_DIR,
+ "Adjusted bridge routerstatus for '%s' to match "
+ "configured address %s:%d.",
+ rs->nickname, fmt_addr(&bridge->addr), rs->or_port);
+ }
+ }
}
/** We just learned a descriptor for a bridge. See if that
@@ -4804,16 +5165,19 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
int first = !any_bridge_descriptors_known();
bridge_info_t *bridge = get_configured_bridge_by_routerinfo(ri);
time_t now = time(NULL);
- ri->is_running = 1;
+ router_set_status(ri->cache_info.identity_digest, 1);
if (bridge) { /* if we actually want to use this one */
+ node_t *node;
/* it's here; schedule its re-fetch for a long time from now. */
if (!from_cache)
download_status_reset(&bridge->fetch_status);
- rewrite_routerinfo_address_for_bridge(bridge, ri);
+ node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+ tor_assert(node);
+ rewrite_node_address_for_bridge(bridge, node);
+ add_an_entry_guard(node, 1, 1);
- add_an_entry_guard(ri, 1);
log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname,
from_cache ? "cached" : "fresh");
/* set entry->made_contact so if it goes down we don't drop it from
@@ -4866,21 +5230,20 @@ any_pending_bridge_descriptor_fetches(void)
* down. Else return 0. If <b>act</b> is 1, then mark the down guards
* up; else just observe and report. */
static int
-entries_retry_helper(or_options_t *options, int act)
+entries_retry_helper(const or_options_t *options, int act)
{
- routerinfo_t *ri;
+ const node_t *node;
int any_known = 0;
int any_running = 0;
- int purpose = options->UseBridges ?
- ROUTER_PURPOSE_BRIDGE : ROUTER_PURPOSE_GENERAL;
+ int need_bridges = options->UseBridges != 0;
if (!entry_guards)
entry_guards = smartlist_create();
- SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e,
- {
- ri = router_get_by_digest(e->identity);
- if (ri && ri->purpose == purpose) {
+ SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) {
+ node = node_get_by_id(e->identity);
+ if (node && node_has_descriptor(node) &&
+ node_is_bridge(node) == need_bridges) {
any_known = 1;
- if (ri->is_running)
+ if (node->is_running)
any_running = 1; /* some entry is both known and running */
else if (act) {
/* Mark all current connections to this OR as unhealthy, since
@@ -4889,15 +5252,15 @@ entries_retry_helper(or_options_t *options, int act)
* the node down and undermine the retry attempt. We mark even
* the established conns, since if the network just came back
* we'll want to attach circuits to fresh conns. */
- connection_or_set_bad_connections(ri->cache_info.identity_digest, 1);
+ connection_or_set_bad_connections(node->identity, 1);
/* mark this entry node for retry */
- router_set_status(ri->cache_info.identity_digest, 1);
+ router_set_status(node->identity, 1);
e->can_retry = 1;
e->bad_since = 0;
}
}
- });
+ } SMARTLIST_FOREACH_END(e);
log_debug(LD_DIR, "%d: any_known %d, any_running %d",
act, any_known, any_running);
return any_known && !any_running;
@@ -4906,7 +5269,7 @@ entries_retry_helper(or_options_t *options, int act)
/** Do we know any descriptors for our bridges / entrynodes, and are
* all the ones we have descriptors for down? */
int
-entries_known_but_down(or_options_t *options)
+entries_known_but_down(const or_options_t *options)
{
tor_assert(entry_list_is_constrained(options));
return entries_retry_helper(options, 0);
@@ -4914,7 +5277,7 @@ entries_known_but_down(or_options_t *options)
/** Mark all down known bridges / entrynodes up. */
void
-entries_retry_all(or_options_t *options)
+entries_retry_all(const or_options_t *options)
{
tor_assert(entry_list_is_constrained(options));
entries_retry_helper(options, 1);
@@ -4932,7 +5295,10 @@ entry_guards_free_all(void)
entry_guards = NULL;
}
clear_bridge_list();
+ clear_transport_list();
smartlist_free(bridge_list);
+ smartlist_free(transport_list);
bridge_list = NULL;
+ transport_list = NULL;
}
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 0e673e1c0..1052db615 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -12,6 +12,21 @@
#ifndef _TOR_CIRCUITBUILD_H
#define _TOR_CIRCUITBUILD_H
+/** Represents a pluggable transport proxy used by a bridge. */
+typedef struct {
+ /** SOCKS version: One of PROXY_SOCKS4, PROXY_SOCKS5. */
+ int socks_version;
+ /** Name of pluggable transport protocol */
+ char *name;
+ /** Address of proxy */
+ tor_addr_t addr;
+ /** Port of proxy */
+ uint16_t port;
+ /** Boolean: We are re-parsing our transport list, and we are going to remove
+ * this one if we don't find it in the list of configured transports. */
+ unsigned marked_for_removal : 1;
+} transport_t;
+
char *circuit_list_path(origin_circuit_t *circ, int verbose);
char *circuit_list_path_for_controller(origin_circuit_t *circ);
void circuit_log_path(int severity, unsigned int domain,
@@ -44,18 +59,19 @@ void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
extend_info_t *extend_info_alloc(const char *nickname, const char *digest,
crypto_pk_env_t *onion_key,
const tor_addr_t *addr, uint16_t port);
-extend_info_t *extend_info_from_router(routerinfo_t *r);
+extend_info_t *extend_info_from_router(const routerinfo_t *r);
+extend_info_t *extend_info_from_node(const node_t *node);
extend_info_t *extend_info_dup(extend_info_t *info);
void extend_info_free(extend_info_t *info);
-routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
+const node_t *build_state_get_exit_node(cpath_build_state_t *state);
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
-void entry_guards_compute_status(or_options_t *options, time_t now);
+void entry_guards_compute_status(const or_options_t *options, time_t now);
int entry_guard_register_connect_status(const char *digest, int succeeded,
int mark_relay_status, time_t now);
void entry_nodes_should_be_added(void);
-int entry_list_is_constrained(or_options_t *options);
-routerinfo_t *choose_random_entry(cpath_build_state_t *state);
+int entry_list_is_constrained(const or_options_t *options);
+const node_t *choose_random_entry(cpath_build_state_t *state);
int entry_guards_parse_state(or_state_t *state, int set, char **msg);
void entry_guards_update_state(or_state_t *state);
int getinfo_helper_entry_guards(control_connection_t *conn,
@@ -64,18 +80,23 @@ int getinfo_helper_entry_guards(control_connection_t *conn,
void mark_bridge_list(void);
void sweep_bridge_list(void);
-int routerinfo_is_a_configured_bridge(routerinfo_t *ri);
+void mark_transport_list(void);
+void sweep_transport_list(void);
+
+int routerinfo_is_a_configured_bridge(const routerinfo_t *ri);
+int node_is_a_configured_bridge(const node_t *node);
void learned_router_identity(const tor_addr_t *addr, uint16_t port,
const char *digest);
void bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
- const char *digest);
+ const char *digest,
+ const char *transport_name);
void retry_bridge_descriptor_fetch_directly(const char *digest);
-void fetch_bridge_descriptors(or_options_t *options, time_t now);
+void fetch_bridge_descriptors(const or_options_t *options, time_t now);
void learned_bridge_descriptor(routerinfo_t *ri, int from_cache);
int any_bridge_descriptors_known(void);
int any_pending_bridge_descriptor_fetches(void);
-int entries_known_but_down(or_options_t *options);
-void entries_retry_all(or_options_t *options);
+int entries_known_but_down(const or_options_t *options);
+void entries_retry_all(const or_options_t *options);
void entry_guards_free_all(void);
@@ -124,5 +145,19 @@ void circuit_build_times_network_circ_success(circuit_build_times_t *cbt);
int circuit_build_times_get_bw_scale(networkstatus_t *ns);
+void clear_transport_list(void);
+int transport_add_from_config(const tor_addr_t *addr, uint16_t port,
+ const char *name, int socks_ver);
+int transport_add(transport_t *t);
+void transport_free(transport_t *transport);
+transport_t *transport_create(const tor_addr_t *addr, uint16_t port,
+ const char *name, int socks_ver);
+
+int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
+ const transport_t **transport);
+transport_t *transport_get_by_name(const char *name);
+
+int validate_pluggable_transports_config(void);
+
#endif
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index e9cc9eb1f..25b80f11f 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -19,6 +19,7 @@
#include "connection_or.h"
#include "control.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "onion.h"
#include "relay.h"
#include "rendclient.h"
@@ -86,10 +87,7 @@ orconn_circid_circuit_map_t *_last_circid_orconn_ent = NULL;
/** Implementation helper for circuit_set_{p,n}_circid_orconn: A circuit ID
* and/or or_connection for circ has just changed from <b>old_conn, old_id</b>
* to <b>conn, id</b>. Adjust the conn,circid map as appropriate, removing
- * the old entry (if any) and adding a new one. If <b>active</b> is true,
- * remove the circuit from the list of active circuits on old_conn and add it
- * to the list of active circuits on conn.
- * XXX "active" isn't an arg anymore */
+ * the old entry (if any) and adding a new one. */
static void
circuit_set_circid_orconn_helper(circuit_t *circ, int direction,
circid_t id,
@@ -552,6 +550,16 @@ circuit_free(circuit_t *circ)
crypto_free_pk_env(ocirc->intro_key);
rend_data_free(ocirc->rend_data);
+
+ tor_free(ocirc->dest_address);
+ if (ocirc->socks_username) {
+ memset(ocirc->socks_username, 0x12, ocirc->socks_username_len);
+ tor_free(ocirc->socks_username);
+ }
+ if (ocirc->socks_password) {
+ memset(ocirc->socks_password, 0x06, ocirc->socks_password_len);
+ tor_free(ocirc->socks_password);
+ }
} else {
or_circuit_t *ocirc = TO_OR_CIRCUIT(circ);
/* Remember cell statistics for this circuit before deallocating. */
@@ -981,7 +989,7 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
int need_uptime = (flags & CIRCLAUNCH_NEED_UPTIME) != 0;
int need_capacity = (flags & CIRCLAUNCH_NEED_CAPACITY) != 0;
int internal = (flags & CIRCLAUNCH_IS_INTERNAL) != 0;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
/* Make sure we're not trying to create a onehop circ by
* cannibalization. */
@@ -1003,19 +1011,20 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
(!need_capacity || circ->build_state->need_capacity) &&
(internal == circ->build_state->is_internal) &&
circ->remaining_relay_early_cells &&
- !circ->build_state->onehop_tunnel) {
+ !circ->build_state->onehop_tunnel &&
+ !circ->isolation_values_set) {
if (info) {
/* need to make sure we don't duplicate hops */
crypt_path_t *hop = circ->cpath;
- routerinfo_t *ri1 = router_get_by_digest(info->identity_digest);
+ const node_t *ri1 = node_get_by_id(info->identity_digest);
do {
- routerinfo_t *ri2;
+ const node_t *ri2;
if (tor_memeq(hop->extend_info->identity_digest,
info->identity_digest, DIGEST_LEN))
goto next;
if (ri1 &&
- (ri2 = router_get_by_digest(hop->extend_info->identity_digest))
- && routers_in_same_family(ri1, ri2))
+ (ri2 = node_get_by_id(hop->extend_info->identity_digest))
+ && nodes_in_same_family(ri1, ri2))
goto next;
hop=hop->next;
} while (hop!=circ->cpath);
@@ -1100,7 +1109,7 @@ void
circuit_expire_all_dirty_circs(void)
{
circuit_t *circ;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
for (circ=global_circuitlist; circ; circ = circ->next) {
if (CIRCUIT_IS_ORIGIN(circ) &&
@@ -1119,9 +1128,11 @@ circuit_expire_all_dirty_circs(void)
* - If circ isn't open yet: call circuit_build_failed() if we're
* the origin, and in either case call circuit_rep_hist_note_result()
* to note stats.
- * - If purpose is C_INTRODUCE_ACK_WAIT, remove the intro point we
- * just tried from our list of intro points for that service
- * descriptor.
+ * - If purpose is C_INTRODUCE_ACK_WAIT, report the intro point
+ * failure we just had to the hidden service client module.
+ * - If purpose is C_INTRODUCING and <b>reason</b> isn't TIMEOUT,
+ * report to the hidden service client module that the intro point
+ * we just tried may be unreachable.
* - Send appropriate destroys and edge_destroys for conns and
* streams attached to circ.
* - If circ->rend_splice is set (we are the midpoint of a joined
@@ -1190,16 +1201,33 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line,
}
if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ int timed_out = (reason == END_CIRC_REASON_TIMEOUT);
tor_assert(circ->state == CIRCUIT_STATE_OPEN);
tor_assert(ocirc->build_state->chosen_exit);
tor_assert(ocirc->rend_data);
/* treat this like getting a nack from it */
- log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). "
- "Removing from descriptor.",
+ log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). %s",
safe_str_client(ocirc->rend_data->onion_address),
+ safe_str_client(build_state_get_exit_nickname(ocirc->build_state)),
+ timed_out ? "Recording timeout." : "Removing from descriptor.");
+ rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit,
+ ocirc->rend_data,
+ timed_out ?
+ INTRO_POINT_FAILURE_TIMEOUT :
+ INTRO_POINT_FAILURE_GENERIC);
+ } else if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCING &&
+ reason != END_CIRC_REASON_TIMEOUT) {
+ origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
+ tor_assert(ocirc->build_state->chosen_exit);
+ tor_assert(ocirc->rend_data);
+ log_info(LD_REND, "Failed intro circ %s to %s "
+ "(building circuit to intro point). "
+ "Marking intro point as possibly unreachable.",
+ safe_str_client(ocirc->rend_data->onion_address),
safe_str_client(build_state_get_exit_nickname(ocirc->build_state)));
- rend_client_remove_intro_point(ocirc->build_state->chosen_exit,
- ocirc->rend_data);
+ rend_client_report_intro_point_failure(ocirc->build_state->chosen_exit,
+ ocirc->rend_data,
+ INTRO_POINT_FAILURE_UNREACHABLE);
}
if (circ->n_conn) {
circuit_clear_cell_queue(circ, circ->n_conn);
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 0ad8b3b51..23efe0534 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -17,6 +17,8 @@
#include "connection.h"
#include "connection_edge.h"
#include "control.h"
+#include "nodelist.h"
+#include "networkstatus.h"
#include "policies.h"
#include "rendclient.h"
#include "rendcommon.h"
@@ -38,19 +40,19 @@ static void circuit_increment_failure_count(void);
* Else return 0.
*/
static int
-circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
+circuit_is_acceptable(const origin_circuit_t *origin_circ,
+ const entry_connection_t *conn,
int must_be_open, uint8_t purpose,
int need_uptime, int need_internal,
time_t now)
{
- routerinfo_t *exitrouter;
+ const circuit_t *circ = TO_CIRCUIT(origin_circ);
+ const node_t *exitnode;
cpath_build_state_t *build_state;
tor_assert(circ);
tor_assert(conn);
tor_assert(conn->socks_request);
- if (!CIRCUIT_IS_ORIGIN(circ))
- return 0; /* this circ doesn't start at us */
if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn))
return 0; /* ignore non-open circs */
if (circ->marked_for_close)
@@ -85,8 +87,8 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
* circuit, it's the magical extra bob hop. so just check the nickname
* of the one we meant to finish at.
*/
- build_state = TO_ORIGIN_CIRCUIT(circ)->build_state;
- exitrouter = build_state_get_exit_router(build_state);
+ build_state = origin_circ->build_state;
+ exitnode = build_state_get_exit_node(build_state);
if (need_uptime && !build_state->need_uptime)
return 0;
@@ -94,7 +96,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
return 0;
if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
- if (!exitrouter && !build_state->onehop_tunnel) {
+ if (!exitnode && !build_state->onehop_tunnel) {
log_debug(LD_CIRC,"Not considering circuit with unknown router.");
return 0; /* this circuit is screwed and doesn't know it yet,
* or is a rendezvous circuit. */
@@ -115,7 +117,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
if (tor_digest_is_zero(digest)) {
/* we don't know the digest; have to compare addr:port */
tor_addr_t addr;
- int r = tor_addr_from_str(&addr, conn->socks_request->address);
+ int r = tor_addr_parse(&addr, conn->socks_request->address);
if (r < 0 ||
!tor_addr_eq(&build_state->chosen_exit->addr, &addr) ||
build_state->chosen_exit->port != conn->socks_request->port)
@@ -128,30 +130,43 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn,
return 0;
}
}
- if (exitrouter && !connection_ap_can_use_exit(conn, exitrouter)) {
+ if (exitnode && !connection_ap_can_use_exit(conn, exitnode)) {
/* can't exit from this router */
return 0;
}
} else { /* not general */
- origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
- if ((conn->rend_data && !ocirc->rend_data) ||
- (!conn->rend_data && ocirc->rend_data) ||
- (conn->rend_data && ocirc->rend_data &&
- rend_cmp_service_ids(conn->rend_data->onion_address,
- ocirc->rend_data->onion_address))) {
+ const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
+ if ((edge_conn->rend_data && !origin_circ->rend_data) ||
+ (!edge_conn->rend_data && origin_circ->rend_data) ||
+ (edge_conn->rend_data && origin_circ->rend_data &&
+ rend_cmp_service_ids(edge_conn->rend_data->onion_address,
+ origin_circ->rend_data->onion_address))) {
/* this circ is not for this conn */
return 0;
}
}
+
+ if (!connection_edge_compatible_with_circuit(conn, origin_circ)) {
+ /* conn needs to be isolated from other conns that have already used
+ * origin_circ */
+ return 0;
+ }
+
return 1;
}
/** Return 1 if circuit <b>a</b> is better than circuit <b>b</b> for
- * <b>purpose</b>, and return 0 otherwise. Used by circuit_get_best.
+ * <b>conn</b>, and return 0 otherwise. Used by circuit_get_best.
*/
static int
-circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
+circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
+ const entry_connection_t *conn)
{
+ const circuit_t *a = TO_CIRCUIT(oa);
+ const circuit_t *b = TO_CIRCUIT(ob);
+ const uint8_t purpose = ENTRY_TO_CONN(conn)->purpose;
+ int a_bits, b_bits;
+
switch (purpose) {
case CIRCUIT_PURPOSE_C_GENERAL:
/* if it's used but less dirty it's best;
@@ -165,8 +180,7 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
if (a->timestamp_dirty ||
timercmp(&a->timestamp_created, &b->timestamp_created, >))
return 1;
- if (CIRCUIT_IS_ORIGIN(b) &&
- TO_ORIGIN_CIRCUIT(b)->build_state->is_internal)
+ if (ob->build_state->is_internal)
/* XXX023 what the heck is this internal thing doing here. I
* think we can get rid of it. circuit_is_acceptable() already
* makes sure that is_internal is exactly what we need it to
@@ -185,6 +199,29 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
return 1;
break;
}
+
+ /* XXXX023 Maybe this check should get a higher priority to avoid
+ * using up circuits too rapidly. */
+
+ a_bits = connection_edge_update_circuit_isolation(conn,
+ (origin_circuit_t*)oa, 1);
+ b_bits = connection_edge_update_circuit_isolation(conn,
+ (origin_circuit_t*)ob, 1);
+ /* if x_bits < 0, then we have not used x for anything; better not to dirty
+ * a connection if we can help it. */
+ if (a_bits < 0) {
+ return 0;
+ } else if (b_bits < 0) {
+ return 1;
+ }
+ a_bits &= ~ oa->isolation_flags_mixed;
+ a_bits &= ~ ob->isolation_flags_mixed;
+ if (n_bits_set_u8(a_bits) < n_bits_set_u8(b_bits)) {
+ /* The fewer new restrictions we need to make on a circuit for stream
+ * isolation, the better. */
+ return 1;
+ }
+
return 0;
}
@@ -205,10 +242,12 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose)
* closest introduce-purposed circuit that you can find.
*/
static origin_circuit_t *
-circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
+circuit_get_best(const entry_connection_t *conn,
+ int must_be_open, uint8_t purpose,
int need_uptime, int need_internal)
{
- circuit_t *circ, *best=NULL;
+ circuit_t *circ;
+ origin_circuit_t *best=NULL;
struct timeval now;
int intro_going_on_but_too_old = 0;
@@ -221,7 +260,11 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
tor_gettimeofday(&now);
for (circ=global_circuitlist;circ;circ = circ->next) {
- if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,
+ origin_circuit_t *origin_circ;
+ if (!CIRCUIT_IS_ORIGIN(circ))
+ continue;
+ origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ if (!circuit_is_acceptable(origin_circ,conn,must_be_open,purpose,
need_uptime,need_internal,now.tv_sec))
continue;
@@ -235,8 +278,8 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
/* now this is an acceptable circ to hand back. but that doesn't
* mean it's the *best* circ to hand back. try to decide.
*/
- if (!best || circuit_is_better(circ,best,purpose))
- best = circ;
+ if (!best || circuit_is_better(origin_circ,best,conn))
+ best = origin_circ;
}
if (!best && intro_going_on_but_too_old)
@@ -244,7 +287,28 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose,
"right now, but it has already taken quite a while. Starting "
"one in parallel.");
- return best ? TO_ORIGIN_CIRCUIT(best) : NULL;
+ return best;
+}
+
+/** Return the number of not-yet-open general-purpose origin circuits. */
+static int
+count_pending_general_client_circuits(void)
+{
+ const circuit_t *circ;
+
+ int count = 0;
+
+ for (circ = global_circuitlist; circ; circ = circ->next) {
+ if (circ->marked_for_close ||
+ circ->state == CIRCUIT_STATE_OPEN ||
+ circ->purpose != CIRCUIT_PURPOSE_C_GENERAL ||
+ !CIRCUIT_IS_ORIGIN(circ))
+ continue;
+
+ ++count;
+ }
+
+ return count;
}
#if 0
@@ -481,11 +545,11 @@ circuit_remove_handled_ports(smartlist_t *needed_ports)
* Else return 0.
*/
int
-circuit_stream_is_being_handled(edge_connection_t *conn,
+circuit_stream_is_being_handled(entry_connection_t *conn,
uint16_t port, int min)
{
circuit_t *circ;
- routerinfo_t *exitrouter;
+ const node_t *exitnode;
int num=0;
time_t now = time(NULL);
int need_uptime = smartlist_string_num_isin(get_options()->LongLivedPorts,
@@ -501,14 +565,14 @@ circuit_stream_is_being_handled(edge_connection_t *conn,
if (build_state->is_internal || build_state->onehop_tunnel)
continue;
- exitrouter = build_state_get_exit_router(build_state);
- if (exitrouter && (!need_uptime || build_state->need_uptime)) {
+ exitnode = build_state_get_exit_node(build_state);
+ if (exitnode && (!need_uptime || build_state->need_uptime)) {
int ok;
if (conn) {
- ok = connection_ap_can_use_exit(conn, exitrouter);
+ ok = connection_ap_can_use_exit(conn, exitnode);
} else {
- addr_policy_result_t r = compare_addr_to_addr_policy(
- 0, port, exitrouter->exit_policy);
+ addr_policy_result_t r;
+ r = compare_tor_addr_to_node_policy(NULL, port, exitnode);
ok = r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED;
}
if (ok) {
@@ -575,7 +639,7 @@ circuit_predict_and_launch_new(void)
log_info(LD_CIRC,
"Have %d clean circs (%d internal), need another exit circ.",
num, num_internal);
- circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+ circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
return;
}
@@ -587,7 +651,7 @@ circuit_predict_and_launch_new(void)
"Have %d clean circs (%d internal), need another internal "
"circ for my hidden service.",
num, num_internal);
- circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+ circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
return;
}
@@ -605,7 +669,7 @@ circuit_predict_and_launch_new(void)
"Have %d clean circs (%d uptime-internal, %d internal), need"
" another hidden service circ.",
num, num_uptime_internal, num_internal);
- circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+ circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
return;
}
@@ -618,7 +682,7 @@ circuit_predict_and_launch_new(void)
flags = CIRCLAUNCH_NEED_CAPACITY;
log_info(LD_CIRC,
"Have %d clean circs need another buildtime test circ.", num);
- circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, flags);
+ circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags);
return;
}
}
@@ -635,7 +699,7 @@ void
circuit_build_needed_circs(time_t now)
{
static time_t time_to_new_circuit = 0;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
/* launch a new circ for any pending streams that need one */
connection_ap_attach_pending();
@@ -654,9 +718,9 @@ circuit_build_needed_circs(time_t now)
circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL);
if (get_options()->RunTesting &&
circ &&
- circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) {
+ circ->timestamp_created.tv_sec + TESTING_CIRCUIT_INTERVAL < now) {
log_fn(LOG_INFO,"Creating a new testing circuit.");
- circuit_launch_by_router(CIRCUIT_PURPOSE_C_GENERAL, NULL, 0);
+ circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, 0);
}
#endif
}
@@ -675,7 +739,11 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
tor_assert(circ);
tor_assert(conn);
- conn->cpath_layer = NULL; /* make sure we don't keep a stale pointer */
+ if (conn->_base.type == CONN_TYPE_AP) {
+ entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
+ entry_conn->may_use_optimistic_data = 0;
+ }
+ conn->cpath_layer = NULL; /* don't keep a stale pointer */
conn->on_circuit = NULL;
if (CIRCUIT_IS_ORIGIN(circ)) {
@@ -936,6 +1004,7 @@ circuit_testing_failed(origin_circuit_t *circ, int at_last_hop)
void
circuit_has_opened(origin_circuit_t *circ)
{
+ int can_try_clearing_isolation = 0, tried_clearing_isolation = 0;
control_event_circuit_status(circ, CIRC_EVENT_BUILT, 0);
/* Remember that this circuit has finished building. Now if we start
@@ -943,9 +1012,12 @@ circuit_has_opened(origin_circuit_t *circ)
* to consider its build time. */
circ->has_opened = 1;
+ again:
+
switch (TO_CIRCUIT(circ)->purpose) {
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
rend_client_rendcirc_has_opened(circ);
+ can_try_clearing_isolation = 1;
connection_ap_attach_pending();
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
@@ -954,6 +1026,7 @@ circuit_has_opened(origin_circuit_t *circ)
case CIRCUIT_PURPOSE_C_GENERAL:
/* Tell any AP connections that have been waiting for a new
* circuit that one is ready. */
+ can_try_clearing_isolation = 1;
connection_ap_attach_pending();
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
@@ -971,6 +1044,24 @@ circuit_has_opened(origin_circuit_t *circ)
* This won't happen in normal operation, but might happen if the
* controller did it. Just let it slide. */
}
+
+ if (/* The circuit may have become non-open if it was cannibalized.*/
+ circ->_base.state == CIRCUIT_STATE_OPEN &&
+ /* Only if the purpose is clearable, and only if we haven't tried
+ * to clear isolation yet, do we try. */
+ can_try_clearing_isolation && !tried_clearing_isolation &&
+ /* If !isolation_values_set, there is nothing to clear. */
+ circ->isolation_values_set &&
+ /* It's not legal to clear a circuit's isolation info if it's ever had
+ * streams attached */
+ !circ->isolation_any_streams_attached) {
+ /* If we have any isolation information set on this circuit, and
+ * we didn't manage to attach any streams to it, then we can
+ * and should clear it and try again. */
+ circuit_clear_isolation(circ);
+ tried_clearing_isolation = 1;
+ goto again;
+ }
}
/** Called whenever a circuit could not be successfully built.
@@ -1091,17 +1182,9 @@ static int did_circs_fail_last_period = 0;
/** Launch a new circuit; see circuit_launch_by_extend_info() for
* details on arguments. */
origin_circuit_t *
-circuit_launch_by_router(uint8_t purpose,
- routerinfo_t *exit, int flags)
+circuit_launch(uint8_t purpose, int flags)
{
- origin_circuit_t *circ;
- extend_info_t *info = NULL;
- if (exit)
- info = extend_info_from_router(exit);
- circ = circuit_launch_by_extend_info(purpose, info, flags);
-
- extend_info_free(info);
- return circ;
+ return circuit_launch_by_extend_info(purpose, NULL, flags);
}
/** Launch a new circuit with purpose <b>purpose</b> and exit node
@@ -1207,7 +1290,7 @@ circuit_reset_failure_count(int timeout)
* Write the found or in-progress or launched circ into *circp.
*/
static int
-circuit_get_open_circ_or_launch(edge_connection_t *conn,
+circuit_get_open_circ_or_launch(entry_connection_t *conn,
uint8_t desired_circuit_purpose,
origin_circuit_t **circp)
{
@@ -1215,15 +1298,15 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
int check_exit_policy;
int need_uptime, need_internal;
int want_onehop;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
tor_assert(conn);
tor_assert(circp);
- tor_assert(conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_CIRCUIT_WAIT);
check_exit_policy =
conn->socks_request->command == SOCKS_COMMAND_CONNECT &&
!conn->use_begindir &&
- !connection_edge_is_rendezvous_stream(conn);
+ !connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn));
want_onehop = conn->want_onehop;
need_uptime = !conn->want_onehop && !conn->use_begindir &&
@@ -1269,12 +1352,14 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
if (check_exit_policy) {
if (!conn->chosen_exit_name) {
struct in_addr in;
- uint32_t addr = 0;
- if (tor_inet_aton(conn->socks_request->address, &in))
- addr = ntohl(in.s_addr);
- if (router_exit_policy_all_routers_reject(addr,
- conn->socks_request->port,
- need_uptime)) {
+ tor_addr_t addr, *addrp=NULL;
+ if (tor_inet_aton(conn->socks_request->address, &in)) {
+ tor_addr_from_in(&addr, &in);
+ addrp = &addr;
+ }
+ if (router_exit_policy_all_nodes_reject(addrp,
+ conn->socks_request->port,
+ need_uptime)) {
log_notice(LD_APP,
"No Tor server allows exit to %s:%d. Rejecting.",
safe_str_client(conn->socks_request->address),
@@ -1284,9 +1369,9 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
} else {
/* XXXX023 Duplicates checks in connection_ap_handshake_attach_circuit:
* refactor into a single function? */
- routerinfo_t *router = router_get_by_nickname(conn->chosen_exit_name, 1);
+ const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
int opt = conn->chosen_exit_optional;
- if (router && !connection_ap_can_use_exit(conn, router)) {
+ if (node && !connection_ap_can_use_exit(conn, node)) {
log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
"Requested exit point '%s' is excluded or "
"would refuse request. %s.",
@@ -1312,22 +1397,37 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
if (!circ) {
extend_info_t *extend_info=NULL;
uint8_t new_circ_purpose;
+ const int n_pending = count_pending_general_client_circuits();
+
+ if (n_pending >= options->MaxClientCircuitsPending) {
+ static ratelim_t delay_limit = RATELIM_INIT(10*60);
+ char *m;
+ if ((m = rate_limit_log(&delay_limit, approx_time()))) {
+ log_notice(LD_APP, "We'd like to launch a circuit to handle a "
+ "connection, but we already have %d general-purpose client "
+ "circuits pending. Waiting until some finish.",
+ n_pending);
+ tor_free(m);
+ }
+ return 0;
+ }
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
/* need to pick an intro point */
- tor_assert(conn->rend_data);
- extend_info = rend_client_get_random_intro(conn->rend_data);
+ rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data;
+ tor_assert(rend_data);
+ extend_info = rend_client_get_random_intro(rend_data);
if (!extend_info) {
log_info(LD_REND,
"No intro points for '%s': re-fetching service descriptor.",
- safe_str_client(conn->rend_data->onion_address));
- rend_client_refetch_v2_renddesc(conn->rend_data);
- conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
+ safe_str_client(rend_data->onion_address));
+ rend_client_refetch_v2_renddesc(rend_data);
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_RENDDESC_WAIT;
return 0;
}
log_info(LD_REND,"Chose %s as intro point for '%s'.",
extend_info_describe(extend_info),
- safe_str_client(conn->rend_data->onion_address));
+ safe_str_client(rend_data->onion_address));
}
/* If we have specified a particular exit node for our
@@ -1335,11 +1435,11 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
*/
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
if (conn->chosen_exit_name) {
- routerinfo_t *r;
+ const node_t *r;
int opt = conn->chosen_exit_optional;
- r = router_get_by_nickname(conn->chosen_exit_name, 1);
- if (r) {
- extend_info = extend_info_from_router(r);
+ r = node_get_by_nickname(conn->chosen_exit_name, 1);
+ if (r && node_has_descriptor(r)) {
+ extend_info = extend_info_from_node(r);
} else {
log_debug(LD_DIR, "considering %d, %s",
want_onehop, conn->chosen_exit_name);
@@ -1354,7 +1454,7 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
log_info(LD_DIR, "Broken exit digest on tunnel conn. Closing.");
return -1;
}
- if (tor_addr_from_str(&addr, conn->socks_request->address) < 0) {
+ if (tor_addr_parse(&addr, conn->socks_request->address) < 0) {
log_info(LD_DIR, "Broken address %s on tunnel conn. Closing.",
escaped_safe_str_client(conn->socks_request->address));
return -1;
@@ -1416,18 +1516,26 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
rep_hist_note_used_internal(time(NULL), need_uptime, 1);
if (circ) {
/* write the service_id into circ */
- circ->rend_data = rend_data_dup(conn->rend_data);
+ circ->rend_data = rend_data_dup(ENTRY_TO_EDGE_CONN(conn)->rend_data);
if (circ->_base.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
circ->_base.state == CIRCUIT_STATE_OPEN)
rend_client_rendcirc_has_opened(circ);
}
}
- }
- if (!circ)
+ } /* endif (!circ) */
+ if (circ) {
+ /* Mark the circuit with the isolation fields for this connection.
+ * When the circuit arrives, we'll clear these flags: this is
+ * just some internal bookkeeping to make sure that we have
+ * launched enough circuits.
+ */
+ connection_edge_update_circuit_isolation(conn, circ, 0);
+ } else {
log_info(LD_APP,
"No safe circuit (purpose %d) ready for edge "
"connection; delaying.",
desired_circuit_purpose);
+ }
*circp = circ;
return 0;
}
@@ -1446,39 +1554,86 @@ cpath_is_on_circuit(origin_circuit_t *circ, crypt_path_t *crypt_path)
return 0;
}
+/** Return true iff client-side optimistic data is supported. */
+static int
+optimistic_data_enabled(void)
+{
+ const or_options_t *options = get_options();
+ if (options->OptimisticData < 0) {
+ /* XXX023 consider having auto default to 1 rather than 0 before
+ * the 0.2.3 branch goes stable. See bug 3617. -RD */
+ const int32_t enabled =
+ networkstatus_get_param(NULL, "UseOptimisticData", 0, 0, 1);
+ return (int)enabled;
+ }
+ return options->OptimisticData;
+}
+
/** Attach the AP stream <b>apconn</b> to circ's linked list of
* p_streams. Also set apconn's cpath_layer to <b>cpath</b>, or to the last
* hop in circ's cpath if <b>cpath</b> is NULL.
*/
static void
-link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ,
+link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ,
crypt_path_t *cpath)
{
+ const node_t *exitnode;
+
/* add it into the linked list of streams on this circuit */
log_debug(LD_APP|LD_CIRC, "attaching new conn to circ. n_circ_id %d.",
circ->_base.n_circ_id);
/* reset it, so we can measure circ timeouts */
- apconn->_base.timestamp_lastread = time(NULL);
- apconn->next_stream = circ->p_streams;
- apconn->on_circuit = TO_CIRCUIT(circ);
+ ENTRY_TO_CONN(apconn)->timestamp_lastread = time(NULL);
+ ENTRY_TO_EDGE_CONN(apconn)->next_stream = circ->p_streams;
+ ENTRY_TO_EDGE_CONN(apconn)->on_circuit = TO_CIRCUIT(circ);
/* assert_connection_ok(conn, time(NULL)); */
- circ->p_streams = apconn;
+ circ->p_streams = ENTRY_TO_EDGE_CONN(apconn);
+
+ if (connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(apconn))) {
+ /* We are attaching a stream to a rendezvous circuit. That means
+ * that an attempt to connect to a hidden service just
+ * succeeded. Tell rendclient.c. */
+ rend_client_note_connection_attempt_ended(
+ ENTRY_TO_EDGE_CONN(apconn)->rend_data->onion_address);
+ }
if (cpath) { /* we were given one; use it */
tor_assert(cpath_is_on_circuit(circ, cpath));
- apconn->cpath_layer = cpath;
- } else { /* use the last hop in the circuit */
+ } else {
+ /* use the last hop in the circuit */
tor_assert(circ->cpath);
tor_assert(circ->cpath->prev);
tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
- apconn->cpath_layer = circ->cpath->prev;
+ cpath = circ->cpath->prev;
+ }
+ ENTRY_TO_EDGE_CONN(apconn)->cpath_layer = cpath;
+
+ circ->isolation_any_streams_attached = 1;
+ connection_edge_update_circuit_isolation(apconn, circ, 0);
+
+ /* See if we can use optimistic data on this circuit */
+ if (cpath->extend_info &&
+ (exitnode = node_get_by_id(cpath->extend_info->identity_digest)) &&
+ exitnode->rs) {
+ /* Okay; we know what exit node this is. */
+ if (optimistic_data_enabled() &&
+ circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL &&
+ exitnode->rs->version_supports_optimistic_data)
+ apconn->may_use_optimistic_data = 1;
+ else
+ apconn->may_use_optimistic_data = 0;
+ log_info(LD_APP, "Looks like completed circuit to %s %s allow "
+ "optimistic data for connection to %s",
+ safe_str_client(node_describe(exitnode)),
+ apconn->may_use_optimistic_data ? "does" : "doesn't",
+ safe_str_client(apconn->socks_request->address));
}
}
/** Return true iff <b>address</b> is matched by one of the entries in
* TrackHostExits. */
int
-hostname_in_track_host_exits(or_options_t *options, const char *address)
+hostname_in_track_host_exits(const or_options_t *options, const char *address)
{
if (!options->TrackHostExits)
return 0;
@@ -1500,9 +1655,10 @@ hostname_in_track_host_exits(or_options_t *options, const char *address)
* <b>conn</b>'s destination.
*/
static void
-consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ)
+consider_recording_trackhost(const entry_connection_t *conn,
+ const origin_circuit_t *circ)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
char *new_address = NULL;
char fp[HEX_DIGEST_LEN+1];
@@ -1537,18 +1693,19 @@ consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ)
* indicated by <b>cpath</b>, or from the last hop in circ's cpath if
* <b>cpath</b> is NULL. */
int
-connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
+connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath)
{
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
tor_assert(conn);
- tor_assert(conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT ||
- conn->_base.state == AP_CONN_STATE_CONTROLLER_WAIT);
+ tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT ||
+ base_conn->state == AP_CONN_STATE_CONTROLLER_WAIT);
tor_assert(conn->socks_request);
tor_assert(circ);
tor_assert(circ->_base.state == CIRCUIT_STATE_OPEN);
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
if (!circ->_base.timestamp_dirty)
circ->_base.timestamp_dirty = time(NULL);
@@ -1578,21 +1735,22 @@ connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
/* XXXX this function should mark for close whenever it returns -1;
* its callers shouldn't have to worry about that. */
int
-connection_ap_handshake_attach_circuit(edge_connection_t *conn)
+connection_ap_handshake_attach_circuit(entry_connection_t *conn)
{
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
int retval;
int conn_age;
int want_onehop;
tor_assert(conn);
- tor_assert(conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
tor_assert(conn->socks_request);
want_onehop = conn->want_onehop;
- conn_age = (int)(time(NULL) - conn->_base.timestamp_created);
+ conn_age = (int)(time(NULL) - base_conn->timestamp_created);
if (conn_age >= get_options()->SocksTimeout) {
- int severity = (tor_addr_is_null(&conn->_base.addr) && !conn->_base.port) ?
+ int severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port) ?
LOG_INFO : LOG_NOTICE;
log_fn(severity, LD_APP,
"Tried for %d seconds to get a connection to %s:%d. Giving up.",
@@ -1601,13 +1759,14 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
return -1;
}
- if (!connection_edge_is_rendezvous_stream(conn)) { /* we're a general conn */
+ if (!connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn))) {
+ /* we're a general conn */
origin_circuit_t *circ=NULL;
if (conn->chosen_exit_name) {
- routerinfo_t *router = router_get_by_nickname(conn->chosen_exit_name, 1);
+ const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
int opt = conn->chosen_exit_optional;
- if (!router && !want_onehop) {
+ if (!node && !want_onehop) {
/* We ran into this warning when trying to extend a circuit to a
* hidden service directory for which we didn't have a router
* descriptor. See flyspray task 767 for more details. We should
@@ -1623,7 +1782,7 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
}
return -1;
}
- if (router && !connection_ap_can_use_exit(conn, router)) {
+ if (node && !connection_ap_can_use_exit(conn, node)) {
log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
"Requested exit point '%s' is excluded or "
"would refuse request. %s.",
@@ -1656,7 +1815,7 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
} else { /* we're a rendezvous conn */
origin_circuit_t *rendcirc=NULL, *introcirc=NULL;
- tor_assert(!conn->cpath_layer);
+ tor_assert(!ENTRY_TO_EDGE_CONN(conn)->cpath_layer);
/* start by finding a rendezvous circuit for us */
@@ -1712,8 +1871,9 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
!c->marked_for_close && CIRCUIT_IS_ORIGIN(c)) {
origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(c);
if (oc->rend_data &&
- !rend_cmp_service_ids(conn->rend_data->onion_address,
- oc->rend_data->onion_address)) {
+ !rend_cmp_service_ids(
+ ENTRY_TO_EDGE_CONN(conn)->rend_data->onion_address,
+ oc->rend_data->onion_address)) {
log_info(LD_REND|LD_CIRC, "Closing introduction circuit that we "
"built in parallel.");
circuit_mark_for_close(c, END_CIRC_REASON_TIMEOUT);
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index bfeaea20d..9867fd820 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -14,7 +14,7 @@
void circuit_expire_building(void);
void circuit_remove_handled_ports(smartlist_t *needed_ports);
-int circuit_stream_is_being_handled(edge_connection_t *conn, uint16_t port,
+int circuit_stream_is_being_handled(entry_connection_t *conn, uint16_t port,
int min);
#if 0
int circuit_conforms_to_options(const origin_circuit_t *circ,
@@ -43,15 +43,15 @@ void circuit_build_failed(origin_circuit_t *circ);
origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
extend_info_t *info,
int flags);
-origin_circuit_t *circuit_launch_by_router(uint8_t purpose,
- routerinfo_t *exit, int flags);
+origin_circuit_t *circuit_launch(uint8_t purpose, int flags);
void circuit_reset_failure_count(int timeout);
-int connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
+int connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath);
-int connection_ap_handshake_attach_circuit(edge_connection_t *conn);
+int connection_ap_handshake_attach_circuit(entry_connection_t *conn);
-int hostname_in_track_host_exits(or_options_t *options, const char *address);
+int hostname_in_track_host_exits(const or_options_t *options,
+ const char *address);
#endif
diff --git a/src/or/command.c b/src/or/command.c
index 1fa8bc6a7..d35e2a9c8 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -25,6 +25,7 @@
#include "control.h"
#include "cpuworker.h"
#include "hibernate.h"
+#include "nodelist.h"
#include "onion.h"
#include "relay.h"
#include "router.h"
@@ -45,6 +46,15 @@ uint64_t stats_n_versions_cells_processed = 0;
/** How many CELL_NETINFO cells have we received, ever? */
uint64_t stats_n_netinfo_cells_processed = 0;
+/** How many CELL_VPADDING cells have we received, ever? */
+uint64_t stats_n_vpadding_cells_processed = 0;
+/** How many CELL_CERTS cells have we received, ever? */
+uint64_t stats_n_cert_cells_processed = 0;
+/** How many CELL_AUTH_CHALLENGE cells have we received, ever? */
+uint64_t stats_n_auth_challenge_cells_processed = 0;
+/** How many CELL_AUTHENTICATE cells have we received, ever? */
+uint64_t stats_n_authenticate_cells_processed = 0;
+
/* These are the main functions for processing cells */
static void command_process_create_cell(cell_t *cell, or_connection_t *conn);
static void command_process_created_cell(cell_t *cell, or_connection_t *conn);
@@ -53,6 +63,12 @@ static void command_process_destroy_cell(cell_t *cell, or_connection_t *conn);
static void command_process_versions_cell(var_cell_t *cell,
or_connection_t *conn);
static void command_process_netinfo_cell(cell_t *cell, or_connection_t *conn);
+static void command_process_cert_cell(var_cell_t *cell,
+ or_connection_t *conn);
+static void command_process_auth_challenge_cell(var_cell_t *cell,
+ or_connection_t *conn);
+static void command_process_authenticate_cell(var_cell_t *cell,
+ or_connection_t *conn);
#ifdef KEEP_TIMING_STATS
/** This is a wrapper function around the actual function that processes the
@@ -92,7 +108,7 @@ command_time_process_cell(cell_t *cell, or_connection_t *conn, int *time,
void
command_process_cell(cell_t *cell, or_connection_t *conn)
{
- int handshaking = (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING);
+ int handshaking = (conn->_base.state != OR_CONN_STATE_OPEN);
#ifdef KEEP_TIMING_STATS
/* how many of each cell have we seen so far this second? needs better
* name. */
@@ -133,9 +149,18 @@ command_process_cell(cell_t *cell, or_connection_t *conn)
#endif
/* Reject all but VERSIONS and NETINFO when handshaking. */
+ /* (VERSIONS should actually be impossible; it's variable-length.) */
if (handshaking && cell->command != CELL_VERSIONS &&
- cell->command != CELL_NETINFO)
+ cell->command != CELL_NETINFO) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received unexpected cell command %d in state %s; ignoring it.",
+ (int)cell->command,
+ conn_state_to_string(CONN_TYPE_OR,conn->_base.state));
return;
+ }
+
+ if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
+ or_handshake_state_record_cell(conn->handshake_state, cell, 1);
switch (cell->command) {
case CELL_PADDING:
@@ -205,20 +230,79 @@ command_process_var_cell(var_cell_t *cell, or_connection_t *conn)
}
#endif
- /* reject all when not handshaking. */
- if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING)
- return;
+ switch (conn->_base.state)
+ {
+ case OR_CONN_STATE_OR_HANDSHAKING_V2:
+ if (cell->command != CELL_VERSIONS)
+ return;
+ break;
+ case OR_CONN_STATE_TLS_HANDSHAKING:
+ /* If we're using bufferevents, it's entirely possible for us to
+ * notice "hey, data arrived!" before we notice "hey, the handshake
+ * finished!" And we need to be accepting both at once to handle both
+ * the v2 and v3 handshakes. */
+
+ /* fall through */
+ case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
+ if (cell->command != CELL_VERSIONS) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received a non-VERSIONS cell with command %d in state %s; "
+ "ignoring it.",
+ (int)cell->command,
+ conn_state_to_string(CONN_TYPE_OR,conn->_base.state));
+ return;
+ }
+ break;
+ case OR_CONN_STATE_OR_HANDSHAKING_V3:
+ if (cell->command != CELL_AUTHENTICATE)
+ or_handshake_state_record_var_cell(conn->handshake_state, cell, 1);
+ break; /* Everything is allowed */
+ case OR_CONN_STATE_OPEN:
+ if (conn->link_proto < 3) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received a variable-length cell with command %d in state %s "
+ "with link protocol %d; ignoring it.",
+ (int)cell->command,
+ conn_state_to_string(CONN_TYPE_OR,conn->_base.state),
+ (int)conn->link_proto);
+ return;
+ }
+ break;
+ default:
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received var-length cell with command %d in unexpected state "
+ "%s [%d]; ignoring it.",
+ (int)cell->command,
+ conn_state_to_string(CONN_TYPE_OR,conn->_base.state),
+ (int)conn->_base.state);
+ return;
+ }
switch (cell->command) {
case CELL_VERSIONS:
++stats_n_versions_cells_processed;
PROCESS_CELL(versions, cell, conn);
break;
+ case CELL_VPADDING:
+ ++stats_n_vpadding_cells_processed;
+ /* Do nothing */
+ break;
+ case CELL_CERT:
+ ++stats_n_cert_cells_processed;
+ PROCESS_CELL(cert, cell, conn);
+ break;
+ case CELL_AUTH_CHALLENGE:
+ ++stats_n_auth_challenge_cells_processed;
+ PROCESS_CELL(auth_challenge, cell, conn);
+ break;
+ case CELL_AUTHENTICATE:
+ ++stats_n_authenticate_cells_processed;
+ PROCESS_CELL(authenticate, cell, conn);
+ break;
default:
- log_warn(LD_BUG,
+ log_fn(LOG_INFO, LD_PROTOCOL,
"Variable-length cell of unknown type (%d) received.",
cell->command);
- tor_fragile_assert();
break;
}
}
@@ -232,7 +316,7 @@ static void
command_process_create_cell(cell_t *cell, or_connection_t *conn)
{
or_circuit_t *circ;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int id_is_high;
if (we_are_hibernating()) {
@@ -270,15 +354,18 @@ command_process_create_cell(cell_t *cell, or_connection_t *conn)
}
if (circuit_id_in_use_on_orconn(cell->circ_id, conn)) {
- routerinfo_t *router = router_get_by_digest(conn->identity_digest);
+ const node_t *node = node_get_by_id(conn->identity_digest);
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received CREATE cell (circID %d) for known circ. "
"Dropping (age %d).",
cell->circ_id, (int)(time(NULL) - conn->_base.timestamp_created));
- if (router)
+ if (node) {
+ char *p = esc_for_log(node_get_platform(node));
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Details: router %s, platform %s.",
- router_describe(router), escaped(router->platform));
+ node_describe(node), p);
+ tor_free(p);
+ }
return;
}
@@ -510,14 +597,40 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn)
{
int highest_supported_version = 0;
const uint8_t *cp, *end;
+ const int started_here = connection_or_nonopen_was_started_here(conn);
if (conn->link_proto != 0 ||
- conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING ||
(conn->handshake_state && conn->handshake_state->received_versions)) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Received a VERSIONS cell on a connection with its version "
"already set to %d; dropping", (int) conn->link_proto);
return;
}
+ switch (conn->_base.state)
+ {
+ case OR_CONN_STATE_OR_HANDSHAKING_V2:
+ break;
+ case OR_CONN_STATE_TLS_HANDSHAKING:
+ case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
+ if (started_here) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR,
+ "Received a versions cell while TLS-handshaking not in "
+ "OR_HANDSHAKING_V3 on a connection we originated.");
+ }
+ conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
+ if (connection_init_or_handshake_state(conn, started_here) < 0) {
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ or_handshake_state_record_var_cell(conn->handshake_state, cell, 1);
+ break;
+ case OR_CONN_STATE_OR_HANDSHAKING_V3:
+ break;
+ default:
+ log_fn(LOG_PROTOCOL_WARN, LD_OR,
+ "VERSIONS cell while in unexpected state");
+ return;
+ }
+
tor_assert(conn->handshake_state);
end = cell->payload + cell->payload_len;
for (cp = cell->payload; cp+1 < end; ++cp) {
@@ -539,19 +652,87 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn)
"That's crazily non-compliant. Closing connection.");
connection_mark_for_close(TO_CONN(conn));
return;
+ } else if (highest_supported_version < 3 &&
+ conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR,
+ "Negotiated link protocol 2 or lower after doing a v3 TLS "
+ "handshake. Closing connection.");
+ connection_mark_for_close(TO_CONN(conn));
+ return;
}
+
conn->link_proto = highest_supported_version;
conn->handshake_state->received_versions = 1;
- log_info(LD_OR, "Negotiated version %d with %s:%d; sending NETINFO.",
- highest_supported_version,
- safe_str_client(conn->_base.address),
- conn->_base.port);
- tor_assert(conn->link_proto >= 2);
+ if (conn->link_proto == 2) {
+ log_info(LD_OR, "Negotiated version %d with %s:%d; sending NETINFO.",
+ highest_supported_version,
+ safe_str_client(conn->_base.address),
+ conn->_base.port);
- if (connection_or_send_netinfo(conn) < 0) {
- connection_mark_for_close(TO_CONN(conn));
- return;
+ if (connection_or_send_netinfo(conn) < 0) {
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ } else {
+ const int send_versions = !started_here;
+ /* If we want to authenticate, send a CERTS cell */
+ const int send_certs = !started_here || public_server_mode(get_options());
+ /* If we're a relay that got a connection, ask for authentication. */
+ const int send_chall = !started_here && public_server_mode(get_options());
+ /* If our certs cell will authenticate us, or if we have no intention of
+ * authenticating, send a netinfo cell right now. */
+ const int send_netinfo =
+ !(started_here && public_server_mode(get_options()));
+ const int send_any =
+ send_versions || send_certs || send_chall || send_netinfo;
+ tor_assert(conn->link_proto >= 3);
+
+ log_info(LD_OR, "Negotiated version %d with %s:%d; %s%s%s%s%s",
+ highest_supported_version,
+ safe_str_client(conn->_base.address),
+ conn->_base.port,
+ send_any ? "Sending cells:" : "Waiting for CERTS cell",
+ send_versions ? " VERSIONS" : "",
+ send_certs ? " CERTS" : "",
+ send_chall ? " AUTH_CHALLENGE" : "",
+ send_netinfo ? " NETINFO" : "");
+
+#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
+ if (1) {
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+#endif
+
+ if (send_versions) {
+ if (connection_or_send_versions(conn, 1) < 0) {
+ log_warn(LD_OR, "Couldn't send versions cell");
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ }
+ if (send_certs) {
+ if (connection_or_send_cert_cell(conn) < 0) {
+ log_warn(LD_OR, "Couldn't send cert cell");
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ }
+ if (send_chall) {
+ if (connection_or_send_auth_challenge_cell(conn) < 0) {
+ log_warn(LD_OR, "Couldn't send auth_challenge cell");
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ }
+ if (send_netinfo) {
+ if (connection_or_send_netinfo(conn) < 0) {
+ log_warn(LD_OR, "Couldn't send netinfo cell");
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ }
}
}
@@ -577,13 +758,41 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn)
conn->link_proto == 0 ? "non-versioned" : "a v1");
return;
}
- if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING) {
+ if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2 &&
+ conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Received a NETINFO cell on non-handshaking connection; dropping.");
return;
}
tor_assert(conn->handshake_state &&
conn->handshake_state->received_versions);
+
+ if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) {
+ tor_assert(conn->link_proto >= 3);
+ if (conn->handshake_state->started_here) {
+ if (!conn->handshake_state->authenticated) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got a NETINFO cell from server, "
+ "but no authentication. Closing the connection.");
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ } else {
+ /* we're the server. If the client never authenticated, we have
+ some housekeeping to do.*/
+ if (!conn->handshake_state->authenticated) {
+ tor_assert(tor_digest_is_zero(
+ (const char*)conn->handshake_state->authenticated_peer_id));
+ connection_or_set_circid_type(conn, NULL);
+
+ connection_or_init_conn_from_address(conn,
+ &conn->_base.addr,
+ conn->_base.port,
+ (const char*)conn->handshake_state->authenticated_peer_id,
+ 0);
+ }
+ }
+ }
+
/* Decode the cell. */
timestamp = ntohl(get_uint32(cell->payload));
if (labs(now - conn->handshake_state->sent_versions_at) < 180) {
@@ -629,7 +838,7 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn)
/** Warn when we get a netinfo skew with at least this value. */
#define NETINFO_NOTICE_SKEW 3600
if (labs(apparent_skew) > NETINFO_NOTICE_SKEW &&
- router_get_by_digest(conn->identity_digest)) {
+ router_get_by_id_digest(conn->identity_digest)) {
char dbuf[64];
int severity;
/*XXXX be smarter about when everybody says we are skewed. */
@@ -656,13 +865,391 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn)
* trustworthy. */
(void)my_apparent_addr;
- if (connection_or_set_state_open(conn)<0)
+ if (connection_or_set_state_open(conn)<0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got good NETINFO cell from %s:%d; but "
+ "was unable to make the OR connection become open.",
+ safe_str_client(conn->_base.address),
+ conn->_base.port);
connection_mark_for_close(TO_CONN(conn));
- else
+ } else {
log_info(LD_OR, "Got good NETINFO cell from %s:%d; OR connection is now "
- "open, using protocol version %d",
+ "open, using protocol version %d. Its ID digest is %s",
safe_str_client(conn->_base.address),
- conn->_base.port, (int)conn->link_proto);
+ conn->_base.port, (int)conn->link_proto,
+ hex_str(conn->identity_digest, DIGEST_LEN));
+ }
assert_connection_ok(TO_CONN(conn),time(NULL));
}
+/** Process a CERT cell from an OR connection.
+ *
+ * If the other side should not have sent us a CERT cell, or the cell is
+ * malformed, or it is supposed to authenticate the TLS key but it doesn't,
+ * then mark the connection.
+ *
+ * If the cell has a good cert chain and we're doing a v3 handshake, then
+ * store the certificates in or_handshake_state. If this is the client side
+ * of the connection, we then authenticate the server or mark the connection.
+ * If it's the server side, wait for an AUTHENTICATE cell.
+ */
+static void
+command_process_cert_cell(var_cell_t *cell, or_connection_t *conn)
+{
+#define ERR(s) \
+ do { \
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \
+ "Received a bad CERT cell from %s:%d: %s", \
+ safe_str(conn->_base.address), conn->_base.port, (s)); \
+ connection_mark_for_close(TO_CONN(conn)); \
+ goto err; \
+ } while (0)
+
+ tor_cert_t *link_cert = NULL;
+ tor_cert_t *id_cert = NULL;
+ tor_cert_t *auth_cert = NULL;
+
+ uint8_t *ptr;
+ int n_certs, i;
+
+ if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
+ ERR("We're not doing a v3 handshake!");
+ if (conn->link_proto < 3)
+ ERR("We're not using link protocol >= 3");
+ if (conn->handshake_state->received_cert_cell)
+ ERR("We already got one");
+ if (conn->handshake_state->authenticated) {
+ /* Should be unreachable, but let's make sure. */
+ ERR("We're already authenticated!");
+ }
+ if (cell->payload_len < 1)
+ ERR("It had no body");
+ if (cell->circ_id)
+ ERR("It had a nonzero circuit ID");
+
+ n_certs = cell->payload[0];
+ ptr = cell->payload + 1;
+ for (i = 0; i < n_certs; ++i) {
+ uint8_t cert_type;
+ uint16_t cert_len;
+ if (ptr + 3 > cell->payload + cell->payload_len) {
+ goto truncated;
+ }
+ cert_type = *ptr;
+ cert_len = ntohs(get_uint16(ptr+1));
+ if (ptr + 3 + cert_len > cell->payload + cell->payload_len) {
+ goto truncated;
+ }
+ if (cert_type == OR_CERT_TYPE_TLS_LINK ||
+ cert_type == OR_CERT_TYPE_ID_1024 ||
+ cert_type == OR_CERT_TYPE_AUTH_1024) {
+ tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len);
+ if (!cert) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received undecodable certificate in CERT cell from %s:%d",
+ safe_str(conn->_base.address), conn->_base.port);
+ } else {
+ if (cert_type == OR_CERT_TYPE_TLS_LINK) {
+ if (link_cert) {
+ tor_cert_free(cert);
+ ERR("Too many TLS_LINK certificates");
+ }
+ link_cert = cert;
+ } else if (cert_type == OR_CERT_TYPE_ID_1024) {
+ if (id_cert) {
+ tor_cert_free(cert);
+ ERR("Too many ID_1024 certificates");
+ }
+ id_cert = cert;
+ } else if (cert_type == OR_CERT_TYPE_AUTH_1024) {
+ if (auth_cert) {
+ tor_cert_free(cert);
+ ERR("Too many AUTH_1024 certificates");
+ }
+ auth_cert = cert;
+ } else {
+ tor_cert_free(cert);
+ }
+ }
+ }
+ ptr += 3 + cert_len;
+ continue;
+
+ truncated:
+ ERR("It ends in the middle of a certificate");
+ }
+
+ if (conn->handshake_state->started_here) {
+ if (! (id_cert && link_cert))
+ ERR("The certs we wanted were missing");
+ /* Okay. We should be able to check the certificates now. */
+ if (! tor_tls_cert_matches_key(conn->tls, link_cert)) {
+ ERR("The link certificate didn't match the TLS public key");
+ }
+ if (! tor_tls_cert_is_valid(link_cert, id_cert, 0))
+ ERR("The link certificate was not valid");
+ if (! tor_tls_cert_is_valid(id_cert, id_cert, 1))
+ ERR("The ID certificate was not valid");
+
+ conn->handshake_state->authenticated = 1;
+ {
+ const digests_t *id_digests = tor_cert_get_id_digests(id_cert);
+ crypto_pk_env_t *identity_rcvd;
+ if (!id_digests)
+ ERR("Couldn't compute digests for key in ID cert");
+
+ identity_rcvd = tor_tls_cert_get_key(id_cert);
+ if (!identity_rcvd)
+ ERR("Internal error: Couldn't get RSA key from ID cert.");
+ memcpy(conn->handshake_state->authenticated_peer_id,
+ id_digests->d[DIGEST_SHA1], DIGEST_LEN);
+ connection_or_set_circid_type(conn, identity_rcvd);
+ crypto_free_pk_env(identity_rcvd);
+ }
+
+ if (connection_or_client_learned_peer_id(conn,
+ conn->handshake_state->authenticated_peer_id) < 0)
+ ERR("Problem setting or checking peer id");
+
+ log_info(LD_OR, "Got some good certificates from %s:%d: Authenticated it.",
+ safe_str(conn->_base.address), conn->_base.port);
+
+ conn->handshake_state->id_cert = id_cert;
+ id_cert = NULL;
+ } else {
+ if (! (id_cert && auth_cert))
+ ERR("The certs we wanted were missing");
+
+ /* Remember these certificates so we can check an AUTHENTICATE cell */
+ conn->handshake_state->id_cert = id_cert;
+ conn->handshake_state->auth_cert = auth_cert;
+ if (! tor_tls_cert_is_valid(auth_cert, id_cert, 1))
+ ERR("The authentication certificate was not valid");
+ if (! tor_tls_cert_is_valid(id_cert, id_cert, 1))
+ ERR("The ID certificate was not valid");
+
+ log_info(LD_OR, "Got some good certificates from %s:%d: "
+ "Waiting for AUTHENTICATE.",
+ safe_str(conn->_base.address), conn->_base.port);
+ /* XXXX check more stuff? */
+
+ id_cert = auth_cert = NULL;
+ }
+
+ conn->handshake_state->received_cert_cell = 1;
+ err:
+ tor_cert_free(id_cert);
+ tor_cert_free(link_cert);
+ tor_cert_free(auth_cert);
+#undef ERR
+}
+
+/** Process an AUTH_CHALLENGE cell from an OR connection.
+ *
+ * If we weren't supposed to get one (for example, because we're not the
+ * originator of the connection), or it's ill-formed, or we aren't doing a v3
+ * handshake, mark the connection. If the cell is well-formed but we don't
+ * want to authenticate, just drop it. If the cell is well-formed *and* we
+ * want to authenticate, send an AUTHENTICATE cell. */
+static void
+command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn)
+{
+ int n_types, i, use_type = -1;
+ uint8_t *cp;
+
+#define ERR(s) \
+ do { \
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \
+ "Received a bad AUTH_CHALLENGE cell from %s:%d: %s", \
+ safe_str(conn->_base.address), conn->_base.port, (s)); \
+ connection_mark_for_close(TO_CONN(conn)); \
+ return; \
+ } while (0)
+
+ if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
+ ERR("We're not currently doing a v3 handshake");
+ if (conn->link_proto < 3)
+ ERR("We're not using link protocol >= 3");
+ if (! conn->handshake_state->started_here)
+ ERR("We didn't originate this connection");
+ if (conn->handshake_state->received_auth_challenge)
+ ERR("We already received one");
+ if (! conn->handshake_state->received_cert_cell)
+ ERR("We haven't gotten a CERTS cell yet");
+ if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2)
+ ERR("It was too short");
+ if (cell->circ_id)
+ ERR("It had a nonzero circuit ID");
+
+ n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN));
+ if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types)
+ ERR("It looks truncated");
+
+ /* Now see if there is an authentication type we can use */
+ cp=cell->payload+OR_AUTH_CHALLENGE_LEN+2;
+ for (i=0; i < n_types; ++i, cp += 2) {
+ uint16_t authtype = ntohs(get_uint16(cp));
+ if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET)
+ use_type = authtype;
+ }
+
+ conn->handshake_state->received_auth_challenge = 1;
+
+ if (use_type && public_server_mode(get_options())) {
+ log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Sending "
+ "authentication",
+ safe_str(conn->_base.address), conn->_base.port);
+
+ if (connection_or_send_authenticate_cell(conn, use_type) < 0) {
+ log_warn(LD_OR, "Couldn't send authenticate cell");
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ if (connection_or_send_netinfo(conn) < 0) {
+ log_warn(LD_OR, "Couldn't send netinfo cell");
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ } else {
+ log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Not "
+ "authenticating",
+ safe_str(conn->_base.address), conn->_base.port);
+ }
+#undef ERR
+}
+
+/** Process an AUTHENTICATE cell from an OR connection.
+ *
+ * If it's ill-formed or we weren't supposed to get one or we're not doing a
+ * v3 handshake, then mark the connection. If it does not authenticate the
+ * other side of the connection successfully (because it isn't signed right,
+ * we didn't get a CERT cell, etc) mark the connection. Otherwise, accept
+ * the identity of the router on the other side of the connection.
+ */
+static void
+command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn)
+{
+ uint8_t expected[V3_AUTH_FIXED_PART_LEN];
+ const uint8_t *auth;
+ int authlen;
+
+#define ERR(s) \
+ do { \
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \
+ "Received a bad AUTHETNICATE cell from %s:%d: %s", \
+ safe_str(conn->_base.address), conn->_base.port, (s)); \
+ connection_mark_for_close(TO_CONN(conn)); \
+ return; \
+ } while (0)
+
+ if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
+ ERR("We're not doing a v3 handshake");
+ if (conn->link_proto < 3)
+ ERR("We're not using link protocol >= 3");
+ if (conn->handshake_state->started_here)
+ ERR("We originated this connection");
+ if (conn->handshake_state->received_authenticate)
+ ERR("We already got one!");
+ if (conn->handshake_state->authenticated) {
+ /* Should be impossible given other checks */
+ ERR("The peer is already authenticated");
+ }
+ if (! conn->handshake_state->received_cert_cell)
+ ERR("We never got a cert cell");
+ if (conn->handshake_state->auth_cert == NULL)
+ ERR("We never got an authentication certificate");
+ if (conn->handshake_state->id_cert == NULL)
+ ERR("We never got an identity certificate");
+ if (cell->payload_len < 4)
+ ERR("Cell was way too short");
+
+ auth = cell->payload;
+ {
+ uint16_t type = ntohs(get_uint16(auth));
+ uint16_t len = ntohs(get_uint16(auth+2));
+ if (4 + len > cell->payload_len)
+ ERR("Authenticator was truncated");
+
+ if (type != AUTHTYPE_RSA_SHA256_TLSSECRET)
+ ERR("Authenticator type was not recognized");
+
+ auth += 4;
+ authlen = len;
+ }
+
+ if (authlen < V3_AUTH_BODY_LEN + 1)
+ ERR("Authenticator was too short");
+
+ if (connection_or_compute_authenticate_cell_body(
+ conn, expected, sizeof(expected), NULL, 1) < 0)
+ ERR("Couldn't compute expected AUTHENTICATE cell body");
+
+ if (tor_memneq(expected, auth, sizeof(expected)))
+ ERR("Some field in the AUTHENTICATE cell body was not as expected");
+
+ {
+ crypto_pk_env_t *pk = tor_tls_cert_get_key(
+ conn->handshake_state->auth_cert);
+ char d[DIGEST256_LEN];
+ char *signed_data;
+ size_t keysize;
+ int signed_len;
+
+ if (!pk)
+ ERR("Internal error: couldn't get RSA key from AUTH cert.");
+ crypto_digest256(d, (char*)auth, V3_AUTH_BODY_LEN, DIGEST_SHA256);
+
+ keysize = crypto_pk_keysize(pk);
+ signed_data = tor_malloc(keysize);
+ signed_len = crypto_pk_public_checksig(pk, signed_data, keysize,
+ (char*)auth + V3_AUTH_BODY_LEN,
+ authlen - V3_AUTH_BODY_LEN);
+ crypto_free_pk_env(pk);
+ if (signed_len < 0) {
+ tor_free(signed_data);
+ ERR("Signature wasn't valid");
+ }
+ if (signed_len < DIGEST256_LEN) {
+ tor_free(signed_data);
+ ERR("Not enough data was signed");
+ }
+ /* Note that we deliberately allow *more* than DIGEST256_LEN bytes here,
+ * in case they're later used to hold a SHA3 digest or something. */
+ if (tor_memneq(signed_data, d, DIGEST256_LEN)) {
+ tor_free(signed_data);
+ ERR("Signature did not match data to be signed.");
+ }
+ tor_free(signed_data);
+ }
+
+ /* Okay, we are authenticated. */
+ conn->handshake_state->received_authenticate = 1;
+ conn->handshake_state->authenticated = 1;
+ conn->handshake_state->digest_received_data = 0;
+ {
+ crypto_pk_env_t *identity_rcvd =
+ tor_tls_cert_get_key(conn->handshake_state->id_cert);
+ const digests_t *id_digests =
+ tor_cert_get_id_digests(conn->handshake_state->id_cert);
+
+ /* This must exist; we checked key type when reading the cert. */
+ tor_assert(id_digests);
+
+ memcpy(conn->handshake_state->authenticated_peer_id,
+ id_digests->d[DIGEST_SHA1], DIGEST_LEN);
+
+ connection_or_set_circid_type(conn, identity_rcvd);
+ crypto_free_pk_env(identity_rcvd);
+
+ connection_or_init_conn_from_address(conn,
+ &conn->_base.addr,
+ conn->_base.port,
+ (const char*)conn->handshake_state->authenticated_peer_id,
+ 0);
+
+ log_info(LD_OR, "Got an AUTHENTICATE cell from %s:%d: Looks good.",
+ safe_str(conn->_base.address), conn->_base.port);
+ }
+
+#undef ERR
+}
+
diff --git a/src/or/config.c b/src/or/config.c
index 78e433620..1b9f9fb47 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -33,13 +33,18 @@
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
+#include "util.h"
#include "routerlist.h"
+#include "transports.h"
#ifdef MS_WINDOWS
#include <shlobj.h>
#endif
#include "procmon.h"
+/* From main.c */
+extern int quiet_level;
+
/** Enumeration of types which option values can take */
typedef enum config_type_t {
CONFIG_TYPE_STRING = 0, /**< An arbitrary string. */
@@ -48,9 +53,13 @@ typedef enum config_type_t {
CONFIG_TYPE_PORT, /**< A port from 1...65535, 0 for "not set", or
* "auto". */
CONFIG_TYPE_INTERVAL, /**< A number of seconds, with optional units*/
+ CONFIG_TYPE_MSEC_INTERVAL,/**< A number of milliseconds, with optional
+ * units */
CONFIG_TYPE_MEMUNIT, /**< A number of bytes, with optional units*/
CONFIG_TYPE_DOUBLE, /**< A floating-point value */
CONFIG_TYPE_BOOL, /**< A boolean value, expressed as 0 or 1. */
+ CONFIG_TYPE_AUTOBOOL, /**< A boolean+auto value, expressed 0 for false,
+ * 1 for true, and -1 for auto */
CONFIG_TYPE_ISOTIME, /**< An ISO-formatted time relative to GMT. */
CONFIG_TYPE_CSV, /**< A list of strings, separated by commas and
* optional whitespace. */
@@ -199,10 +208,12 @@ static config_var_t _option_vars[] = {
V(CircuitStreamTimeout, INTERVAL, "0"),
V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/
V(ClientDNSRejectInternalAddresses, BOOL,"1"),
- V(ClientRejectInternalAddresses, BOOL, "1"),
V(ClientOnly, BOOL, "0"),
+ V(ClientRejectInternalAddresses, BOOL, "1"),
+ V(ClientTransportPlugin, LINELIST, NULL),
V(ConsensusParams, STRING, NULL),
V(ConnLimit, UINT, "1000"),
+ V(ConnDirectionStatistics, BOOL, "0"),
V(ConstrainedSockets, BOOL, "0"),
V(ConstrainedSockSize, MEMUNIT, "8192"),
V(ContactInfo, STRING, NULL),
@@ -215,6 +226,7 @@ static config_var_t _option_vars[] = {
V(CookieAuthentication, BOOL, "0"),
V(CookieAuthFileGroupReadable, BOOL, "0"),
V(CookieAuthFile, STRING, NULL),
+ V(CountPrivateBandwidth, BOOL, "0"),
V(DataDirectory, FILENAME, NULL),
OBSOLETE("DebugLogFile"),
V(DirAllowPrivateAddresses, BOOL, NULL),
@@ -232,7 +244,8 @@ static config_var_t _option_vars[] = {
V(DirReqStatistics, BOOL, "1"),
VAR("DirServer", LINELIST, DirServers, NULL),
V(DisableAllSwap, BOOL, "0"),
- V(DNSPort, PORT, "0"),
+ V(DisableIOCP, BOOL, "1"),
+ V(DNSPort, LINELIST, NULL),
V(DNSListenAddress, LINELIST, NULL),
V(DownloadExtraInfo, BOOL, "0"),
V(EnforceDistinctSubnets, BOOL, "1"),
@@ -273,6 +286,7 @@ static config_var_t _option_vars[] = {
BOOL, "0"),
OBSOLETE("Group"),
V(HardwareAccel, BOOL, "0"),
+ V(HeartbeatPeriod, INTERVAL, "6 hours"),
V(AccelName, STRING, NULL),
V(AccelDir, FILENAME, NULL),
V(HashedControlPassword, LINELIST, NULL),
@@ -291,6 +305,7 @@ static config_var_t _option_vars[] = {
V(HTTPProxyAuthenticator, STRING, NULL),
V(HTTPSProxy, STRING, NULL),
V(HTTPSProxyAuthenticator, STRING, NULL),
+ VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL),
V(Socks4Proxy, STRING, NULL),
V(Socks5Proxy, STRING, NULL),
V(Socks5ProxyUsername, STRING, NULL),
@@ -302,23 +317,25 @@ static config_var_t _option_vars[] = {
OBSOLETE("LinkPadding"),
OBSOLETE("LogLevel"),
OBSOLETE("LogFile"),
+ V(LogTimeGranularity, MSEC_INTERVAL, "1 second"),
V(LongLivedPorts, CSV,
- "21,22,706,1863,5050,5190,5222,5223,6667,6697,8300"),
+ "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"),
VAR("MapAddress", LINELIST, AddressMap, NULL),
V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"),
V(MaxCircuitDirtiness, INTERVAL, "10 minutes"),
+ V(MaxClientCircuitsPending, UINT, "32"),
V(MaxOnionsPending, UINT, "100"),
OBSOLETE("MonthlyAccountingStart"),
V(MyFamily, STRING, NULL),
V(NewCircuitPeriod, INTERVAL, "30 seconds"),
VAR("NamingAuthoritativeDirectory",BOOL, NamingAuthoritativeDir, "0"),
V(NATDListenAddress, LINELIST, NULL),
- V(NATDPort, PORT, "0"),
+ V(NATDPort, LINELIST, NULL),
V(Nickname, STRING, NULL),
V(WarnUnsafeSocks, BOOL, "1"),
OBSOLETE("NoPublish"),
VAR("NodeFamily", LINELIST, NodeFamilies, NULL),
- V(NumCPUs, UINT, "1"),
+ V(NumCPUs, UINT, "0"),
V(NumEntryGuards, UINT, "3"),
V(ORListenAddress, LINELIST, NULL),
V(ORPort, PORT, "0"),
@@ -328,6 +345,9 @@ static config_var_t _option_vars[] = {
V(PerConnBWRate, MEMUNIT, "0"),
V(PidFile, STRING, NULL),
V(TestingTorNetwork, BOOL, "0"),
+ V(OptimisticData, AUTOBOOL, "auto"),
+ V(PortForwarding, BOOL, "0"),
+ V(PortForwardingHelper, FILENAME, "tor-fw-helper"),
V(PreferTunneledDirConns, BOOL, "1"),
V(ProtocolWarnings, BOOL, "0"),
V(PublishServerDescriptor, CSV, "1"),
@@ -339,7 +359,7 @@ static config_var_t _option_vars[] = {
V(RecommendedClientVersions, LINELIST, NULL),
V(RecommendedServerVersions, LINELIST, NULL),
OBSOLETE("RedirectExit"),
- V(RefuseUnknownExits, STRING, "auto"),
+ V(RefuseUnknownExits, AUTOBOOL, "auto"),
V(RejectPlaintextPorts, CSV, ""),
V(RelayBandwidthBurst, MEMUNIT, "0"),
V(RelayBandwidthRate, MEMUNIT, "0"),
@@ -364,22 +384,24 @@ static config_var_t _option_vars[] = {
V(ShutdownWaitLength, INTERVAL, "30 seconds"),
V(SocksListenAddress, LINELIST, NULL),
V(SocksPolicy, LINELIST, NULL),
- V(SocksPort, PORT, "9050"),
+ V(SocksPort, LINELIST, NULL),
V(SocksTimeout, INTERVAL, "2 minutes"),
OBSOLETE("StatusFetchPeriod"),
V(StrictNodes, BOOL, "0"),
OBSOLETE("SysLog"),
V(TestSocks, BOOL, "0"),
OBSOLETE("TestVia"),
+ V(TokenBucketRefillInterval, MSEC_INTERVAL, "100 msec"),
V(TrackHostExits, CSV, NULL),
V(TrackHostExitsExpire, INTERVAL, "30 minutes"),
OBSOLETE("TrafficShaping"),
V(TransListenAddress, LINELIST, NULL),
- V(TransPort, PORT, "0"),
+ V(TransPort, LINELIST, NULL),
V(TunnelDirConns, BOOL, "1"),
V(UpdateBridgesFromAuthority, BOOL, "0"),
V(UseBridges, BOOL, "0"),
V(UseEntryGuards, BOOL, "1"),
+ V(UseMicrodescriptors, AUTOBOOL, "auto"),
V(User, STRING, NULL),
VAR("V1AuthoritativeDirectory",BOOL, V1AuthoritativeDir, "0"),
VAR("V2AuthoritativeDirectory",BOOL, V2AuthoritativeDir, "0"),
@@ -396,6 +418,7 @@ static config_var_t _option_vars[] = {
VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
V(VirtualAddrNetwork, STRING, "127.192.0.0/10"),
V(WarnPlaintextPorts, CSV, "23,109,110,143"),
+ V(_UseFilteringSSLBufferevents, BOOL, "0"),
VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"),
VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"),
VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"),
@@ -412,7 +435,7 @@ static config_var_t _option_vars[] = {
/** Override default values with these if the user sets the TestingTorNetwork
* option. */
-static config_var_t testing_tor_network_defaults[] = {
+static const config_var_t testing_tor_network_defaults[] = {
V(ServerDNSAllowBrokenConfig, BOOL, "1"),
V(DirAllowPrivateAddresses, BOOL, "1"),
V(EnforceDistinctSubnets, BOOL, "0"),
@@ -421,6 +444,7 @@ static config_var_t testing_tor_network_defaults[] = {
V(AuthDirMaxServersPerAuthAddr,UINT, "0"),
V(ClientDNSRejectInternalAddresses, BOOL,"0"),
V(ClientRejectInternalAddresses, BOOL, "0"),
+ V(CountPrivateBandwidth, BOOL, "1"),
V(ExitPolicyRejectPrivate, BOOL, "0"),
V(V3AuthVotingInterval, INTERVAL, "5 minutes"),
V(V3AuthVoteDelay, INTERVAL, "20 seconds"),
@@ -432,6 +456,7 @@ static config_var_t testing_tor_network_defaults[] = {
V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
V(_UsingTestNetworkDefaults, BOOL, "1"),
+
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
};
#undef VAR
@@ -457,6 +482,9 @@ static config_var_t _state_vars[] = {
VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL),
V(EntryGuards, LINELIST_V, NULL),
+ VAR("TransportProxy", LINELIST_S, TransportProxies, NULL),
+ V(TransportProxies, LINELIST_V, NULL),
+
V(BWHistoryReadEnds, ISOTIME, NULL),
V(BWHistoryReadInterval, UINT, "900"),
V(BWHistoryReadValues, CSV, ""),
@@ -483,7 +511,6 @@ static config_var_t _state_vars[] = {
V(CircuitBuildAbandonedCount, UINT, "0"),
VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL),
VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL),
-
{ NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
};
@@ -534,39 +561,48 @@ static char *get_windows_conf_root(void);
#endif
static void config_line_append(config_line_t **lst,
const char *key, const char *val);
-static void option_clear(config_format_t *fmt, or_options_t *options,
- config_var_t *var);
-static void option_reset(config_format_t *fmt, or_options_t *options,
- config_var_t *var, int use_defaults);
-static void config_free(config_format_t *fmt, void *options);
+static void option_clear(const config_format_t *fmt, or_options_t *options,
+ const config_var_t *var);
+static void option_reset(const config_format_t *fmt, or_options_t *options,
+ const config_var_t *var, int use_defaults);
+static void config_free(const config_format_t *fmt, void *options);
static int config_lines_eq(config_line_t *a, config_line_t *b);
-static int option_is_same(config_format_t *fmt,
- or_options_t *o1, or_options_t *o2,
+static int option_is_same(const config_format_t *fmt,
+ const or_options_t *o1, const or_options_t *o2,
const char *name);
-static or_options_t *options_dup(config_format_t *fmt, or_options_t *old);
-static int options_validate(or_options_t *old_options, or_options_t *options,
+static or_options_t *options_dup(const config_format_t *fmt,
+ const or_options_t *old);
+static int options_validate(or_options_t *old_options,
+ or_options_t *options,
int from_setconf, char **msg);
-static int options_act_reversible(or_options_t *old_options, char **msg);
-static int options_act(or_options_t *old_options);
-static int options_transition_allowed(or_options_t *old, or_options_t *new,
+static int options_act_reversible(const or_options_t *old_options, char **msg);
+static int options_act(const or_options_t *old_options);
+static int options_transition_allowed(const or_options_t *old,
+ const or_options_t *new,
char **msg);
-static int options_transition_affects_workers(or_options_t *old_options,
- or_options_t *new_options);
-static int options_transition_affects_descriptor(or_options_t *old_options,
- or_options_t *new_options);
+static int options_transition_affects_workers(
+ const or_options_t *old_options, const or_options_t *new_options);
+static int options_transition_affects_descriptor(
+ const or_options_t *old_options, const or_options_t *new_options);
static int check_nickname_list(const char *lst, const char *name, char **msg);
-static void config_register_addressmaps(or_options_t *options);
static int parse_bridge_line(const char *line, int validate_only);
+static int parse_client_transport_line(const char *line, int validate_only);
+
+static int parse_server_transport_line(const char *line, int validate_only);
static int parse_dir_server_line(const char *line,
- authority_type_t required_type,
+ dirinfo_type_t required_type,
int validate_only);
+static void port_cfg_free(port_cfg_t *port);
+static int parse_client_ports(const or_options_t *options, int validate_only,
+ char **msg_out, int *n_ports_out);
static int validate_data_directory(or_options_t *options);
-static int write_configuration_file(const char *fname, or_options_t *options);
-static config_line_t *get_assigned_option(config_format_t *fmt,
- void *options, const char *key,
- int escape_val);
-static void config_init(config_format_t *fmt, void *options);
+static int write_configuration_file(const char *fname,
+ const or_options_t *options);
+static config_line_t *get_assigned_option(const config_format_t *fmt,
+ const void *options, const char *key,
+ int escape_val);
+static void config_init(const config_format_t *fmt, void *options);
static int or_state_validate(or_state_t *old_options, or_state_t *options,
int from_setconf, char **msg);
static int or_state_load(void);
@@ -576,8 +612,9 @@ static int is_listening_on_low_port(int port_option,
const config_line_t *listen_options);
static uint64_t config_parse_memunit(const char *s, int *ok);
+static int config_parse_msec_interval(const char *s, int *ok);
static int config_parse_interval(const char *s, int *ok);
-static void init_libevent(void);
+static void init_libevent(const or_options_t *options);
static int opt_streq(const char *s1, const char *s2);
/** Magic value for or_options_t. */
@@ -604,7 +641,7 @@ static config_var_t state_extra_var = {
};
/** Configuration format for or_state_t. */
-static config_format_t state_format = {
+static const config_format_t state_format = {
sizeof(or_state_t),
OR_STATE_MAGIC,
STRUCT_OFFSET(or_state_t, _magic),
@@ -628,6 +665,8 @@ static or_state_t *global_state = NULL;
static config_line_t *global_cmdline_options = NULL;
/** Contents of most recently read DirPortFrontPage file. */
static char *global_dirfrontpagecontents = NULL;
+/** List of port_cfg_t for client-level (SOCKS, DNS, Trans, NATD) ports. */
+static smartlist_t *configured_client_ports = NULL;
/** Return the contents of our frontpage string, or NULL if not configured. */
const char *
@@ -638,7 +677,7 @@ get_dirportfrontpage(void)
/** Allocate an empty configuration object of a given format type. */
static void *
-config_alloc(config_format_t *fmt)
+config_alloc(const config_format_t *fmt)
{
void *opts = tor_malloc_zero(fmt->size);
*(uint32_t*)STRUCT_VAR_P(opts, fmt->magic_offset) = fmt->magic;
@@ -648,12 +687,19 @@ config_alloc(config_format_t *fmt)
/** Return the currently configured options. */
or_options_t *
-get_options(void)
+get_options_mutable(void)
{
tor_assert(global_options);
return global_options;
}
+/** Returns the currently configured options */
+const or_options_t *
+get_options(void)
+{
+ return get_options_mutable();
+}
+
/** Change the current global options to contain <b>new_val</b> instead of
* their current value; take action based on the new value; free the old value
* as necessary. Returns 0 on success, -1 on failure.
@@ -661,6 +707,9 @@ get_options(void)
int
set_options(or_options_t *new_val, char **msg)
{
+ int i;
+ smartlist_t *elements;
+ config_line_t *line;
or_options_t *old_options = global_options;
global_options = new_val;
/* Note that we pass the *old* options below, for comparison. It
@@ -675,7 +724,34 @@ set_options(or_options_t *new_val, char **msg)
"Acting on config options left us in a broken state. Dying.");
exit(1);
}
-
+ /* Issues a CONF_CHANGED event to notify controller of the change. If Tor is
+ * just starting up then the old_options will be undefined. */
+ if (old_options) {
+ elements = smartlist_create();
+ for (i=0; options_format.vars[i].name; ++i) {
+ const config_var_t *var = &options_format.vars[i];
+ const char *var_name = var->name;
+ if (var->type == CONFIG_TYPE_LINELIST_S ||
+ var->type == CONFIG_TYPE_OBSOLETE) {
+ continue;
+ }
+ if (!option_is_same(&options_format, new_val, old_options, var_name)) {
+ line = get_assigned_option(&options_format, new_val, var_name, 1);
+
+ if (line) {
+ for (; line; line = line->next) {
+ smartlist_add(elements, line->key);
+ smartlist_add(elements, line->value);
+ }
+ } else {
+ smartlist_add(elements, (char*)options_format.vars[i].name);
+ smartlist_add(elements, NULL);
+ }
+ }
+ }
+ control_event_conf_changed(elements);
+ smartlist_free(elements);
+ }
config_free(&options_format, old_options);
return 0;
@@ -711,6 +787,11 @@ or_options_free(or_options_t *options)
return;
routerset_free(options->_ExcludeExitNodesUnion);
+ if (options->NodeFamilySets) {
+ SMARTLIST_FOREACH(options->NodeFamilySets, routerset_t *,
+ rs, routerset_free(rs));
+ smartlist_free(options->NodeFamilySets);
+ }
config_free(&options_format, options);
}
@@ -728,6 +809,13 @@ config_free_all(void)
config_free_lines(global_cmdline_options);
global_cmdline_options = NULL;
+ if (configured_client_ports) {
+ SMARTLIST_FOREACH(configured_client_ports,
+ port_cfg_t *, p, tor_free(p));
+ smartlist_free(configured_client_ports);
+ configured_client_ports = NULL;
+ }
+
tor_free(torrc_fname);
tor_free(_version);
tor_free(global_dirfrontpagecontents);
@@ -793,7 +881,7 @@ escaped_safe_str(const char *address)
/** Add the default directory authorities directly into the trusted dir list,
* but only add them insofar as they share bits with <b>type</b>. */
static void
-add_default_trusted_dir_authorities(authority_type_t type)
+add_default_trusted_dir_authorities(dirinfo_type_t type)
{
int i;
const char *dirservers[] = {
@@ -867,16 +955,16 @@ validate_dir_authorities(or_options_t *options, or_options_t *old_options)
/* Now go through the four ways you can configure an alternate
* set of directory authorities, and make sure none are broken. */
for (cl = options->DirServers; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_AUTHORITY, 1)<0)
+ if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0)
return -1;
for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_AUTHORITY, 1)<0)
+ if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0)
return -1;
for (cl = options->AlternateDirAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_AUTHORITY, 1)<0)
+ if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0)
return -1;
for (cl = options->AlternateHSAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_AUTHORITY, 1)<0)
+ if (parse_dir_server_line(cl->value, NO_DIRINFO, 1)<0)
return -1;
return 0;
}
@@ -885,8 +973,8 @@ validate_dir_authorities(or_options_t *options, or_options_t *old_options)
* as appropriate.
*/
static int
-consider_adding_dir_authorities(or_options_t *options,
- or_options_t *old_options)
+consider_adding_dir_authorities(const or_options_t *options,
+ const or_options_t *old_options)
{
config_line_t *cl;
int need_to_update =
@@ -907,27 +995,28 @@ consider_adding_dir_authorities(or_options_t *options,
if (!options->DirServers) {
/* then we may want some of the defaults */
- authority_type_t type = NO_AUTHORITY;
+ dirinfo_type_t type = NO_DIRINFO;
if (!options->AlternateBridgeAuthority)
- type |= BRIDGE_AUTHORITY;
+ type |= BRIDGE_DIRINFO;
if (!options->AlternateDirAuthority)
- type |= V1_AUTHORITY | V2_AUTHORITY | V3_AUTHORITY;
+ type |= V1_DIRINFO | V2_DIRINFO | V3_DIRINFO | EXTRAINFO_DIRINFO |
+ MICRODESC_DIRINFO;
if (!options->AlternateHSAuthority)
- type |= HIDSERV_AUTHORITY;
+ type |= HIDSERV_DIRINFO;
add_default_trusted_dir_authorities(type);
}
for (cl = options->DirServers; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_AUTHORITY, 0)<0)
+ if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0)
return -1;
for (cl = options->AlternateBridgeAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_AUTHORITY, 0)<0)
+ if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0)
return -1;
for (cl = options->AlternateDirAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_AUTHORITY, 0)<0)
+ if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0)
return -1;
for (cl = options->AlternateHSAuthority; cl; cl = cl->next)
- if (parse_dir_server_line(cl->value, NO_AUTHORITY, 0)<0)
+ if (parse_dir_server_line(cl->value, NO_DIRINFO, 0)<0)
return -1;
return 0;
}
@@ -939,12 +1028,12 @@ consider_adding_dir_authorities(or_options_t *options,
* Return 0 if all goes well, return -1 if things went badly.
*/
static int
-options_act_reversible(or_options_t *old_options, char **msg)
+options_act_reversible(const or_options_t *old_options, char **msg)
{
smartlist_t *new_listeners = smartlist_create();
smartlist_t *replaced_listeners = smartlist_create();
static int libevent_initialized = 0;
- or_options_t *options = get_options();
+ or_options_t *options = get_options_mutable();
int running_tor = options->command == CMD_RUN_TOR;
int set_conn_limit = 0;
int r = -1;
@@ -973,6 +1062,7 @@ options_act_reversible(or_options_t *old_options, char **msg)
#endif
if (running_tor) {
+ int n_client_ports=0;
/* We need to set the connection limit before we can open the listeners. */
if (set_max_file_descriptors((unsigned)options->ConnLimit,
&options->_ConnLimit) < 0) {
@@ -984,10 +1074,20 @@ options_act_reversible(or_options_t *old_options, char **msg)
/* Set up libevent. (We need to do this before we can register the
* listeners as listeners.) */
if (running_tor && !libevent_initialized) {
- init_libevent();
+ init_libevent(options);
libevent_initialized = 1;
}
+ /* Adjust the client port configuration so we can launch listeners. */
+ if (parse_client_ports(options, 0, msg, &n_client_ports)) {
+ if (!*msg)
+ *msg = tor_strdup("Unexpected problem parsing client port config");
+ goto rollback;
+ }
+
+ /* Set the hibernation state appropriately.*/
+ consider_hibernation(time(NULL));
+
/* Launch the listeners. (We do this before we setuid, so we can bind to
* ports under 1024.) We don't want to rebind if we're hibernating. */
if (!we_are_hibernating()) {
@@ -1040,7 +1140,7 @@ options_act_reversible(or_options_t *old_options, char **msg)
/* Write control ports to disk as appropriate */
control_ports_write_to_file();
- if (directory_caches_v2_dir_info(options)) {
+ if (directory_caches_v2_dir_info(options)) {
size_t len = strlen(options->DataDirectory)+32;
char *fn = tor_malloc(len);
tor_snprintf(fn, len, "%s"PATH_SEPARATOR"cached-status",
@@ -1116,7 +1216,7 @@ options_act_reversible(or_options_t *old_options, char **msg)
/** If we need to have a GEOIP ip-to-country map to run with our configured
* options, return 1 and set *<b>reason_out</b> to a description of why. */
int
-options_need_geoip_info(or_options_t *options, const char **reason_out)
+options_need_geoip_info(const or_options_t *options, const char **reason_out)
{
int bridge_usage =
options->BridgeRelay && options->BridgeRecordUsageByCountry;
@@ -1141,7 +1241,7 @@ options_need_geoip_info(or_options_t *options, const char **reason_out)
/** Return the bandwidthrate that we are going to report to the authorities
* based on the config options. */
uint32_t
-get_effective_bwrate(or_options_t *options)
+get_effective_bwrate(const or_options_t *options)
{
uint64_t bw = options->BandwidthRate;
if (bw > options->MaxAdvertisedBandwidth)
@@ -1155,7 +1255,7 @@ get_effective_bwrate(or_options_t *options)
/** Return the bandwidthburst that we are going to report to the authorities
* based on the config options. */
uint32_t
-get_effective_bwburst(or_options_t *options)
+get_effective_bwburst(const or_options_t *options)
{
uint64_t bw = options->BandwidthBurst;
if (options->RelayBandwidthBurst > 0 && bw > options->RelayBandwidthBurst)
@@ -1174,10 +1274,10 @@ get_effective_bwburst(or_options_t *options)
* here yet. Some is still in do_hup() and other places.
*/
static int
-options_act(or_options_t *old_options)
+options_act(const or_options_t *old_options)
{
config_line_t *cl;
- or_options_t *options = get_options();
+ or_options_t *options = get_options_mutable();
int running_tor = options->command == CMD_RUN_TOR;
char *msg;
const int transition_affects_workers =
@@ -1222,6 +1322,32 @@ options_act(or_options_t *old_options)
rep_hist_load_mtbf_data(time(NULL));
}
+ mark_transport_list();
+ pt_prepare_proxy_list_for_config_read();
+ if (options->ClientTransportPlugin) {
+ for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
+ if (parse_client_transport_line(cl->value, 0)<0) {
+ log_warn(LD_BUG,
+ "Previously validated ClientTransportPlugin line "
+ "could not be added!");
+ return -1;
+ }
+ }
+ }
+
+ if (options->ServerTransportPlugin) {
+ for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
+ if (parse_server_transport_line(cl->value, 0)<0) {
+ log_warn(LD_BUG,
+ "Previously validated ServerTransportPlugin line "
+ "could not be added!");
+ return -1;
+ }
+ }
+ }
+ sweep_transport_list();
+ sweep_proxy_list();
+
/* Bail out at this point if we're not going to be a client or server:
* we want to not fork, and to log stuff to stderr. */
if (!running_tor)
@@ -1281,17 +1407,16 @@ options_act(or_options_t *old_options)
if (accounting_is_enabled(options))
configure_accounting(time(NULL));
- /* parse RefuseUnknownExits tristate */
- if (!strcmp(options->RefuseUnknownExits, "0"))
- options->RefuseUnknownExits_ = 0;
- else if (!strcmp(options->RefuseUnknownExits, "1"))
- options->RefuseUnknownExits_ = 1;
- else if (!strcmp(options->RefuseUnknownExits, "auto"))
- options->RefuseUnknownExits_ = -1;
- else {
- /* Should have caught this in options_validate */
- return -1;
- }
+#ifdef USE_BUFFEREVENTS
+ /* If we're using the bufferevents implementation and our rate limits
+ * changed, we need to tell the rate-limiting system about it. */
+ if (!old_options ||
+ old_options->BandwidthRate != options->BandwidthRate ||
+ old_options->BandwidthBurst != options->BandwidthBurst ||
+ old_options->RelayBandwidthRate != options->RelayBandwidthRate ||
+ old_options->RelayBandwidthBurst != options->RelayBandwidthBurst)
+ connection_bucket_init();
+#endif
/* Change the cell EWMA settings */
cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus());
@@ -1411,7 +1536,9 @@ options_act(or_options_t *old_options)
}
if (options->CellStatistics || options->DirReqStatistics ||
- options->EntryStatistics || options->ExitPortStatistics) {
+ options->EntryStatistics || options->ExitPortStatistics ||
+ options->ConnDirectionStatistics ||
+ options->BridgeAuthoritativeDir) {
time_t now = time(NULL);
int print_notice = 0;
if ((!old_options || !old_options->CellStatistics) &&
@@ -1425,10 +1552,13 @@ options_act(or_options_t *old_options)
geoip_dirreq_stats_init(now);
print_notice = 1;
} else {
- log_notice(LD_CONFIG, "Configured to measure directory request "
- "statistics, but no GeoIP database found! "
- "Please specify a GeoIP database using the "
- "GeoIPFile option!");
+ options->DirReqStatistics = 0;
+ /* Don't warn Tor clients, they don't use statistics */
+ if (options->ORPort)
+ log_notice(LD_CONFIG, "Configured to measure directory request "
+ "statistics, but no GeoIP database found. "
+ "Please specify a GeoIP database using the "
+ "GeoIPFile option.");
}
}
if ((!old_options || !old_options->EntryStatistics) &&
@@ -1437,10 +1567,11 @@ options_act(or_options_t *old_options)
geoip_entry_stats_init(now);
print_notice = 1;
} else {
+ options->EntryStatistics = 0;
log_notice(LD_CONFIG, "Configured to measure entry node "
- "statistics, but no GeoIP database found! "
+ "statistics, but no GeoIP database found. "
"Please specify a GeoIP database using the "
- "GeoIPFile option!");
+ "GeoIPFile option.");
}
}
if ((!old_options || !old_options->ExitPortStatistics) &&
@@ -1448,6 +1579,15 @@ options_act(or_options_t *old_options)
rep_hist_exit_stats_init(now);
print_notice = 1;
}
+ if ((!old_options || !old_options->ConnDirectionStatistics) &&
+ options->ConnDirectionStatistics) {
+ rep_hist_conn_stats_init(now);
+ }
+ if ((!old_options || !old_options->BridgeAuthoritativeDir) &&
+ options->BridgeAuthoritativeDir) {
+ rep_hist_desc_stats_init(now);
+ print_notice = 1;
+ }
if (print_notice)
log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
"the *-stats files that will first be written to the "
@@ -1466,6 +1606,12 @@ options_act(or_options_t *old_options)
if (old_options && old_options->ExitPortStatistics &&
!options->ExitPortStatistics)
rep_hist_exit_stats_term();
+ if (old_options && old_options->ConnDirectionStatistics &&
+ !options->ConnDirectionStatistics)
+ rep_hist_conn_stats_term();
+ if (old_options && old_options->BridgeAuthoritativeDir &&
+ !options->BridgeAuthoritativeDir)
+ rep_hist_desc_stats_term();
/* Check if we need to parse and add the EntryNodes config option. */
if (options->EntryNodes &&
@@ -1523,7 +1669,7 @@ options_act(or_options_t *old_options)
* apply abbreviations that work for the config file and the command line.
* If <b>warn_obsolete</b> is set, warn about deprecated names. */
static const char *
-expand_abbrev(config_format_t *fmt, const char *option, int command_line,
+expand_abbrev(const config_format_t *fmt, const char *option, int command_line,
int warn_obsolete)
{
int i;
@@ -1683,12 +1829,9 @@ config_free_lines(config_line_t *front)
}
}
-/** If <b>key</b> is a configuration option, return the corresponding
- * config_var_t. Otherwise, if <b>key</b> is a non-standard abbreviation,
- * warn, and return the corresponding config_var_t. Otherwise return NULL.
- */
+/** As config_find_option, but return a non-const pointer. */
static config_var_t *
-config_find_option(config_format_t *fmt, const char *key)
+config_find_option_mutable(config_format_t *fmt, const char *key)
{
int i;
size_t keylen = strlen(key);
@@ -1713,9 +1856,20 @@ config_find_option(config_format_t *fmt, const char *key)
return NULL;
}
+/** If <b>key</b> is a configuration option, return the corresponding const
+ * config_var_t. Otherwise, if <b>key</b> is a non-standard abbreviation,
+ * warn, and return the corresponding const config_var_t. Otherwise return
+ * NULL.
+ */
+static const config_var_t *
+config_find_option(const config_format_t *fmt, const char *key)
+{
+ return config_find_option_mutable((config_format_t*)fmt, key);
+}
+
/** Return the number of option entries in <b>fmt</b>. */
static int
-config_count_options(config_format_t *fmt)
+config_count_options(const config_format_t *fmt)
{
int i;
for (i=0; fmt->vars[i].name; ++i)
@@ -1733,11 +1887,11 @@ config_count_options(config_format_t *fmt)
* Called from config_assign_line() and option_reset().
*/
static int
-config_assign_value(config_format_t *fmt, or_options_t *options,
+config_assign_value(const config_format_t *fmt, or_options_t *options,
config_line_t *c, char **msg)
{
int i, ok;
- config_var_t *var;
+ const config_var_t *var;
void *lvalue;
CHECK(fmt, options);
@@ -1780,6 +1934,18 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
break;
}
+ case CONFIG_TYPE_MSEC_INTERVAL: {
+ i = config_parse_msec_interval(c->value, &ok);
+ if (!ok) {
+ tor_asprintf(msg,
+ "Msec interval '%s %s' is malformed or out of bounds.",
+ c->key, c->value);
+ return -1;
+ }
+ *(int *)lvalue = i;
+ break;
+ }
+
case CONFIG_TYPE_MEMUNIT: {
uint64_t u64 = config_parse_memunit(c->value, &ok);
if (!ok) {
@@ -1803,6 +1969,20 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
*(int *)lvalue = i;
break;
+ case CONFIG_TYPE_AUTOBOOL:
+ if (!strcmp(c->value, "auto"))
+ *(int *)lvalue = -1;
+ else if (!strcmp(c->value, "0"))
+ *(int *)lvalue = 0;
+ else if (!strcmp(c->value, "1"))
+ *(int *)lvalue = 1;
+ else {
+ tor_asprintf(msg, "Boolean '%s %s' expects 0, 1, or 'auto'.",
+ c->key, c->value);
+ return -1;
+ }
+ break;
+
case CONFIG_TYPE_STRING:
case CONFIG_TYPE_FILENAME:
tor_free(*(char **)lvalue);
@@ -1873,11 +2053,11 @@ config_assign_value(config_format_t *fmt, or_options_t *options,
* Called from config_assign().
*/
static int
-config_assign_line(config_format_t *fmt, or_options_t *options,
+config_assign_line(const config_format_t *fmt, or_options_t *options,
config_line_t *c, int use_defaults,
int clear_first, bitarray_t *options_seen, char **msg)
{
- config_var_t *var;
+ const config_var_t *var;
CHECK(fmt, options);
@@ -1938,10 +2118,10 @@ config_assign_line(config_format_t *fmt, or_options_t *options,
/** Restore the option named <b>key</b> in options to its default value.
* Called from config_assign(). */
static void
-config_reset_line(config_format_t *fmt, or_options_t *options,
+config_reset_line(const config_format_t *fmt, or_options_t *options,
const char *key, int use_defaults)
{
- config_var_t *var;
+ const config_var_t *var;
CHECK(fmt, options);
@@ -1956,7 +2136,7 @@ config_reset_line(config_format_t *fmt, or_options_t *options,
int
option_is_recognized(const char *key)
{
- config_var_t *var = config_find_option(&options_format, key);
+ const config_var_t *var = config_find_option(&options_format, key);
return (var != NULL);
}
@@ -1965,14 +2145,14 @@ option_is_recognized(const char *key)
const char *
option_get_canonical_name(const char *key)
{
- config_var_t *var = config_find_option(&options_format, key);
+ const config_var_t *var = config_find_option(&options_format, key);
return var ? var->name : NULL;
}
/** Return a canonical list of the options assigned for key.
*/
config_line_t *
-option_get_assignment(or_options_t *options, const char *key)
+option_get_assignment(const or_options_t *options, const char *key)
{
return get_assigned_option(&options_format, options, key, 1);
}
@@ -2025,10 +2205,10 @@ config_lines_dup(const config_line_t *inp)
* value needs to be quoted before it's put in a config file, quote and
* escape that value. Return NULL if no such key exists. */
static config_line_t *
-get_assigned_option(config_format_t *fmt, void *options,
+get_assigned_option(const config_format_t *fmt, const void *options,
const char *key, int escape_val)
{
- config_var_t *var;
+ const config_var_t *var;
const void *value;
config_line_t *result;
tor_assert(options && key);
@@ -2074,6 +2254,7 @@ get_assigned_option(config_format_t *fmt, void *options,
}
/* fall through */
case CONFIG_TYPE_INTERVAL:
+ case CONFIG_TYPE_MSEC_INTERVAL:
case CONFIG_TYPE_UINT:
/* This means every or_options_t uint or bool element
* needs to be an int. Not, say, a uint16_t or char. */
@@ -2089,6 +2270,14 @@ get_assigned_option(config_format_t *fmt, void *options,
tor_asprintf(&result->value, "%f", *(double*)value);
escape_val = 0; /* Can't need escape. */
break;
+
+ case CONFIG_TYPE_AUTOBOOL:
+ if (*(int*)value == -1) {
+ result->value = tor_strdup("auto");
+ escape_val = 0;
+ break;
+ }
+ /* fall through */
case CONFIG_TYPE_BOOL:
result->value = tor_strdup(*(int*)value ? "1" : "0");
escape_val = 0; /* Can't need escape. */
@@ -2201,7 +2390,7 @@ options_trial_assign() calls config_assign(1, 1)
returns.
*/
static int
-config_assign(config_format_t *fmt, void *options, config_line_t *list,
+config_assign(const config_format_t *fmt, void *options, config_line_t *list,
int use_defaults, int clear_first, char **msg)
{
config_line_t *p;
@@ -2263,7 +2452,7 @@ options_trial_assign(config_line_t *list, int use_defaults,
return r;
}
- if (options_validate(get_options(), trial_options, 1, msg) < 0) {
+ if (options_validate(get_options_mutable(), trial_options, 1, msg) < 0) {
config_free(&options_format, trial_options);
return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */
}
@@ -2285,7 +2474,8 @@ options_trial_assign(config_line_t *list, int use_defaults,
/** Reset config option <b>var</b> to 0, 0.0, NULL, or the equivalent.
* Called from option_reset() and config_free(). */
static void
-option_clear(config_format_t *fmt, or_options_t *options, config_var_t *var)
+option_clear(const config_format_t *fmt, or_options_t *options,
+ const config_var_t *var)
{
void *lvalue = STRUCT_VAR_P(options, var->var_offset);
(void)fmt; /* unused */
@@ -2301,11 +2491,15 @@ option_clear(config_format_t *fmt, or_options_t *options, config_var_t *var)
*(time_t*)lvalue = 0;
break;
case CONFIG_TYPE_INTERVAL:
+ case CONFIG_TYPE_MSEC_INTERVAL:
case CONFIG_TYPE_UINT:
case CONFIG_TYPE_PORT:
case CONFIG_TYPE_BOOL:
*(int*)lvalue = 0;
break;
+ case CONFIG_TYPE_AUTOBOOL:
+ *(int*)lvalue = -1;
+ break;
case CONFIG_TYPE_MEMUNIT:
*(uint64_t*)lvalue = 0;
break;
@@ -2339,8 +2533,8 @@ option_clear(config_format_t *fmt, or_options_t *options, config_var_t *var)
* <b>use_defaults</b>, set it to its default value.
* Called by config_init() and option_reset_line() and option_assign_line(). */
static void
-option_reset(config_format_t *fmt, or_options_t *options,
- config_var_t *var, int use_defaults)
+option_reset(const config_format_t *fmt, or_options_t *options,
+ const config_var_t *var, int use_defaults)
{
config_line_t *c;
char *msg = NULL;
@@ -2380,7 +2574,7 @@ list_torrc_options(void)
int i;
smartlist_t *lines = smartlist_create();
for (i = 0; _option_vars[i].name; ++i) {
- config_var_t *var = &_option_vars[i];
+ const config_var_t *var = &_option_vars[i];
if (var->type == CONFIG_TYPE_OBSOLETE ||
var->type == CONFIG_TYPE_LINELIST_V)
continue;
@@ -2399,7 +2593,7 @@ static uint32_t last_resolved_addr = 0;
* public IP address.
*/
int
-resolve_my_address(int warn_severity, or_options_t *options,
+resolve_my_address(int warn_severity, const or_options_t *options,
uint32_t *addr_out, char **hostname_out)
{
struct in_addr in;
@@ -2408,7 +2602,7 @@ resolve_my_address(int warn_severity, or_options_t *options,
int explicit_ip=1;
int explicit_hostname=1;
int from_interface=0;
- char tmpbuf[INET_NTOA_BUF_LEN];
+ char *addr_string = NULL;
const char *address = options->Address;
int notice_severity = warn_severity <= LOG_NOTICE ?
LOG_NOTICE : warn_severity;
@@ -2450,48 +2644,43 @@ resolve_my_address(int warn_severity, or_options_t *options,
return -1;
}
from_interface = 1;
- in.s_addr = htonl(interface_ip);
- tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
+ addr = interface_ip;
log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for "
- "local interface. Using that.", tmpbuf);
+ "local interface. Using that.", fmt_addr32(addr));
strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
} else { /* resolved hostname into addr */
- in.s_addr = htonl(addr);
-
if (!explicit_hostname &&
- is_internal_IP(ntohl(in.s_addr), 0)) {
+ is_internal_IP(addr, 0)) {
uint32_t interface_ip;
- tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' "
- "resolves to a private IP address (%s). Trying something "
- "else.", hostname, tmpbuf);
+ "resolves to a private IP address (%s). Trying something "
+ "else.", hostname, fmt_addr32(addr));
if (get_interface_address(warn_severity, &interface_ip)) {
log_fn(warn_severity, LD_CONFIG,
"Could not get local interface IP address. Too bad.");
} else if (is_internal_IP(interface_ip, 0)) {
- struct in_addr in2;
- in2.s_addr = htonl(interface_ip);
- tor_inet_ntoa(&in2,tmpbuf,sizeof(tmpbuf));
log_fn(notice_severity, LD_CONFIG,
"Interface IP address '%s' is a private address too. "
- "Ignoring.", tmpbuf);
+ "Ignoring.", fmt_addr32(interface_ip));
} else {
from_interface = 1;
- in.s_addr = htonl(interface_ip);
- tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
+ addr = interface_ip;
log_fn(notice_severity, LD_CONFIG,
"Learned IP address '%s' for local interface."
- " Using that.", tmpbuf);
+ " Using that.", fmt_addr32(addr));
strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname));
}
}
}
+ } else {
+ addr = ntohl(in.s_addr); /* set addr so that addr_string is not
+ * illformed */
}
- tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
- if (is_internal_IP(ntohl(in.s_addr), 0)) {
+ addr_string = tor_dup_ip(addr);
+ if (is_internal_IP(addr, 0)) {
/* make sure we're ok with publishing an internal IP */
if (!options->DirServers && !options->AlternateDirAuthority) {
/* if they are using the default dirservers, disallow internal IPs
@@ -2499,7 +2688,8 @@ resolve_my_address(int warn_severity, or_options_t *options,
log_fn(warn_severity, LD_CONFIG,
"Address '%s' resolves to private IP address '%s'. "
"Tor servers that use the default DirServers must have public "
- "IP addresses.", hostname, tmpbuf);
+ "IP addresses.", hostname, addr_string);
+ tor_free(addr_string);
return -1;
}
if (!explicit_ip) {
@@ -2507,19 +2697,20 @@ resolve_my_address(int warn_severity, or_options_t *options,
* they're using an internal address. */
log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private "
"IP address '%s'. Please set the Address config option to be "
- "the IP address you want to use.", hostname, tmpbuf);
+ "the IP address you want to use.", hostname, addr_string);
+ tor_free(addr_string);
return -1;
}
}
- log_debug(LD_CONFIG, "Resolved Address to '%s'.", tmpbuf);
- *addr_out = ntohl(in.s_addr);
+ log_debug(LD_CONFIG, "Resolved Address to '%s'.", fmt_addr32(addr));
+ *addr_out = addr;
if (last_resolved_addr && last_resolved_addr != *addr_out) {
/* Leave this as a notice, regardless of the requested severity,
* at least until dynamic IP address support becomes bulletproof. */
log_notice(LD_NET,
"Your IP address seems to have changed to %s. Updating.",
- tmpbuf);
+ addr_string);
ip_address_changed(0);
}
if (last_resolved_addr != *addr_out) {
@@ -2538,11 +2729,12 @@ resolve_my_address(int warn_severity, or_options_t *options,
}
control_event_server_status(LOG_NOTICE,
"EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s %s%s",
- tmpbuf, method, h?"HOSTNAME=":"", h);
+ addr_string, method, h?"HOSTNAME=":"", h);
}
last_resolved_addr = *addr_out;
if (hostname_out)
*hostname_out = tor_strdup(hostname);
+ tor_free(addr_string);
return 0;
}
@@ -2577,7 +2769,7 @@ is_local_addr(const tor_addr_t *addr)
/** Release storage held by <b>options</b>. */
static void
-config_free(config_format_t *fmt, void *options)
+config_free(const config_format_t *fmt, void *options)
{
int i;
@@ -2616,8 +2808,9 @@ config_lines_eq(config_line_t *a, config_line_t *b)
* and <b>o2</b>. Must not be called for LINELIST_S or OBSOLETE options.
*/
static int
-option_is_same(config_format_t *fmt,
- or_options_t *o1, or_options_t *o2, const char *name)
+option_is_same(const config_format_t *fmt,
+ const or_options_t *o1, const or_options_t *o2,
+ const char *name)
{
config_line_t *c1, *c2;
int r = 1;
@@ -2634,7 +2827,7 @@ option_is_same(config_format_t *fmt,
/** Copy storage held by <b>old</b> into a new or_options_t and return it. */
static or_options_t *
-options_dup(config_format_t *fmt, or_options_t *old)
+options_dup(const config_format_t *fmt, const or_options_t *old)
{
or_options_t *newopts;
int i;
@@ -2698,7 +2891,7 @@ is_listening_on_low_port(int port_option,
return (port_option < 1024);
for (l = listen_options; l; l = l->next) {
- parse_addr_port(LOG_WARN, l->value, NULL, NULL, &p);
+ addr_port_lookup(LOG_WARN, l->value, NULL, NULL, &p);
if (p<1024) {
return 1;
}
@@ -2710,10 +2903,10 @@ is_listening_on_low_port(int port_option,
/** Set all vars in the configuration object <b>options</b> to their default
* values. */
static void
-config_init(config_format_t *fmt, void *options)
+config_init(const config_format_t *fmt, void *options)
{
int i;
- config_var_t *var;
+ const config_var_t *var;
CHECK(fmt, options);
for (i=0; fmt->vars[i].name; ++i) {
@@ -2729,7 +2922,7 @@ config_init(config_format_t *fmt, void *options)
* Else, if comment_defaults, write default values as comments.
*/
static char *
-config_dump(config_format_t *fmt, void *options, int minimal,
+config_dump(const config_format_t *fmt, const void *options, int minimal,
int comment_defaults)
{
smartlist_t *elements;
@@ -2797,7 +2990,7 @@ config_dump(config_format_t *fmt, void *options, int minimal,
* include options that are the same as Tor's defaults.
*/
char *
-options_dump(or_options_t *options, int minimal)
+options_dump(const or_options_t *options, int minimal)
{
return config_dump(&options_format, options, minimal, 0);
}
@@ -2856,24 +3049,24 @@ static int
compute_publishserverdescriptor(or_options_t *options)
{
smartlist_t *list = options->PublishServerDescriptor;
- authority_type_t *auth = &options->_PublishServerDescriptor;
- *auth = NO_AUTHORITY;
+ dirinfo_type_t *auth = &options->_PublishServerDescriptor;
+ *auth = NO_DIRINFO;
if (!list) /* empty list, answer is none */
return 0;
SMARTLIST_FOREACH(list, const char *, string, {
if (!strcasecmp(string, "v1"))
- *auth |= V1_AUTHORITY;
+ *auth |= V1_DIRINFO;
else if (!strcmp(string, "1"))
if (options->BridgeRelay)
- *auth |= BRIDGE_AUTHORITY;
+ *auth |= BRIDGE_DIRINFO;
else
- *auth |= V2_AUTHORITY | V3_AUTHORITY;
+ *auth |= V2_DIRINFO | V3_DIRINFO;
else if (!strcasecmp(string, "v2"))
- *auth |= V2_AUTHORITY;
+ *auth |= V2_DIRINFO;
else if (!strcasecmp(string, "v3"))
- *auth |= V3_AUTHORITY;
+ *auth |= V3_DIRINFO;
else if (!strcasecmp(string, "bridge"))
- *auth |= BRIDGE_AUTHORITY;
+ *auth |= BRIDGE_DIRINFO;
else if (!strcasecmp(string, "hidserv"))
log_warn(LD_CONFIG,
"PublishServerDescriptor hidserv is invalid. See "
@@ -2901,6 +3094,10 @@ compute_publishserverdescriptor(or_options_t *options)
* will generate too many circuits and potentially overload the network. */
#define MIN_CIRCUIT_STREAM_TIMEOUT 10
+/** Lowest allowable value for HeartbeatPeriod; if this is too low, we might
+ * expose more information than we're comfortable with. */
+#define MIN_HEARTBEAT_PERIOD (30*60)
+
/** Return 0 if every setting in <b>options</b> is reasonable, and a
* permissible transition from <b>old_options</b>. Else return -1.
* Should have no side effects, except for normalizing the contents of
@@ -2920,6 +3117,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
int i;
config_line_t *cl;
const char *uname = get_uname();
+ int n_client_ports=0;
#define REJECT(arg) \
STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
#define COMPLAIN(arg) STMT_BEGIN log(LOG_WARN, LD_CONFIG, arg); STMT_END
@@ -2943,57 +3141,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->DirPort == 0 && options->DirListenAddress != NULL)
REJECT("DirPort must be defined if DirListenAddress is defined.");
- if (options->DNSPort == 0 && options->DNSListenAddress != NULL)
- REJECT("DNSPort must be defined if DNSListenAddress is defined.");
-
- if (options->ControlPort == 0 && options->ControlListenAddress != NULL)
- REJECT("ControlPort must be defined if ControlListenAddress is defined.");
-
- if (options->TransPort == 0 && options->TransListenAddress != NULL)
- REJECT("TransPort must be defined if TransListenAddress is defined.");
-
- if (options->NATDPort == 0 && options->NATDListenAddress != NULL)
- REJECT("NATDPort must be defined if NATDListenAddress is defined.");
-
- /* Don't gripe about SocksPort 0 with SocksListenAddress set; a standard
- * configuration does this. */
-
- for (i = 0; i < 3; ++i) {
- int is_socks = i==0;
- int is_trans = i==1;
- config_line_t *line, *opt, *old;
- const char *tp;
- if (is_socks) {
- opt = options->SocksListenAddress;
- old = old_options ? old_options->SocksListenAddress : NULL;
- tp = "SOCKS proxy";
- } else if (is_trans) {
- opt = options->TransListenAddress;
- old = old_options ? old_options->TransListenAddress : NULL;
- tp = "transparent proxy";
- } else {
- opt = options->NATDListenAddress;
- old = old_options ? old_options->NATDListenAddress : NULL;
- tp = "natd proxy";
- }
-
- for (line = opt; line; line = line->next) {
- char *address = NULL;
- uint16_t port;
- uint32_t addr;
- if (parse_addr_port(LOG_WARN, line->value, &address, &addr, &port)<0)
- continue; /* We'll warn about this later. */
- if (!is_internal_IP(addr, 1) &&
- (!old_options || !config_lines_eq(old, opt))) {
- log_warn(LD_CONFIG,
- "You specified a public address '%s' for a %s. Other "
- "people on the Internet might find your computer and use it as "
- "an open %s. Please don't allow this unless you have "
- "a good reason.", address, tp, tp);
- }
- tor_free(address);
- }
- }
+ if (parse_client_ports(options, 1, msg, &n_client_ports) < 0)
+ return -1;
if (validate_data_directory(options)<0)
REJECT("Invalid DataDirectory");
@@ -3017,8 +3166,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
"misconfigured or something else goes wrong.");
/* Special case on first boot if no Log options are given. */
- if (!options->Logs && !options->RunAsDaemon && !from_setconf)
- config_line_append(&options->Logs, "Log", "notice stdout");
+ if (!options->Logs && !options->RunAsDaemon && !from_setconf) {
+ if (quiet_level == 0)
+ config_line_append(&options->Logs, "Log", "notice stdout");
+ else if (quiet_level == 1)
+ config_line_append(&options->Logs, "Log", "warn stdout");
+ }
if (options_init_logs(options, 1)<0) /* Validate the log(s) */
REJECT("Failed to validate Log options. See logs for details.");
@@ -3030,20 +3183,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Failed to resolve/guess local address. See logs for details.");
}
- if (strcmp(options->RefuseUnknownExits, "0") &&
- strcmp(options->RefuseUnknownExits, "1") &&
- strcmp(options->RefuseUnknownExits, "auto")) {
- REJECT("RefuseUnknownExits must be 0, 1, or auto");
- }
-
#ifndef MS_WINDOWS
if (options->RunAsDaemon && torrc_fname && path_is_relative(torrc_fname))
REJECT("Can't use a relative path to torrc when RunAsDaemon is set.");
#endif
- if (options->SocksPort == 0 && options->TransPort == 0 &&
- options->NATDPort == 0 && options->ORPort == 0 &&
- options->DNSPort == 0 && !options->RendConfigLines)
+ if (n_client_ports == 0 && options->ORPort == 0 && !options->RendConfigLines)
log(LOG_WARN, LD_CONFIG,
"SocksPort, TransPort, NATDPort, DNSPort, and ORPort are all "
"undefined, and there aren't any hidden services configured. "
@@ -3054,6 +3199,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("TransPort and TransListenAddress are disabled in this build.");
#endif
+ if (options->TokenBucketRefillInterval <= 0
+ || options->TokenBucketRefillInterval > 1000) {
+ REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive.");
+ }
+
if (options->AccountingMax &&
(is_listening_on_low_port(options->ORPort, options->ORListenAddress) ||
is_listening_on_low_port(options->DirPort, options->DirListenAddress)))
@@ -3073,17 +3223,24 @@ options_validate(or_options_t *old_options, or_options_t *options,
routerset_union(options->_ExcludeExitNodesUnion,options->ExcludeNodes);
}
+ if (options->NodeFamilies) {
+ options->NodeFamilySets = smartlist_create();
+ for (cl = options->NodeFamilies; cl; cl = cl->next) {
+ routerset_t *rs = routerset_new();
+ if (routerset_parse(rs, cl->value, cl->key) == 0) {
+ smartlist_add(options->NodeFamilySets, rs);
+ } else {
+ routerset_free(rs);
+ }
+ }
+ }
+
if (options->ExcludeNodes && options->StrictNodes) {
COMPLAIN("You have asked to exclude certain relays from all positions "
"in your circuits. Expect hidden services and other Tor "
"features to be broken in unpredictable ways.");
}
- if (options->EntryNodes && !routerset_is_list(options->EntryNodes)) {
- /* XXXX fix this; see entry_guards_prepend_from_config(). */
- REJECT("IPs or countries are not yet supported in EntryNodes.");
- }
-
if (options->AuthoritativeDir) {
if (!options->ContactInfo && !options->TestingTorNetwork)
REJECT("Authoritative directory servers must set ContactInfo");
@@ -3145,6 +3302,15 @@ options_validate(or_options_t *old_options, or_options_t *options,
return -1;
}
+ if (options->MaxClientCircuitsPending <= 0 ||
+ options->MaxClientCircuitsPending > MAX_MAX_CLIENT_CIRCUITS_PENDING) {
+ tor_asprintf(msg,
+ "MaxClientCircuitsPending must be between 1 and %d, but "
+ "was set to %d", MAX_MAX_CLIENT_CIRCUITS_PENDING,
+ options->MaxClientCircuitsPending);
+ return -1;
+ }
+
if (validate_ports_csv(options->FirewallPorts, "FirewallPorts", msg) < 0)
return -1;
@@ -3293,9 +3459,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if ((options->BridgeRelay
- || options->_PublishServerDescriptor & BRIDGE_AUTHORITY)
+ || options->_PublishServerDescriptor & BRIDGE_DIRINFO)
&& (options->_PublishServerDescriptor
- & (V1_AUTHORITY|V2_AUTHORITY|V3_AUTHORITY))) {
+ & (V1_DIRINFO|V2_DIRINFO|V3_DIRINFO))) {
REJECT("Bridges are not supposed to publish router descriptors to the "
"directory authorities. Please correct your "
"PublishServerDescriptor line.");
@@ -3338,6 +3504,13 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->CircuitStreamTimeout = MIN_CIRCUIT_STREAM_TIMEOUT;
}
+ if (options->HeartbeatPeriod &&
+ options->HeartbeatPeriod < MIN_HEARTBEAT_PERIOD) {
+ log_warn(LD_CONFIG, "HeartbeatPeriod option is too short; "
+ "raising to %d seconds.", MIN_HEARTBEAT_PERIOD);
+ options->HeartbeatPeriod = MIN_HEARTBEAT_PERIOD;
+ }
+
if (options->KeepalivePeriod < 1)
REJECT("KeepalivePeriod option must be positive.");
@@ -3414,7 +3587,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Failed to parse accounting options. See logs for details.");
if (options->HTTPProxy) { /* parse it now */
- if (tor_addr_port_parse(options->HTTPProxy,
+ if (tor_addr_port_lookup(options->HTTPProxy,
&options->HTTPProxyAddr, &options->HTTPProxyPort) < 0)
REJECT("HTTPProxy failed to parse or resolve. Please fix.");
if (options->HTTPProxyPort == 0) { /* give it a default */
@@ -3428,7 +3601,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (options->HTTPSProxy) { /* parse it now */
- if (tor_addr_port_parse(options->HTTPSProxy,
+ if (tor_addr_port_lookup(options->HTTPSProxy,
&options->HTTPSProxyAddr, &options->HTTPSProxyPort) <0)
REJECT("HTTPSProxy failed to parse or resolve. Please fix.");
if (options->HTTPSProxyPort == 0) { /* give it a default */
@@ -3442,7 +3615,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (options->Socks4Proxy) { /* parse it now */
- if (tor_addr_port_parse(options->Socks4Proxy,
+ if (tor_addr_port_lookup(options->Socks4Proxy,
&options->Socks4ProxyAddr,
&options->Socks4ProxyPort) <0)
REJECT("Socks4Proxy failed to parse or resolve. Please fix.");
@@ -3452,7 +3625,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (options->Socks5Proxy) { /* parse it now */
- if (tor_addr_port_parse(options->Socks5Proxy,
+ if (tor_addr_port_lookup(options->Socks5Proxy,
&options->Socks5ProxyAddr,
&options->Socks5ProxyPort) <0)
REJECT("Socks5Proxy failed to parse or resolve. Please fix.");
@@ -3461,8 +3634,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
}
- if (options->Socks4Proxy && options->Socks5Proxy)
- REJECT("You cannot specify both Socks4Proxy and SOCKS5Proxy");
+ /* Check if more than one proxy type has been enabled. */
+ if (!!options->Socks4Proxy + !!options->Socks5Proxy +
+ !!options->HTTPSProxy + !!options->ClientTransportPlugin > 1)
+ REJECT("You have configured more than one proxy type. "
+ "(Socks4Proxy|Socks5Proxy|HTTPSProxy|ClientTransportPlugin)");
if (options->Socks5ProxyUsername) {
size_t len;
@@ -3565,8 +3741,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (check_nickname_list(options->MyFamily, "MyFamily", msg))
return -1;
for (cl = options->NodeFamilies; cl; cl = cl->next) {
- if (check_nickname_list(cl->value, "NodeFamily", msg))
+ routerset_t *rs = routerset_new();
+ if (routerset_parse(rs, cl->value, cl->key)) {
+ routerset_free(rs);
return -1;
+ }
+ routerset_free(rs);
}
if (validate_addr_policies(options, msg) < 0)
@@ -3579,11 +3759,20 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("If you set UseBridges, you must specify at least one bridge.");
if (options->UseBridges && !options->TunnelDirConns)
REJECT("If you set UseBridges, you must set TunnelDirConns.");
- if (options->Bridges) {
- for (cl = options->Bridges; cl; cl = cl->next) {
- if (parse_bridge_line(cl->value, 1)<0)
- REJECT("Bridge line did not parse. See logs for details.");
- }
+
+ for (cl = options->Bridges; cl; cl = cl->next) {
+ if (parse_bridge_line(cl->value, 1)<0)
+ REJECT("Bridge line did not parse. See logs for details.");
+ }
+
+ for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
+ if (parse_client_transport_line(cl->value, 1)<0)
+ REJECT("Transport line did not parse. See logs for details.");
+ }
+
+ for (cl = options->ServerTransportPlugin; cl; cl = cl->next) {
+ if (parse_server_transport_line(cl->value, 1)<0)
+ REJECT("Server transport line did not parse. See logs for details.");
}
if (options->ConstrainedSockets) {
@@ -3766,17 +3955,13 @@ options_validate(or_options_t *old_options, or_options_t *options,
static int
opt_streq(const char *s1, const char *s2)
{
- if (!s1 && !s2)
- return 1;
- else if (s1 && s2 && !strcmp(s1,s2))
- return 1;
- else
- return 0;
+ return 0 == strcmp_opt(s1, s2);
}
/** Check if any of the previous options have changed but aren't allowed to. */
static int
-options_transition_allowed(or_options_t *old, or_options_t *new_val,
+options_transition_allowed(const or_options_t *old,
+ const or_options_t *new_val,
char **msg)
{
if (!old)
@@ -3826,14 +4011,26 @@ options_transition_allowed(or_options_t *old, or_options_t *new_val,
return -1;
}
+ if (old->TokenBucketRefillInterval != new_val->TokenBucketRefillInterval) {
+ *msg = tor_strdup("While Tor is running, changing TokenBucketRefill"
+ "Interval is not allowed");
+ return -1;
+ }
+
+ if (old->DisableIOCP != new_val->DisableIOCP) {
+ *msg = tor_strdup("While Tor is running, changing DisableIOCP "
+ "is not allowed.");
+ return -1;
+ }
+
return 0;
}
/** Return 1 if any change from <b>old_options</b> to <b>new_options</b>
* will require us to rotate the CPU and DNS workers; else return 0. */
static int
-options_transition_affects_workers(or_options_t *old_options,
- or_options_t *new_options)
+options_transition_affects_workers(const or_options_t *old_options,
+ const or_options_t *new_options)
{
if (!opt_streq(old_options->DataDirectory, new_options->DataDirectory) ||
old_options->NumCPUs != new_options->NumCPUs ||
@@ -3856,8 +4053,8 @@ options_transition_affects_workers(or_options_t *old_options,
/** Return 1 if any change from <b>old_options</b> to <b>new_options</b>
* will require us to generate a new descriptor; else return 0. */
static int
-options_transition_affects_descriptor(or_options_t *old_options,
- or_options_t *new_options)
+options_transition_affects_descriptor(const or_options_t *old_options,
+ const or_options_t *new_options)
{
/* XXX We can be smarter here. If your DirPort isn't being
* published and you just turned it off, no need to republish. Etc. */
@@ -3878,7 +4075,8 @@ options_transition_affects_descriptor(or_options_t *old_options,
!opt_streq(old_options->ContactInfo, new_options->ContactInfo) ||
!opt_streq(old_options->MyFamily, new_options->MyFamily) ||
!opt_streq(old_options->AccountingStart, new_options->AccountingStart) ||
- old_options->AccountingMax != new_options->AccountingMax)
+ old_options->AccountingMax != new_options->AccountingMax ||
+ public_server_mode(old_options) != public_server_mode(new_options))
return 1;
return 0;
@@ -4061,6 +4259,8 @@ load_torrc_from_disk(int argc, char **argv)
"Unable to open configuration file \"%s\".", fname);
goto err;
}
+ } else {
+ log(LOG_NOTICE, LD_CONFIG, "Read configuration file \"%s\".", fname);
}
return cf;
@@ -4221,9 +4421,9 @@ options_init_from_string(const char *cf,
/* Change defaults. */
int i;
for (i = 0; testing_tor_network_defaults[i].name; ++i) {
- config_var_t *new_var = &testing_tor_network_defaults[i];
+ const config_var_t *new_var = &testing_tor_network_defaults[i];
config_var_t *old_var =
- config_find_option(&options_format, new_var->name);
+ config_find_option_mutable(&options_format, new_var->name);
tor_assert(new_var);
tor_assert(old_var);
old_var->initvalue = new_var->initvalue;
@@ -4299,8 +4499,8 @@ get_torrc_fname(void)
/** Adjust the address map based on the MapAddress elements in the
* configuration <b>options</b>
*/
-static void
-config_register_addressmaps(or_options_t *options)
+void
+config_register_addressmaps(const or_options_t *options)
{
smartlist_t *elts;
config_line_t *opt;
@@ -4349,6 +4549,35 @@ options_init_logs(or_options_t *options, int validate_only)
options->RunAsDaemon;
#endif
+ if (options->LogTimeGranularity <= 0) {
+ log_warn(LD_CONFIG, "Log time granularity '%d' has to be positive.",
+ options->LogTimeGranularity);
+ return -1;
+ } else if (1000 % options->LogTimeGranularity != 0 &&
+ options->LogTimeGranularity % 1000 != 0) {
+ int granularity = options->LogTimeGranularity;
+ if (granularity < 40) {
+ do granularity++;
+ while (1000 % granularity != 0);
+ } else if (granularity < 1000) {
+ granularity = 1000 / granularity;
+ while (1000 % granularity != 0)
+ granularity--;
+ granularity = 1000 / granularity;
+ } else {
+ granularity = 1000 * ((granularity / 1000) + 1);
+ }
+ log_warn(LD_CONFIG, "Log time granularity '%d' has to be either a "
+ "divisor or a multiple of 1 second. Changing to "
+ "'%d'.",
+ options->LogTimeGranularity, granularity);
+ if (!validate_only)
+ set_log_time_granularity(granularity);
+ } else {
+ if (!validate_only)
+ set_log_time_granularity(options->LogTimeGranularity);
+ }
+
ok = 1;
elts = smartlist_create();
@@ -4438,6 +4667,8 @@ parse_bridge_line(const char *line, int validate_only)
smartlist_t *items = NULL;
int r;
char *addrport=NULL, *fingerprint=NULL;
+ char *transport_name=NULL;
+ char *field1=NULL;
tor_addr_t addr;
uint16_t port = 0;
char digest[DIGEST_LEN];
@@ -4449,9 +4680,25 @@ parse_bridge_line(const char *line, int validate_only)
log_warn(LD_CONFIG, "Too few arguments to Bridge line.");
goto err;
}
- addrport = smartlist_get(items, 0);
+
+ /* field1 is either a transport name or addrport */
+ field1 = smartlist_get(items, 0);
smartlist_del_keeporder(items, 0);
- if (tor_addr_port_parse(addrport, &addr, &port)<0) {
+
+ if (!(strstr(field1, ".") || strstr(field1, ":"))) {
+ /* new-style bridge line */
+ transport_name = field1;
+ if (smartlist_len(items) < 1) {
+ log_warn(LD_CONFIG, "Too few items to Bridge line.");
+ goto err;
+ }
+ addrport = smartlist_get(items, 0);
+ smartlist_del_keeporder(items, 0);
+ } else {
+ addrport = field1;
+ }
+
+ if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
log_warn(LD_CONFIG, "Error parsing Bridge address '%s'", addrport);
goto err;
}
@@ -4475,26 +4722,282 @@ parse_bridge_line(const char *line, int validate_only)
}
if (!validate_only) {
- log_debug(LD_DIR, "Bridge at %s:%d (%s)", fmt_addr(&addr),
- (int)port,
+ log_debug(LD_DIR, "Bridge at %s:%d (transport: %s) (%s)",
+ fmt_addr(&addr), (int)port,
+ transport_name ? transport_name : "no transport",
fingerprint ? fingerprint : "no key listed");
- bridge_add_from_config(&addr, port, fingerprint ? digest : NULL);
+ bridge_add_from_config(&addr, port,
+ fingerprint ? digest : NULL, transport_name);
}
r = 0;
goto done;
- err:
+ err:
r = -1;
- done:
+ done:
SMARTLIST_FOREACH(items, char*, s, tor_free(s));
smartlist_free(items);
tor_free(addrport);
+ tor_free(transport_name);
tor_free(fingerprint);
return r;
}
+/** Read the contents of a ClientTransportPlugin line from
+ * <b>line</b>. Return 0 if the line is well-formed, and -1 if it
+ * isn't.
+ *
+ * If <b>validate_only</b> is 0, and the line is well-formed:
+ * - If it's an external proxy line, add the transport described in the line to
+ * our internal transport list.
+ * - If it's a managed proxy line, launch the managed proxy. */
+static int
+parse_client_transport_line(const char *line, int validate_only)
+{
+ smartlist_t *items = NULL;
+ int r;
+ char *field2=NULL;
+
+ const char *transports=NULL;
+ smartlist_t *transport_list=NULL;
+ char *addrport=NULL;
+ tor_addr_t addr;
+ uint16_t port = 0;
+ int socks_ver=PROXY_NONE;
+
+ /* managed proxy options */
+ int is_managed=0;
+ char **proxy_argv=NULL;
+ char **tmp=NULL;
+ int proxy_argc,i;
+
+ int line_length;
+
+ items = smartlist_create();
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+ line_length = smartlist_len(items);
+ if (line_length < 3) {
+ log_warn(LD_CONFIG, "Too few arguments on ClientTransportPlugin line.");
+ goto err;
+ }
+
+ /* Get the first line element, split it to commas into
+ transport_list (in case it's multiple transports) and validate
+ the transport names. */
+ transports = smartlist_get(items, 0);
+ transport_list = smartlist_create();
+ smartlist_split_string(transport_list, transports, ",",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport_name) {
+ if (!string_is_C_identifier(transport_name)) {
+ log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
+ transport_name);
+ goto err;
+ }
+ } SMARTLIST_FOREACH_END(transport_name);
+
+ /* field2 is either a SOCKS version or "exec" */
+ field2 = smartlist_get(items, 1);
+
+ if (!strcmp(field2,"socks4")) {
+ socks_ver = PROXY_SOCKS4;
+ } else if (!strcmp(field2,"socks5")) {
+ socks_ver = PROXY_SOCKS5;
+ } else if (!strcmp(field2,"exec")) {
+ is_managed=1;
+ } else {
+ log_warn(LD_CONFIG, "Strange ClientTransportPlugin field '%s'.",
+ field2);
+ goto err;
+ }
+
+ if (is_managed) { /* managed */
+ if (!validate_only) { /* if we are not just validating, use the
+ rest of the line as the argv of the proxy
+ to be launched */
+ proxy_argc = line_length-2;
+ tor_assert(proxy_argc > 0);
+ proxy_argv = tor_malloc_zero(sizeof(char*)*(proxy_argc+1));
+ tmp = proxy_argv;
+ for (i=0;i<proxy_argc;i++) { /* store arguments */
+ *tmp++ = smartlist_get(items, 2);
+ smartlist_del_keeporder(items, 2);
+ }
+ *tmp = NULL; /*terminated with NUL pointer, just like execve() likes it*/
+
+ /* kickstart the thing */
+ pt_kickstart_client_proxy(transport_list, proxy_argv);
+ }
+ } else { /* external */
+ if (smartlist_len(transport_list) != 1) {
+ log_warn(LD_CONFIG, "You can't have an external proxy with "
+ "more than one transports.");
+ goto err;
+ }
+
+ addrport = smartlist_get(items, 2);
+
+ if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
+ log_warn(LD_CONFIG, "Error parsing transport "
+ "address '%s'", addrport);
+ goto err;
+ }
+ if (!port) {
+ log_warn(LD_CONFIG,
+ "Transport address '%s' has no port.", addrport);
+ goto err;
+ }
+
+ if (!validate_only) {
+ transport_add_from_config(&addr, port, smartlist_get(transport_list, 0),
+ socks_ver);
+
+ log_info(LD_DIR, "Transport '%s' found at %s:%d",
+ transports, fmt_addr(&addr), (int)port);
+ }
+ }
+
+ r = 0;
+ goto done;
+
+ err:
+ r = -1;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ if (transport_list) {
+ SMARTLIST_FOREACH(transport_list, char*, s, tor_free(s));
+ smartlist_free(transport_list);
+ }
+
+ return r;
+}
+
+/** Read the contents of a ServerTransportPlugin line from
+ * <b>line</b>. Return 0 if the line is well-formed, and -1 if it
+ * isn't.
+ * If <b>validate_only</b> is 0, the line is well-formed, and it's a
+ * managed proxy line, launch the managed proxy. */
+static int
+parse_server_transport_line(const char *line, int validate_only)
+{
+ smartlist_t *items = NULL;
+ int r;
+ const char *transports=NULL;
+ smartlist_t *transport_list=NULL;
+ char *type=NULL;
+ char *addrport=NULL;
+ tor_addr_t addr;
+ uint16_t port = 0;
+
+ /* managed proxy options */
+ int is_managed=0;
+ char **proxy_argv=NULL;
+ char **tmp=NULL;
+ int proxy_argc,i;
+
+ int line_length;
+
+ items = smartlist_create();
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+ line_length = smartlist_len(items);
+ if (line_length < 3) {
+ log_warn(LD_CONFIG, "Too few arguments on ServerTransportPlugin line.");
+ goto err;
+ }
+
+ /* Get the first line element, split it to commas into
+ transport_list (in case it's multiple transports) and validate
+ the transport names. */
+ transports = smartlist_get(items, 0);
+ transport_list = smartlist_create();
+ smartlist_split_string(transport_list, transports, ",",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport_name) {
+ if (!string_is_C_identifier(transport_name)) {
+ log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
+ transport_name);
+ goto err;
+ }
+ } SMARTLIST_FOREACH_END(transport_name);
+
+ type = smartlist_get(items, 1);
+
+ if (!strcmp(type, "exec")) {
+ is_managed=1;
+ } else if (!strcmp(type, "proxy")) {
+ is_managed=0;
+ } else {
+ log_warn(LD_CONFIG, "Strange ServerTransportPlugin type '%s'", type);
+ goto err;
+ }
+
+ if (is_managed) { /* managed */
+ if (!validate_only) {
+ proxy_argc = line_length-2;
+ tor_assert(proxy_argc > 0);
+ proxy_argv = tor_malloc_zero(sizeof(char*)*(proxy_argc+1));
+ tmp = proxy_argv;
+
+ for (i=0;i<proxy_argc;i++) { /* store arguments */
+ *tmp++ = smartlist_get(items, 2);
+ smartlist_del_keeporder(items, 2);
+ }
+ *tmp = NULL; /*terminated with NUL pointer, just like execve() likes it*/
+
+ /* kickstart the thing */
+ pt_kickstart_server_proxy(transport_list, proxy_argv);
+ }
+ } else { /* external */
+ if (smartlist_len(transport_list) != 1) {
+ log_warn(LD_CONFIG, "You can't have an external proxy with "
+ "more than one transports.");
+ goto err;
+ }
+
+ addrport = smartlist_get(items, 2);
+
+ if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
+ log_warn(LD_CONFIG, "Error parsing transport "
+ "address '%s'", addrport);
+ goto err;
+ }
+ if (!port) {
+ log_warn(LD_CONFIG,
+ "Transport address '%s' has no port.", addrport);
+ goto err;
+ }
+
+ if (!validate_only) {
+ log_info(LD_DIR, "Server transport '%s' at %s:%d.",
+ transports, fmt_addr(&addr), (int)port);
+ }
+ }
+
+ r = 0;
+ goto done;
+
+ err:
+ r = -1;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ if (transport_list) {
+ SMARTLIST_FOREACH(transport_list, char*, s, tor_free(s));
+ smartlist_free(transport_list);
+ }
+
+ return r;
+}
+
/** Read the contents of a DirServer line from <b>line</b>. If
* <b>validate_only</b> is 0, and the line is well-formed, and it
* shares any bits with <b>required_type</b> or <b>required_type</b>
@@ -4502,7 +5005,7 @@ parse_bridge_line(const char *line, int validate_only)
* bits it's missing) as a valid authority. Return 0 on success,
* or -1 if the line isn't well-formed or if we can't add it. */
static int
-parse_dir_server_line(const char *line, authority_type_t required_type,
+parse_dir_server_line(const char *line, dirinfo_type_t required_type,
int validate_only)
{
smartlist_t *items = NULL;
@@ -4511,7 +5014,7 @@ parse_dir_server_line(const char *line, authority_type_t required_type,
uint16_t dir_port = 0, or_port = 0;
char digest[DIGEST_LEN];
char v3_digest[DIGEST_LEN];
- authority_type_t type = V2_AUTHORITY;
+ dirinfo_type_t type = V2_DIRINFO;
int is_not_hidserv_authority = 0, is_not_v2_authority = 0;
items = smartlist_create();
@@ -4532,13 +5035,13 @@ parse_dir_server_line(const char *line, authority_type_t required_type,
if (TOR_ISDIGIT(flag[0]))
break;
if (!strcasecmp(flag, "v1")) {
- type |= (V1_AUTHORITY | HIDSERV_AUTHORITY);
+ type |= (V1_DIRINFO | HIDSERV_DIRINFO);
} else if (!strcasecmp(flag, "hs")) {
- type |= HIDSERV_AUTHORITY;
+ type |= HIDSERV_DIRINFO;
} else if (!strcasecmp(flag, "no-hs")) {
is_not_hidserv_authority = 1;
} else if (!strcasecmp(flag, "bridge")) {
- type |= BRIDGE_AUTHORITY;
+ type |= BRIDGE_DIRINFO;
} else if (!strcasecmp(flag, "no-v2")) {
is_not_v2_authority = 1;
} else if (!strcasecmpstart(flag, "orport=")) {
@@ -4555,7 +5058,7 @@ parse_dir_server_line(const char *line, authority_type_t required_type,
log_warn(LD_CONFIG, "Bad v3 identity digest '%s' on DirServer line",
flag);
} else {
- type |= V3_AUTHORITY;
+ type |= V3_DIRINFO|EXTRAINFO_DIRINFO|MICRODESC_DIRINFO;
}
} else {
log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirServer line",
@@ -4565,9 +5068,9 @@ parse_dir_server_line(const char *line, authority_type_t required_type,
smartlist_del_keeporder(items, 0);
}
if (is_not_hidserv_authority)
- type &= ~HIDSERV_AUTHORITY;
+ type &= ~HIDSERV_DIRINFO;
if (is_not_v2_authority)
- type &= ~V2_AUTHORITY;
+ type &= ~V2_DIRINFO;
if (smartlist_len(items) < 2) {
log_warn(LD_CONFIG, "Too few arguments to DirServer line.");
@@ -4575,7 +5078,7 @@ parse_dir_server_line(const char *line, authority_type_t required_type,
}
addrport = smartlist_get(items, 0);
smartlist_del_keeporder(items, 0);
- if (parse_addr_port(LOG_WARN, addrport, &address, NULL, &dir_port)<0) {
+ if (addr_port_lookup(LOG_WARN, addrport, &address, NULL, &dir_port)<0) {
log_warn(LD_CONFIG, "Error parsing DirServer address '%s'", addrport);
goto err;
}
@@ -4630,6 +5133,374 @@ parse_dir_server_line(const char *line, authority_type_t required_type,
return r;
}
+/** Free all storage held in <b>port</b> */
+static void
+port_cfg_free(port_cfg_t *port)
+{
+ tor_free(port);
+}
+
+/** Warn for every port in <b>ports</b> that is on a publicly routable
+ * address. */
+static void
+warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname)
+{
+ SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+ if (port->is_unix_addr) {
+ /* Unix sockets aren't accessible over a network. */
+ } else if (!tor_addr_is_internal(&port->addr, 1)) {
+ log_warn(LD_CONFIG, "You specified a public address for %sPort. "
+ "Other people on the Internet might find your computer and "
+ "use it as an open proxy. Please don't allow this unless you "
+ "have a good reason.", portname);
+ } else if (!tor_addr_is_loopback(&port->addr)) {
+ log_notice(LD_CONFIG, "You configured a non-loopback address for "
+ "%sPort. This allows everybody on your local network to use "
+ "your machine as a proxy. Make sure this is what you wanted.",
+ portname);
+ }
+ } SMARTLIST_FOREACH_END(port);
+}
+
+#define CL_PORT_NO_OPTIONS (1u<<0)
+#define CL_PORT_WARN_NONLOCAL (1u<<1)
+#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
+
+/**
+ * Parse port configuration for a single client port type.
+ *
+ * Read entries of the "FooPort" type from the list <b>ports</b>, and
+ * entries of the "FooListenAddress" type from the list
+ * <b>listenaddrs</b>. Two syntaxes are supported: a legacy syntax
+ * where FooPort is at most a single entry containing a port number and
+ * where FooListenAddress has any number of address:port combinations;
+ * and a new syntax where there are no FooListenAddress entries and
+ * where FooPort can have any number of entries of the format
+ * "[Address:][Port] IsolationOptions".
+ *
+ * In log messages, describe the port type as <b>portname</b>.
+ *
+ * If no address is specified, default to <b>defaultaddr</b>. If no
+ * FooPort is given, default to defaultport (if 0, there is no default).
+ *
+ * If CL_PORT_NO_OPTIONS is set in <b>flags</b>, do not allow stream
+ * isolation options in the FooPort entries.
+ *
+ * If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the
+ * ports are not on a local address.
+ *
+ * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn
+ * if FooListenAddress is set but FooPort is 0.
+ *
+ * On success, if <b>out</b> is given, add a new port_cfg_t entry to
+ * <b>out</b> for every port that the client should listen on. Return 0
+ * on success, -1 on failure.
+ */
+static int
+parse_client_port_config(smartlist_t *out,
+ const config_line_t *ports,
+ const config_line_t *listenaddrs,
+ const char *portname,
+ int listener_type,
+ const char *defaultaddr,
+ int defaultport,
+ unsigned flags)
+{
+ smartlist_t *elts;
+ int retval = -1;
+ const unsigned allow_client_options = !(flags & CL_PORT_NO_OPTIONS);
+ const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
+ const unsigned allow_spurious_listenaddr =
+ flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
+
+ /* FooListenAddress is deprecated; let's make it work like it used to work,
+ * though. */
+ if (listenaddrs) {
+ int mainport = defaultport;
+
+ if (ports && ports->next) {
+ log_warn(LD_CONFIG, "%sListenAddress can't be used when there are "
+ "multiple %sPort lines", portname, portname);
+ return -1;
+ } else if (ports) {
+ if (!strcmp(ports->value, "auto")) {
+ mainport = CFG_AUTO_PORT;
+ } else {
+ int ok;
+ mainport = (int)tor_parse_long(ports->value, 10, 0, 65535, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG, "%sListenAddress can only be used with a single "
+ "%sPort with value \"auto\" or 1-65535.",
+ portname, portname);
+ return -1;
+ }
+ }
+ }
+
+ if (mainport == 0) {
+ if (allow_spurious_listenaddr)
+ return 1;
+ log_warn(LD_CONFIG, "%sPort must be defined if %sListenAddress is used",
+ portname, portname);
+ return -1;
+ }
+
+ for (; listenaddrs; listenaddrs = listenaddrs->next) {
+ tor_addr_t addr;
+ uint16_t port = 0;
+ if (tor_addr_port_lookup(listenaddrs->value, &addr, &port) < 0) {
+ log_warn(LD_CONFIG, "Unable to parse %sListenAddress '%s'",
+ portname, listenaddrs->value);
+ return -1;
+ }
+ if (out) {
+ port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ cfg->type = listener_type;
+ cfg->port = port ? port : mainport;
+ tor_addr_copy(&cfg->addr, &addr);
+ cfg->session_group = SESSION_GROUP_UNSET;
+ cfg->isolation_flags = ISO_DEFAULT;
+ smartlist_add(out, cfg);
+ }
+ }
+
+ if (warn_nonlocal && out)
+ warn_nonlocal_client_ports(out, portname);
+ return 0;
+ } /* end if (listenaddrs) */
+
+ /* No ListenAddress lines. If there's no FooPort, then maybe make a default
+ * one. */
+ if (! ports) {
+ if (defaultport && out) {
+ port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ cfg->type = listener_type;
+ cfg->port = defaultport;
+ tor_addr_parse(&cfg->addr, defaultaddr);
+ cfg->session_group = SESSION_GROUP_UNSET;
+ cfg->isolation_flags = ISO_DEFAULT;
+ smartlist_add(out, cfg);
+ }
+ return 0;
+ }
+
+ /* At last we can actually parse the FooPort lines. The syntax is:
+ * [Addr:](Port|auto) [Options].*/
+ elts = smartlist_create();
+
+ for (; ports; ports = ports->next) {
+ tor_addr_t addr;
+ int port;
+ int sessiongroup = SESSION_GROUP_UNSET;
+ unsigned isolation = ISO_DEFAULT;
+
+ char *addrport;
+ uint16_t ptmp=0;
+ int ok;
+
+ smartlist_split_string(elts, ports->value, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(elts) == 0) {
+ log_warn(LD_CONFIG, "Invalid %sPort line with no value", portname);
+ goto err;
+ }
+
+ if (!allow_client_options && smartlist_len(elts) > 1) {
+ log_warn(LD_CONFIG, "Too many options on %sPort line", portname);
+ goto err;
+ }
+
+ /* Now parse the addr/port value */
+ addrport = smartlist_get(elts, 0);
+ if (!strcmp(addrport, "auto")) {
+ port = CFG_AUTO_PORT;
+ tor_addr_parse(&addr, defaultaddr);
+ } else if (!strcasecmpend(addrport, ":auto")) {
+ char *addrtmp = tor_strndup(addrport, strlen(addrport)-5);
+ port = CFG_AUTO_PORT;
+ if (tor_addr_port_lookup(addrtmp, &addr, &ptmp)<0 || ptmp) {
+ log_warn(LD_CONFIG, "Invalid address '%s' for %sPort",
+ escaped(addrport), portname);
+ tor_free(addrtmp);
+ goto err;
+ }
+ } else {
+ /* Try parsing integer port before address, because, who knows?
+ "9050" might be a valid address. */
+ port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL);
+ if (ok) {
+ tor_addr_parse(&addr, defaultaddr);
+ } else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) {
+ if (ptmp == 0) {
+ log_warn(LD_CONFIG, "%sPort line has address but no port", portname);
+ goto err;
+ }
+ port = ptmp;
+ } else {
+ log_warn(LD_CONFIG, "Couldn't parse address '%s' for %sPort",
+ escaped(addrport), portname);
+ goto err;
+ }
+ }
+
+ /* Now parse the rest of the options, if any. */
+ SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
+ int no = 0, isoflag = 0;
+ const char *elt_orig = elt;
+ if (elt_sl_idx == 0)
+ continue; /* Skip addr:port */
+ if (!strcasecmpstart(elt, "SessionGroup=")) {
+ int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
+ 10, 0, INT_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
+ portname, escaped(elt));
+ goto err;
+ }
+ if (sessiongroup >= 0) {
+ log_warn(LD_CONFIG, "Multiple SessionGroup options on %sPort",
+ portname);
+ goto err;
+ }
+ sessiongroup = group;
+ continue;
+ }
+
+ if (!strcasecmpstart(elt, "No")) {
+ no = 1;
+ elt += 2;
+ }
+ if (!strcasecmpend(elt, "s"))
+ elt[strlen(elt)-1] = '\0'; /* kill plurals. */
+
+ if (!strcasecmp(elt, "IsolateDestPort")) {
+ isoflag = ISO_DESTPORT;
+ } else if (!strcasecmp(elt, "IsolateDestAddr")) {
+ isoflag = ISO_DESTADDR;
+ } else if (!strcasecmp(elt, "IsolateSOCKSAuth")) {
+ isoflag = ISO_SOCKSAUTH;
+ } else if (!strcasecmp(elt, "IsolateClientProtocol")) {
+ isoflag = ISO_CLIENTPROTO;
+ } else if (!strcasecmp(elt, "IsolateClientAddr")) {
+ isoflag = ISO_CLIENTADDR;
+ } else {
+ log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
+ portname, escaped(elt_orig));
+ }
+
+ if (no) {
+ isolation &= ~isoflag;
+ } else {
+ isolation |= isoflag;
+ }
+ } SMARTLIST_FOREACH_END(elt);
+
+ if (out && port) {
+ port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t));
+ cfg->type = listener_type;
+ cfg->port = port;
+ tor_addr_copy(&cfg->addr, &addr);
+ cfg->session_group = sessiongroup;
+ cfg->isolation_flags = isolation;
+ smartlist_add(out, cfg);
+ }
+ SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
+ smartlist_clear(elts);
+ }
+
+ if (warn_nonlocal && out)
+ warn_nonlocal_client_ports(out, portname);
+
+ retval = 0;
+ err:
+ SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
+ smartlist_free(elts);
+ return retval;
+}
+
+/** Parse all client port types (Socks, DNS, Trans, NATD) from
+ * <b>options</b>. On success, set *<b>n_ports_out</b> to the number of
+ * ports that are listed and return 0. On failure, set *<b>msg</b> to a
+ * description of the problem and return -1.
+ *
+ * If <b>validate_only</b> is false, set configured_client_ports to the
+ * new list of ports parsed from <b>options</b>.
+ **/
+static int
+parse_client_ports(const or_options_t *options, int validate_only,
+ char **msg, int *n_ports_out)
+{
+ smartlist_t *ports;
+ int retval = -1;
+
+ ports = smartlist_create();
+
+ *n_ports_out = 0;
+
+ if (parse_client_port_config(ports,
+ options->SocksPort, options->SocksListenAddress,
+ "Socks", CONN_TYPE_AP_LISTENER,
+ "127.0.0.1", 9050,
+ CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR) < 0) {
+ *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
+ goto err;
+ }
+ if (parse_client_port_config(ports,
+ options->DNSPort, options->DNSListenAddress,
+ "DNS", CONN_TYPE_AP_DNS_LISTENER,
+ "127.0.0.1", 0,
+ CL_PORT_WARN_NONLOCAL) < 0) {
+ *msg = tor_strdup("Invalid DNSPort/DNSListenAddress configuration");
+ goto err;
+ }
+ if (parse_client_port_config(ports,
+ options->TransPort, options->TransListenAddress,
+ "Trans", CONN_TYPE_AP_TRANS_LISTENER,
+ "127.0.0.1", 0,
+ CL_PORT_WARN_NONLOCAL) < 0) {
+ *msg = tor_strdup("Invalid TransPort/TransListenAddress configuration");
+ goto err;
+ }
+ if (parse_client_port_config(ports,
+ options->NATDPort, options->NATDListenAddress,
+ "NATD", CONN_TYPE_AP_NATD_LISTENER,
+ "127.0.0.1", 0,
+ CL_PORT_WARN_NONLOCAL) < 0) {
+ *msg = tor_strdup("Invalid NatdPort/NatdListenAddress configuration");
+ goto err;
+ }
+
+ *n_ports_out = smartlist_len(ports);
+
+ if (!validate_only) {
+ if (configured_client_ports) {
+ SMARTLIST_FOREACH(configured_client_ports,
+ port_cfg_t *, p, port_cfg_free(p));
+ smartlist_free(configured_client_ports);
+ }
+ configured_client_ports = ports;
+ ports = NULL; /* prevent free below. */
+ }
+
+ retval = 0;
+ err:
+ if (ports) {
+ SMARTLIST_FOREACH(ports, port_cfg_t *, p, port_cfg_free(p));
+ smartlist_free(ports);
+ }
+ return retval;
+}
+
+/** Return a list of port_cfg_t for client ports parsed from the
+ * options. */
+const smartlist_t *
+get_configured_client_ports(void)
+{
+ if (!configured_client_ports)
+ configured_client_ports = smartlist_create();
+ return configured_client_ports;
+}
+
/** Adjust the value of options->DataDirectory, or fill it in if it's
* absent. Return 0 on success, -1 on failure. */
static int
@@ -4701,7 +5572,7 @@ validate_data_directory(or_options_t *options)
* doesn't begin with GENERATED_FILE_PREFIX, rename it. Otherwise
* replace it. Return 0 on success, -1 on failure. */
static int
-write_configuration_file(const char *fname, or_options_t *options)
+write_configuration_file(const char *fname, const or_options_t *options)
{
char *old_val=NULL, *new_val=NULL, *new_conf=NULL;
int rename_old = 0, r;
@@ -4842,6 +5713,26 @@ static struct unit_table_t time_units[] = {
{ NULL, 0 },
};
+/** Table to map the names of time units to the number of milliseconds
+ * they contain. */
+static struct unit_table_t time_msec_units[] = {
+ { "", 1 },
+ { "msec", 1 },
+ { "millisecond", 1 },
+ { "milliseconds", 1 },
+ { "second", 1000 },
+ { "seconds", 1000 },
+ { "minute", 60*1000 },
+ { "minutes", 60*1000 },
+ { "hour", 60*60*1000 },
+ { "hours", 60*60*1000 },
+ { "day", 24*60*60*1000 },
+ { "days", 24*60*60*1000 },
+ { "week", 7*24*60*60*1000 },
+ { "weeks", 7*24*60*60*1000 },
+ { NULL, 0 },
+};
+
/** Parse a string <b>val</b> containing a number, zero or more
* spaces, and an optional unit string. If the unit appears in the
* table <b>u</b>, then multiply the number by the unit multiplier.
@@ -4905,6 +5796,25 @@ config_parse_memunit(const char *s, int *ok)
return u;
}
+/** Parse a string in the format "number unit", where unit is a unit of
+ * time in milliseconds. On success, set *<b>ok</b> to true and return
+ * the number of milliseconds in the provided interval. Otherwise, set
+ * *<b>ok</b> to 0 and return -1. */
+static int
+config_parse_msec_interval(const char *s, int *ok)
+{
+ uint64_t r;
+ r = config_parse_units(s, time_msec_units, ok);
+ if (!ok)
+ return -1;
+ if (r > INT_MAX) {
+ log_warn(LD_CONFIG, "Msec interval '%s' is too long", s);
+ *ok = 0;
+ return -1;
+ }
+ return (int)r;
+}
+
/** Parse a string in the format "number unit", where unit is a unit of time.
* On success, set *<b>ok</b> to true and return the number of seconds in
* the provided interval. Otherwise, set *<b>ok</b> to 0 and return -1.
@@ -4924,13 +5834,29 @@ config_parse_interval(const char *s, int *ok)
return (int)r;
}
+/** Return the number of cpus configured in <b>options</b>. If we are
+ * told to auto-detect the number of cpus, return the auto-detected number. */
+int
+get_num_cpus(const or_options_t *options)
+{
+ if (options->NumCPUs == 0) {
+ int n = compute_num_cpus();
+ return (n >= 1) ? n : 1;
+ } else {
+ return options->NumCPUs;
+ }
+}
+
/**
* Initialize the libevent library.
*/
static void
-init_libevent(void)
+init_libevent(const or_options_t *options)
{
const char *badness=NULL;
+ tor_libevent_cfg cfg;
+
+ tor_assert(options);
configure_libevent_logging();
/* If the kernel complains that some method (say, epoll) doesn't
@@ -4940,7 +5866,12 @@ init_libevent(void)
tor_check_libevent_header_compatibility();
- tor_libevent_initialize();
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.disable_iocp = options->DisableIOCP;
+ cfg.num_cpus = get_num_cpus(options);
+ cfg.msec_per_tick = options->TokenBucketRefillInterval;
+
+ tor_libevent_initialize(&cfg);
suppress_libevent_log_msg(NULL);
@@ -4979,7 +5910,7 @@ get_or_state(void)
* Note: Consider using the get_datadir_fname* macros in or.h.
*/
char *
-options_get_datadir_fname2_suffix(or_options_t *options,
+options_get_datadir_fname2_suffix(const or_options_t *options,
const char *sub1, const char *sub2,
const char *suffix)
{
@@ -5014,6 +5945,69 @@ options_get_datadir_fname2_suffix(or_options_t *options,
return fname;
}
+/** Return true if <b>line</b> is a valid state TransportProxy line.
+ * Return false otherwise. */
+static int
+state_transport_line_is_valid(const char *line)
+{
+ smartlist_t *items = NULL;
+ char *addrport=NULL;
+ tor_addr_t addr;
+ uint16_t port = 0;
+ int r;
+
+ items = smartlist_create();
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+ if (smartlist_len(items) != 2) {
+ log_warn(LD_CONFIG, "state: Not enough arguments in TransportProxy line.");
+ goto err;
+ }
+
+ addrport = smartlist_get(items, 1);
+ if (tor_addr_port_lookup(addrport, &addr, &port) < 0) {
+ log_warn(LD_CONFIG, "state: Could not parse addrport.");
+ goto err;
+ }
+
+ if (!port) {
+ log_warn(LD_CONFIG, "state: Transport line did not contain port.");
+ goto err;
+ }
+
+ r = 1;
+ goto done;
+
+ err:
+ r = 0;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ return r;
+}
+
+/** Return 0 if all TransportProxy lines in <b>state</b> are well
+ * formed. Otherwise, return -1. */
+static int
+validate_transports_in_state(or_state_t *state)
+{
+ int broken = 0;
+ config_line_t *line;
+
+ for (line = state->TransportProxies ; line ; line = line->next) {
+ tor_assert(!strcmp(line->key, "TransportProxy"));
+ if (!state_transport_line_is_valid(line->value))
+ broken = 1;
+ }
+
+ if (broken)
+ log_warn(LD_CONFIG, "state: State file seems to be broken.");
+
+ return 0;
+}
+
/** Return 0 if every setting in <b>state</b> is reasonable, and a
* permissible transition from <b>old_state</b>. Else warn and return -1.
* Should have no side effects, except for normalizing the contents of
@@ -5032,6 +6026,9 @@ or_state_validate(or_state_t *old_state, or_state_t *state,
if (entry_guards_parse_state(state, 0, msg)<0)
return -1;
+ if (validate_transports_in_state(state)<0)
+ return -1;
+
return 0;
}
@@ -5264,6 +6261,150 @@ or_state_save(time_t now)
return 0;
}
+/** Return the config line for transport <b>transport</b> in the current state.
+ * Return NULL if there is no config line for <b>transport</b>. */
+static config_line_t *
+get_transport_in_state_by_name(const char *transport)
+{
+ or_state_t *or_state = get_or_state();
+ config_line_t *line;
+ config_line_t *ret = NULL;
+ smartlist_t *items = NULL;
+
+ for (line = or_state->TransportProxies ; line ; line = line->next) {
+ tor_assert(!strcmp(line->key, "TransportProxy"));
+
+ items = smartlist_create();
+ smartlist_split_string(items, line->value, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+ if (smartlist_len(items) != 2) /* broken state */
+ goto done;
+
+ if (!strcmp(smartlist_get(items, 0), transport)) {
+ ret = line;
+ goto done;
+ }
+
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ items = NULL;
+ }
+
+ done:
+ if (items) {
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ }
+ return ret;
+}
+
+/** Return string containing the address:port part of the
+ * TransportProxy <b>line</b> for transport <b>transport</b>.
+ * If the line is corrupted, return NULL. */
+static const char *
+get_transport_bindaddr(const char *line, const char *transport)
+{
+ char *line_tmp = NULL;
+
+ if (strlen(line) < strlen(transport) + 2) {
+ goto broken_state;
+ } else {
+ /* line should start with the name of the transport and a space.
+ (for example, "obfs2 127.0.0.1:47245") */
+ tor_asprintf(&line_tmp, "%s ", transport);
+ if (strcmpstart(line, line_tmp))
+ goto broken_state;
+
+ tor_free(line_tmp);
+ return (line+strlen(transport)+1);
+ }
+
+ broken_state:
+ tor_free(line_tmp);
+ return NULL;
+}
+
+/** Return a static string containing the address:port a proxy
+ * transport should bind on. */
+const char *
+get_bindaddr_for_transport(const char *transport)
+{
+ static const char default_addrport[] = "127.0.0.1:0";
+ const char *bindaddr = NULL;
+
+ config_line_t *line = get_transport_in_state_by_name(transport);
+ if (!line)
+ return default_addrport;
+
+ bindaddr = get_transport_bindaddr(line->value, transport);
+
+ return bindaddr ? bindaddr : default_addrport;
+}
+
+/** Save <b>transport</b> listening on <b>addr</b>:<b>port</b> to
+ state */
+void
+save_transport_to_state(const char *transport,
+ const tor_addr_t *addr, uint16_t port)
+{
+ or_state_t *state = get_or_state();
+
+ char *transport_addrport=NULL;
+
+ /** find where to write on the state */
+ config_line_t **next, *line;
+
+ /* see if this transport is already stored in state */
+ config_line_t *transport_line =
+ get_transport_in_state_by_name(transport);
+
+ if (transport_line) { /* if transport already exists in state... */
+ const char *prev_bindaddr = /* get its addrport... */
+ get_transport_bindaddr(transport_line->value, transport);
+ tor_asprintf(&transport_addrport, "%s:%d", fmt_addr(addr), (int)port);
+
+ /* if transport in state has the same address as this one, life is good */
+ if (!strcmp(prev_bindaddr, transport_addrport)) {
+ log_info(LD_CONFIG, "Transport seems to have spawned on its usual "
+ "address:port.");
+ goto done;
+ } else { /* if addrport in state is different than the one we got */
+ log_info(LD_CONFIG, "Transport seems to have spawned on different "
+ "address:port. Let's update the state file with the new "
+ "address:port");
+ tor_free(transport_line->value); /* free the old line */
+ tor_asprintf(&transport_line->value, "%s %s:%d", transport,
+ fmt_addr(addr),
+ (int) port); /* replace old addrport line with new line */
+ }
+ } else { /* never seen this one before; save it in state for next time */
+ log_info(LD_CONFIG, "It's the first time we see this transport. "
+ "Let's save its address:port");
+ next = &state->TransportProxies;
+ /* find the last TransportProxy line in the state and point 'next'
+ right after it */
+ line = state->TransportProxies;
+ while (line) {
+ next = &(line->next);
+ line = line->next;
+ }
+
+ /* allocate space for the new line and fill it in */
+ *next = line = tor_malloc_zero(sizeof(config_line_t));
+ line->key = tor_strdup("TransportProxy");
+ tor_asprintf(&line->value, "%s %s:%d", transport,
+ fmt_addr(addr), (int) port);
+
+ next = &(line->next);
+ }
+
+ if (!get_options()->AvoidDiskWrites)
+ or_state_mark_dirty(state, 0);
+
+ done:
+ tor_free(transport_addrport);
+}
+
/** Given a file name check to see whether the file exists but has not been
* modified for a very long time. If so, remove it. */
void
@@ -5296,7 +6437,7 @@ getinfo_helper_config(control_connection_t *conn,
smartlist_t *sl = smartlist_create();
int i;
for (i = 0; _option_vars[i].name; ++i) {
- config_var_t *var = &_option_vars[i];
+ const config_var_t *var = &_option_vars[i];
const char *type;
char *line;
switch (var->type) {
@@ -5305,9 +6446,11 @@ getinfo_helper_config(control_connection_t *conn,
case CONFIG_TYPE_UINT: type = "Integer"; break;
case CONFIG_TYPE_PORT: type = "Port"; break;
case CONFIG_TYPE_INTERVAL: type = "TimeInterval"; break;
+ case CONFIG_TYPE_MSEC_INTERVAL: type = "TimeMsecInterval"; break;
case CONFIG_TYPE_MEMUNIT: type = "DataSize"; break;
case CONFIG_TYPE_DOUBLE: type = "Float"; break;
case CONFIG_TYPE_BOOL: type = "Boolean"; break;
+ case CONFIG_TYPE_AUTOBOOL: type = "Boolean+Auto"; break;
case CONFIG_TYPE_ISOTIME: type = "Time"; break;
case CONFIG_TYPE_ROUTERSET: type = "RouterList"; break;
case CONFIG_TYPE_CSV: type = "CommaList"; break;
diff --git a/src/or/config.h b/src/or/config.h
index 78a67dddf..76f6841d7 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -13,7 +13,8 @@
#define _TOR_CONFIG_H
const char *get_dirportfrontpage(void);
-or_options_t *get_options(void);
+const or_options_t *get_options(void);
+or_options_t *get_options_mutable(void);
int set_options(or_options_t *new_val, char **msg);
void config_free_all(void);
const char *safe_str_client(const char *address);
@@ -26,21 +27,21 @@ int config_get_lines(const char *string, config_line_t **result);
void config_free_lines(config_line_t *front);
setopt_err_t options_trial_assign(config_line_t *list, int use_defaults,
int clear_first, char **msg);
-int resolve_my_address(int warn_severity, or_options_t *options,
+int resolve_my_address(int warn_severity, const or_options_t *options,
uint32_t *addr, char **hostname_out);
int is_local_addr(const tor_addr_t *addr) ATTR_PURE;
void options_init(or_options_t *options);
-char *options_dump(or_options_t *options, int minimal);
+char *options_dump(const or_options_t *options, int minimal);
int options_init_from_torrc(int argc, char **argv);
setopt_err_t options_init_from_string(const char *cf,
int command, const char *command_arg, char **msg);
int option_is_recognized(const char *key);
const char *option_get_canonical_name(const char *key);
-config_line_t *option_get_assignment(or_options_t *options,
+config_line_t *option_get_assignment(const or_options_t *options,
const char *key);
int options_save_current(void);
const char *get_torrc_fname(void);
-char *options_get_datadir_fname2_suffix(or_options_t *options,
+char *options_get_datadir_fname2_suffix(const or_options_t *options,
const char *sub1, const char *sub2,
const char *suffix);
#define get_datadir_fname2_suffix(sub1, sub2, suffix) \
@@ -57,23 +58,35 @@ char *options_get_datadir_fname2_suffix(or_options_t *options,
#define get_datadir_fname_suffix(sub1, suffix) \
get_datadir_fname2_suffix((sub1), NULL, (suffix))
+int get_num_cpus(const or_options_t *options);
+
or_state_t *get_or_state(void);
int did_last_state_file_write_fail(void);
int or_state_save(time_t now);
-int options_need_geoip_info(or_options_t *options, const char **reason_out);
+const smartlist_t *get_configured_client_ports(void);
+
+int options_need_geoip_info(const or_options_t *options,
+ const char **reason_out);
+
+void save_transport_to_state(const char *transport_name,
+ const tor_addr_t *addr, uint16_t port);
+const char *get_bindaddr_for_transport(const char *transport);
+
int getinfo_helper_config(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
const char *tor_get_digests(void);
-uint32_t get_effective_bwrate(or_options_t *options);
-uint32_t get_effective_bwburst(or_options_t *options);
+uint32_t get_effective_bwrate(const or_options_t *options);
+uint32_t get_effective_bwburst(const or_options_t *options);
#ifdef CONFIG_PRIVATE
/* Used only by config.c and test.c */
or_options_t *options_new(void);
#endif
+void config_register_addressmaps(const or_options_t *options);
+
#endif
diff --git a/src/or/connection.c b/src/or/connection.c
index 2049f4240..bf39a5cb9 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -36,17 +36,28 @@
#include "router.h"
#include "routerparse.h"
+#ifdef USE_BUFFEREVENTS
+#include <event2/event.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
static connection_t *connection_create_listener(
const struct sockaddr *listensockaddr,
socklen_t listensocklen, int type,
- char* address);
+ const char *address,
+ const port_cfg_t *portcfg);
static void connection_init(time_t now, connection_t *conn, int type,
int socket_family);
static int connection_init_accepted_conn(connection_t *conn,
- uint8_t listener_type);
+ const listener_connection_t *listener);
static int connection_handle_listener_read(connection_t *conn, int new_type);
+#ifndef USE_BUFFEREVENTS
static int connection_bucket_should_increase(int bucket,
or_connection_t *conn);
+#endif
static int connection_finished_flushing(connection_t *conn);
static int connection_flushed_some(connection_t *conn);
static int connection_finished_connecting(connection_t *conn);
@@ -60,6 +71,8 @@ static void set_constrained_socket_buffers(tor_socket_t sock, int size);
static const char *connection_proxy_state_to_string(int state);
static int connection_read_https_proxy_response(connection_t *conn);
static void connection_send_socks5_connect(connection_t *conn);
+static const char *proxy_type_to_string(int proxy_type);
+static int get_proxy_type(void);
/** The last IPv4 address that our network interface seemed to have been
* binding to, in host order. We use this to detect when our IP changes. */
@@ -68,6 +81,15 @@ static uint32_t last_interface_ip = 0;
* Used to detect IP address changes. */
static smartlist_t *outgoing_addrs = NULL;
+#define CASE_ANY_LISTENER_TYPE \
+ case CONN_TYPE_OR_LISTENER: \
+ case CONN_TYPE_AP_LISTENER: \
+ case CONN_TYPE_DIR_LISTENER: \
+ case CONN_TYPE_CONTROL_LISTENER: \
+ case CONN_TYPE_AP_TRANS_LISTENER: \
+ case CONN_TYPE_AP_NATD_LISTENER: \
+ case CONN_TYPE_AP_DNS_LISTENER
+
/**************************************************************/
/**
@@ -108,13 +130,7 @@ conn_state_to_string(int type, int state)
{
static char buf[96];
switch (type) {
- case CONN_TYPE_OR_LISTENER:
- case CONN_TYPE_AP_LISTENER:
- case CONN_TYPE_AP_TRANS_LISTENER:
- case CONN_TYPE_AP_NATD_LISTENER:
- case CONN_TYPE_AP_DNS_LISTENER:
- case CONN_TYPE_DIR_LISTENER:
- case CONN_TYPE_CONTROL_LISTENER:
+ CASE_ANY_LISTENER_TYPE:
if (state == LISTENER_STATE_READY)
return "ready";
break;
@@ -124,10 +140,13 @@ conn_state_to_string(int type, int state)
case OR_CONN_STATE_PROXY_HANDSHAKING: return "handshaking (proxy)";
case OR_CONN_STATE_TLS_HANDSHAKING: return "handshaking (TLS)";
case OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING:
- return "renegotiating (TLS)";
+ return "renegotiating (TLS, v2 handshake)";
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
- return "waiting for renegotiation (TLS)";
- case OR_CONN_STATE_OR_HANDSHAKING: return "handshaking (Tor)";
+ return "waiting for renegotiation or V3 handshake";
+ case OR_CONN_STATE_OR_HANDSHAKING_V2:
+ return "handshaking (Tor, v2 handshake)";
+ case OR_CONN_STATE_OR_HANDSHAKING_V3:
+ return "handshaking (Tor, v3 handshake)";
case OR_CONN_STATE_OPEN: return "open";
}
break;
@@ -183,6 +202,26 @@ conn_state_to_string(int type, int state)
return buf;
}
+#ifdef USE_BUFFEREVENTS
+/** Return true iff the connection's type is one that can use a
+ bufferevent-based implementation. */
+int
+connection_type_uses_bufferevent(connection_t *conn)
+{
+ switch (conn->type) {
+ case CONN_TYPE_AP:
+ case CONN_TYPE_EXIT:
+ case CONN_TYPE_DIR:
+ case CONN_TYPE_CONTROL:
+ case CONN_TYPE_OR:
+ case CONN_TYPE_CPUWORKER:
+ return 1;
+ default:
+ return 0;
+ }
+}
+#endif
+
/** Allocate and return a new dir_connection_t, initialized as by
* connection_init(). */
dir_connection_t *
@@ -211,16 +250,26 @@ or_connection_new(int socket_family)
return or_conn;
}
+/** Allocate and return a new entry_connection_t, initialized as by
+ * connection_init(). */
+entry_connection_t *
+entry_connection_new(int type, int socket_family)
+{
+ entry_connection_t *entry_conn = tor_malloc_zero(sizeof(entry_connection_t));
+ tor_assert(type == CONN_TYPE_AP);
+ connection_init(time(NULL), ENTRY_TO_CONN(entry_conn), type, socket_family);
+ entry_conn->socks_request = socks_request_new();
+ return entry_conn;
+}
+
/** Allocate and return a new edge_connection_t, initialized as by
* connection_init(). */
edge_connection_t *
edge_connection_new(int type, int socket_family)
{
edge_connection_t *edge_conn = tor_malloc_zero(sizeof(edge_connection_t));
- tor_assert(type == CONN_TYPE_EXIT || type == CONN_TYPE_AP);
+ tor_assert(type == CONN_TYPE_EXIT);
connection_init(time(NULL), TO_CONN(edge_conn), type, socket_family);
- if (type == CONN_TYPE_AP)
- edge_conn->socks_request = tor_malloc_zero(sizeof(socks_request_t));
return edge_conn;
}
@@ -237,6 +286,17 @@ control_connection_new(int socket_family)
return control_conn;
}
+/** Allocate and return a new listener_connection_t, initialized as by
+ * connection_init(). */
+listener_connection_t *
+listener_connection_new(int type, int socket_family)
+{
+ listener_connection_t *listener_conn =
+ tor_malloc_zero(sizeof(listener_connection_t));
+ connection_init(time(NULL), TO_CONN(listener_conn), type, socket_family);
+ return listener_conn;
+}
+
/** Allocate, initialize, and return a new connection_t subtype of <b>type</b>
* to make or receive connections of address family <b>socket_family</b>. The
* type should be one of the CONN_TYPE_* constants. */
@@ -248,15 +308,20 @@ connection_new(int type, int socket_family)
return TO_CONN(or_connection_new(socket_family));
case CONN_TYPE_EXIT:
- case CONN_TYPE_AP:
return TO_CONN(edge_connection_new(type, socket_family));
+ case CONN_TYPE_AP:
+ return ENTRY_TO_CONN(entry_connection_new(type, socket_family));
+
case CONN_TYPE_DIR:
return TO_CONN(dir_connection_new(socket_family));
case CONN_TYPE_CONTROL:
return TO_CONN(control_connection_new(socket_family));
+ CASE_ANY_LISTENER_TYPE:
+ return TO_CONN(listener_connection_new(type, socket_family));
+
default: {
connection_t *conn = tor_malloc_zero(sizeof(connection_t));
connection_init(time(NULL), conn, type, socket_family);
@@ -288,15 +353,20 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family)
conn->magic = OR_CONNECTION_MAGIC;
break;
case CONN_TYPE_EXIT:
- case CONN_TYPE_AP:
conn->magic = EDGE_CONNECTION_MAGIC;
break;
+ case CONN_TYPE_AP:
+ conn->magic = ENTRY_CONNECTION_MAGIC;
+ break;
case CONN_TYPE_DIR:
conn->magic = DIR_CONNECTION_MAGIC;
break;
case CONN_TYPE_CONTROL:
conn->magic = CONTROL_CONNECTION_MAGIC;
break;
+ CASE_ANY_LISTENER_TYPE:
+ conn->magic = LISTENER_CONNECTION_MAGIC;
+ break;
default:
conn->magic = BASE_CONNECTION_MAGIC;
break;
@@ -308,10 +378,13 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family)
conn->type = type;
conn->socket_family = socket_family;
- if (!connection_is_listener(conn)) { /* listeners never use their buf */
+#ifndef USE_BUFFEREVENTS
+ if (!connection_is_listener(conn)) {
+ /* listeners never use their buf */
conn->inbuf = buf_new();
conn->outbuf = buf_new();
}
+#endif
conn->timestamp_created = now;
conn->timestamp_lastread = now;
@@ -350,6 +423,10 @@ _connection_free(connection_t *conn)
memlen = sizeof(or_connection_t);
break;
case CONN_TYPE_AP:
+ tor_assert(conn->magic == ENTRY_CONNECTION_MAGIC);
+ mem = TO_ENTRY_CONN(conn);
+ memlen = sizeof(entry_connection_t);
+ break;
case CONN_TYPE_EXIT:
tor_assert(conn->magic == EDGE_CONNECTION_MAGIC);
mem = TO_EDGE_CONN(conn);
@@ -365,6 +442,11 @@ _connection_free(connection_t *conn)
mem = TO_CONTROL_CONN(conn);
memlen = sizeof(control_connection_t);
break;
+ CASE_ANY_LISTENER_TYPE:
+ tor_assert(conn->magic == LISTENER_CONNECTION_MAGIC);
+ mem = TO_LISTENER_CONN(conn);
+ memlen = sizeof(listener_connection_t);
+ break;
default:
tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
mem = conn;
@@ -377,7 +459,8 @@ _connection_free(connection_t *conn)
"bytes on inbuf, %d on outbuf.",
conn_type_to_string(conn->type),
conn_state_to_string(conn->type, conn->state),
- (int)buf_datalen(conn->inbuf), (int)buf_datalen(conn->outbuf));
+ (int)connection_get_inbuf_len(conn),
+ (int)connection_get_outbuf_len(conn));
}
if (!connection_is_listener(conn)) {
@@ -407,15 +490,21 @@ _connection_free(connection_t *conn)
smartlist_free(or_conn->active_circuit_pqueue);
tor_free(or_conn->nickname);
}
- if (CONN_IS_EDGE(conn)) {
- edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
- tor_free(edge_conn->chosen_exit_name);
- if (edge_conn->socks_request) {
- memset(edge_conn->socks_request, 0xcc, sizeof(socks_request_t));
- tor_free(edge_conn->socks_request);
+ if (conn->type == CONN_TYPE_AP) {
+ entry_connection_t *entry_conn = TO_ENTRY_CONN(conn);
+ tor_free(entry_conn->chosen_exit_name);
+ tor_free(entry_conn->original_dest_address);
+ if (entry_conn->socks_request)
+ socks_request_free(entry_conn->socks_request);
+ if (entry_conn->pending_optimistic_data) {
+ generic_buffer_free(entry_conn->pending_optimistic_data);
+ }
+ if (entry_conn->sending_optimistic_data) {
+ generic_buffer_free(entry_conn->sending_optimistic_data);
}
-
- rend_data_free(edge_conn->rend_data);
+ }
+ if (CONN_IS_EDGE(conn)) {
+ rend_data_free(TO_EDGE_CONN(conn)->rend_data);
}
if (conn->type == CONN_TYPE_CONTROL) {
control_connection_t *control_conn = TO_CONTROL_CONN(conn);
@@ -424,6 +513,15 @@ _connection_free(connection_t *conn)
tor_free(conn->read_event); /* Probably already freed by connection_free. */
tor_free(conn->write_event); /* Probably already freed by connection_free. */
+ IF_HAS_BUFFEREVENT(conn, {
+ /* This was a workaround to handle bugs in some old versions of libevent
+ * where callbacks can occur after calling bufferevent_free(). Setting
+ * the callbacks to NULL prevented this. It shouldn't be necessary any
+ * more, but let's not tempt fate for now. */
+ bufferevent_setcb(conn->bufev, NULL, NULL, NULL, NULL);
+ bufferevent_free(conn->bufev);
+ conn->bufev = NULL;
+ });
if (conn->type == CONN_TYPE_DIR) {
dir_connection_t *dir_conn = TO_DIR_CONN(conn);
@@ -450,6 +548,12 @@ _connection_free(connection_t *conn)
log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest");
connection_or_remove_from_identity_map(TO_OR_CONN(conn));
}
+#ifdef USE_BUFFEREVENTS
+ if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) {
+ ev_token_bucket_cfg_free(TO_OR_CONN(conn)->bucket_cfg);
+ TO_OR_CONN(conn)->bucket_cfg = NULL;
+ }
+#endif
memset(mem, 0xCC, memlen); /* poison memory */
tor_free(mem);
@@ -485,39 +589,9 @@ connection_free(connection_t *conn)
_connection_free(conn);
}
-/** Call _connection_free() on every connection in our array, and release all
- * storage held by connection.c. This is used by cpuworkers and dnsworkers
- * when they fork, so they don't keep resources held open (especially
- * sockets).
- *
- * Don't do the checks in connection_free(), because they will
- * fail.
- */
-void
-connection_free_all(void)
-{
- smartlist_t *conns = get_connection_array();
-
- /* We don't want to log any messages to controllers. */
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- if (conn->type == CONN_TYPE_CONTROL)
- TO_CONTROL_CONN(conn)->event_mask = 0);
-
- control_update_global_event_mask();
-
- /* Unlink everything from the identity map. */
- connection_or_clear_identity_map();
-
- SMARTLIST_FOREACH(conns, connection_t *, conn, _connection_free(conn));
-
- if (outgoing_addrs) {
- SMARTLIST_FOREACH(outgoing_addrs, void*, addr, tor_free(addr));
- smartlist_free(outgoing_addrs);
- outgoing_addrs = NULL;
- }
-}
-
-/** Do any cleanup needed:
+/**
+ * Called when we're about to finally unlink and free a connection:
+ * perform necessary accounting and cleanup
* - Directory conns that failed to fetch a rendezvous descriptor
* need to inform pending rendezvous streams.
* - OR conns need to call rep_hist_note_*() to record status.
@@ -530,114 +604,20 @@ connection_free_all(void)
void
connection_about_to_close_connection(connection_t *conn)
{
- circuit_t *circ;
- dir_connection_t *dir_conn;
- or_connection_t *or_conn;
- edge_connection_t *edge_conn;
- time_t now = time(NULL);
-
tor_assert(conn->marked_for_close);
- if (CONN_IS_EDGE(conn)) {
- edge_conn = TO_EDGE_CONN(conn);
- if (!edge_conn->edge_has_sent_end) {
- log_warn(LD_BUG, "(Harmless.) Edge connection (marked at %s:%d) "
- "hasn't sent end yet?",
- conn->marked_for_close_file, conn->marked_for_close);
- tor_fragile_assert();
- }
- }
-
switch (conn->type) {
case CONN_TYPE_DIR:
- dir_conn = TO_DIR_CONN(conn);
- if (conn->state < DIR_CONN_STATE_CLIENT_FINISHED) {
- /* It's a directory connection and connecting or fetching
- * failed: forget about this router, and maybe try again. */
- connection_dir_request_failed(dir_conn);
- }
- /* If we were trying to fetch a v2 rend desc and did not succeed,
- * retry as needed. (If a fetch is successful, the connection state
- * is changed to DIR_PURPOSE_HAS_FETCHED_RENDDESC to mark that
- * refetching is unnecessary.) */
- if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 &&
- dir_conn->rend_data &&
- strlen(dir_conn->rend_data->onion_address) ==
- REND_SERVICE_ID_LEN_BASE32)
- rend_client_refetch_v2_renddesc(dir_conn->rend_data);
+ connection_dir_about_to_close(TO_DIR_CONN(conn));
break;
case CONN_TYPE_OR:
- or_conn = TO_OR_CONN(conn);
- /* Remember why we're closing this connection. */
- if (conn->state != OR_CONN_STATE_OPEN) {
- /* Inform any pending (not attached) circs that they should
- * give up. */
- circuit_n_conn_done(TO_OR_CONN(conn), 0);
- /* now mark things down as needed */
- if (connection_or_nonopen_was_started_here(or_conn)) {
- or_options_t *options = get_options();
- rep_hist_note_connect_failed(or_conn->identity_digest, now);
- entry_guard_register_connect_status(or_conn->identity_digest,0,
- !options->HTTPSProxy, now);
- if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) {
- int reason = tls_error_to_orconn_end_reason(or_conn->tls_error);
- control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED,
- reason);
- if (!authdir_mode_tests_reachability(options))
- control_event_bootstrap_problem(
- orconn_end_reason_to_control_string(reason), reason);
- }
- }
- } else if (conn->hold_open_until_flushed) {
- /* We only set hold_open_until_flushed when we're intentionally
- * closing a connection. */
- rep_hist_note_disconnect(or_conn->identity_digest, now);
- control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
- tls_error_to_orconn_end_reason(or_conn->tls_error));
- } else if (!tor_digest_is_zero(or_conn->identity_digest)) {
- rep_hist_note_connection_died(or_conn->identity_digest, now);
- control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
- tls_error_to_orconn_end_reason(or_conn->tls_error));
- }
- /* Now close all the attached circuits on it. */
- circuit_unlink_all_from_or_conn(TO_OR_CONN(conn),
- END_CIRC_REASON_OR_CONN_CLOSED);
+ connection_or_about_to_close(TO_OR_CONN(conn));
break;
case CONN_TYPE_AP:
- edge_conn = TO_EDGE_CONN(conn);
- if (edge_conn->socks_request->has_finished == 0) {
- /* since conn gets removed right after this function finishes,
- * there's no point trying to send back a reply at this point. */
- log_warn(LD_BUG,"Closing stream (marked at %s:%d) without sending"
- " back a socks reply.",
- conn->marked_for_close_file, conn->marked_for_close);
- }
- if (!edge_conn->end_reason) {
- log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having"
- " set end_reason.",
- conn->marked_for_close_file, conn->marked_for_close);
- }
- if (edge_conn->dns_server_request) {
- log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having"
- " replied to DNS request.",
- conn->marked_for_close_file, conn->marked_for_close);
- dnsserv_reject_request(edge_conn);
- }
- control_event_stream_bandwidth(edge_conn);
- control_event_stream_status(edge_conn, STREAM_EVENT_CLOSED,
- edge_conn->end_reason);
- circ = circuit_get_by_edge_conn(edge_conn);
- if (circ)
- circuit_detach_stream(circ, edge_conn);
+ connection_ap_about_to_close(TO_ENTRY_CONN(conn));
break;
case CONN_TYPE_EXIT:
- edge_conn = TO_EDGE_CONN(conn);
- circ = circuit_get_by_edge_conn(edge_conn);
- if (circ)
- circuit_detach_stream(circ, edge_conn);
- if (conn->state == EXIT_CONN_STATE_RESOLVING) {
- connection_dns_remove(edge_conn);
- }
+ connection_exit_about_to_close(TO_EDGE_CONN(conn));
break;
}
}
@@ -674,10 +654,9 @@ connection_close_immediate(connection_t *conn)
conn->s = -1;
if (conn->linked)
conn->linked_conn_is_closed = 1;
- if (!connection_is_listener(conn)) {
+ if (conn->outbuf)
buf_clear(conn->outbuf);
- conn->outbuf_flushlen = 0;
- }
+ conn->outbuf_flushlen = 0;
}
/** Mark <b>conn</b> to be closed next time we loop through
@@ -747,48 +726,6 @@ connection_expire_held_open(void)
});
}
-/** Create an AF_INET listenaddr struct.
- * <b>listenaddress</b> provides the host and optionally the port information
- * for the new structure. If no port is provided in <b>listenaddress</b> then
- * <b>listenport</b> is used.
- *
- * If not NULL <b>readable_address</b> will contain a copy of the host part of
- * <b>listenaddress</b>.
- *
- * The listenaddr struct has to be freed by the caller.
- */
-static struct sockaddr_in *
-create_inet_sockaddr(const char *listenaddress, int listenport,
- char **readable_address, socklen_t *socklen_out) {
- struct sockaddr_in *listenaddr = NULL;
- uint32_t addr;
- uint16_t usePort = 0;
-
- if (parse_addr_port(LOG_WARN,
- listenaddress, readable_address, &addr, &usePort)<0) {
- log_warn(LD_CONFIG,
- "Error parsing/resolving ListenAddress %s", listenaddress);
- goto err;
- }
- if (usePort==0) {
- if (listenport != CFG_AUTO_PORT)
- usePort = listenport;
- }
-
- listenaddr = tor_malloc_zero(sizeof(struct sockaddr_in));
- listenaddr->sin_addr.s_addr = htonl(addr);
- listenaddr->sin_family = AF_INET;
- listenaddr->sin_port = htons((uint16_t) usePort);
-
- *socklen_out = sizeof(struct sockaddr_in);
-
- return listenaddr;
-
- err:
- tor_free(listenaddr);
- return NULL;
-}
-
#ifdef HAVE_SYS_UN_H
/** Create an AF_UNIX listenaddr struct.
* <b>listenaddress</b> provides the path to the Unix socket.
@@ -862,7 +799,7 @@ warn_too_many_conns(void)
/** Check whether we should be willing to open an AF_UNIX socket in
* <b>path</b>. Return 0 if we should go ahead and -1 if we shouldn't. */
static int
-check_location_for_unix_socket(or_options_t *options, const char *path)
+check_location_for_unix_socket(const or_options_t *options, const char *path)
{
int r = -1;
char *p = tor_strdup(path);
@@ -923,12 +860,20 @@ make_socket_reuseable(tor_socket_t sock)
static connection_t *
connection_create_listener(const struct sockaddr *listensockaddr,
socklen_t socklen,
- int type, char* address)
+ int type, const char *address,
+ const port_cfg_t *port_cfg)
{
+ listener_connection_t *lis_conn;
connection_t *conn;
tor_socket_t s; /* the socket we're going to make */
+ or_options_t const *options = get_options();
+#if defined(HAVE_PWD_H) && defined(HAVE_SYS_UN_H)
+ struct passwd *pw = NULL;
+#endif
uint16_t usePort = 0, gotPort = 0;
int start_reading = 0;
+ static int global_next_session_group = SESSION_GROUP_FIRST_AUTO;
+ tor_addr_t addr;
if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
warn_too_many_conns();
@@ -936,7 +881,6 @@ connection_create_listener(const struct sockaddr *listensockaddr,
}
if (listensockaddr->sa_family == AF_INET) {
- tor_addr_t addr;
int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER);
if (is_tcp)
start_reading = 1;
@@ -950,7 +894,8 @@ connection_create_listener(const struct sockaddr *listensockaddr,
is_tcp ? SOCK_STREAM : SOCK_DGRAM,
is_tcp ? IPPROTO_TCP: IPPROTO_UDP);
if (!SOCKET_OK(s)) {
- log_warn(LD_NET,"Socket creation failed.");
+ log_warn(LD_NET,"Socket creation failed: %s",
+ tor_socket_strerror(tor_socket_errno(-1)));
goto err;
}
@@ -998,12 +943,14 @@ connection_create_listener(const struct sockaddr *listensockaddr,
* and listeners at the same time */
tor_assert(type == CONN_TYPE_CONTROL_LISTENER);
- if (check_location_for_unix_socket(get_options(), address) < 0)
+ if (check_location_for_unix_socket(options, address) < 0)
goto err;
log_notice(LD_NET, "Opening %s on %s",
conn_type_to_string(type), address);
+ tor_addr_make_unspec(&addr);
+
if (unlink(address) < 0 && errno != ENOENT) {
log_warn(LD_NET, "Could not unlink %s: %s", address,
strerror(errno));
@@ -1020,7 +967,20 @@ connection_create_listener(const struct sockaddr *listensockaddr,
tor_socket_strerror(tor_socket_errno(s)));
goto err;
}
- if (get_options()->ControlSocketsGroupWritable) {
+#ifdef HAVE_PWD_H
+ if (options->User) {
+ pw = getpwnam(options->User);
+ if (pw == NULL) {
+ log_warn(LD_NET,"Unable to chown() %s socket: user %s not found.",
+ address, options->User);
+ } else if (chown(address, pw->pw_uid, pw->pw_gid) < 0) {
+ log_warn(LD_NET,"Unable to chown() %s socket: %s.",
+ address, strerror(errno));
+ goto err;
+ }
+ }
+#endif
+ if (options->ControlSocketsGroupWritable) {
/* We need to use chmod; fchmod doesn't work on sockets on all
* platforms. */
if (chmod(address, 0660) < 0) {
@@ -1045,11 +1005,23 @@ connection_create_listener(const struct sockaddr *listensockaddr,
set_socket_nonblocking(s);
- conn = connection_new(type, listensockaddr->sa_family);
+ lis_conn = listener_connection_new(type, listensockaddr->sa_family);
+ conn = TO_CONN(lis_conn);
conn->socket_family = listensockaddr->sa_family;
conn->s = s;
conn->address = tor_strdup(address);
conn->port = gotPort;
+ tor_addr_copy(&conn->addr, &addr);
+
+ if (port_cfg->isolation_flags) {
+ lis_conn->isolation_flags = port_cfg->isolation_flags;
+ if (port_cfg->session_group >= 0) {
+ lis_conn->session_group = port_cfg->session_group;
+ } else {
+ /* XXXX023 This can wrap after ~INT_MAX ports are opened. */
+ lis_conn->session_group = global_next_session_group--;
+ }
+ }
if (connection_add(conn) < 0) { /* no space, forget it */
log_warn(LD_NET,"connection_add for listener failed. Giving up.");
@@ -1146,7 +1118,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
struct sockaddr *remote = (struct sockaddr*)addrbuf;
/* length of the remote address. Must be whatever accept() needs. */
socklen_t remotelen = (socklen_t)sizeof(addrbuf);
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
tor_assert((size_t)remotelen >= sizeof(struct sockaddr_in));
memset(addrbuf, 0, sizeof(addrbuf));
@@ -1260,7 +1232,7 @@ connection_handle_listener_read(connection_t *conn, int new_type)
return 0; /* no need to tear down the parent */
}
- if (connection_init_accepted_conn(newconn, conn->type) < 0) {
+ if (connection_init_accepted_conn(newconn, TO_LISTENER_CONN(conn)) < 0) {
if (! newconn->marked_for_close)
connection_mark_for_close(newconn);
return 0;
@@ -1274,7 +1246,8 @@ connection_handle_listener_read(connection_t *conn, int new_type)
* and place it in circuit_wait.
*/
static int
-connection_init_accepted_conn(connection_t *conn, uint8_t listener_type)
+connection_init_accepted_conn(connection_t *conn,
+ const listener_connection_t *listener)
{
connection_start_reading(conn);
@@ -1283,16 +1256,20 @@ connection_init_accepted_conn(connection_t *conn, uint8_t listener_type)
control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0);
return connection_tls_start_handshake(TO_OR_CONN(conn), 1);
case CONN_TYPE_AP:
- switch (listener_type) {
+ TO_ENTRY_CONN(conn)->isolation_flags = listener->isolation_flags;
+ TO_ENTRY_CONN(conn)->session_group = listener->session_group;
+ TO_ENTRY_CONN(conn)->nym_epoch = get_signewnym_epoch();
+ TO_ENTRY_CONN(conn)->socks_request->listener_type = listener->_base.type;
+ switch (TO_CONN(listener)->type) {
case CONN_TYPE_AP_LISTENER:
conn->state = AP_CONN_STATE_SOCKS_WAIT;
break;
case CONN_TYPE_AP_TRANS_LISTENER:
- TO_EDGE_CONN(conn)->is_transparent_ap = 1;
+ TO_ENTRY_CONN(conn)->is_transparent_ap = 1;
conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- return connection_ap_process_transparent(TO_EDGE_CONN(conn));
+ return connection_ap_process_transparent(TO_ENTRY_CONN(conn));
case CONN_TYPE_AP_NATD_LISTENER:
- TO_EDGE_CONN(conn)->is_transparent_ap = 1;
+ TO_ENTRY_CONN(conn)->is_transparent_ap = 1;
conn->state = AP_CONN_STATE_NATD_WAIT;
break;
}
@@ -1325,8 +1302,8 @@ connection_connect(connection_t *conn, const char *address,
int inprogress = 0;
char addrbuf[256];
struct sockaddr *dest_addr;
- socklen_t dest_addr_len;
- or_options_t *options = get_options();
+ int dest_addr_len;
+ const or_options_t *options = get_options();
int protocol_family;
if (get_n_open_sockets() >= get_options()->_ConnLimit-1) {
@@ -1383,7 +1360,7 @@ connection_connect(connection_t *conn, const char *address,
make_socket_reuseable(s);
- if (connect(s, dest_addr, dest_addr_len) < 0) {
+ if (connect(s, dest_addr, (socklen_t)dest_addr_len) < 0) {
int e = tor_socket_errno(s);
if (!ERRNO_IS_CONN_EINPROGRESS(e)) {
/* yuck. kill it. */
@@ -1408,7 +1385,7 @@ connection_connect(connection_t *conn, const char *address,
escaped_safe_str_client(address),
port, inprogress?"in progress":"established", s);
conn->s = s;
- if (connection_add(conn) < 0) /* no space, forget it */
+ if (connection_add_connecting(conn) < 0) /* no space, forget it */
return -1;
return inprogress ? 0 : 1;
}
@@ -1421,6 +1398,7 @@ connection_proxy_state_to_string(int state)
static const char *unknown = "???";
static const char *states[] = {
"PROXY_NONE",
+ "PROXY_INFANT",
"PROXY_HTTPS_WANT_CONNECT_OK",
"PROXY_SOCKS4_WANT_CONNECT_OK",
"PROXY_SOCKS5_WANT_AUTH_METHOD_NONE",
@@ -1449,7 +1427,7 @@ connection_proxy_state_to_string(int state)
int
connection_proxy_connect(connection_t *conn, int type)
{
- or_options_t *options;
+ const or_options_t *options;
tor_assert(conn);
@@ -1641,6 +1619,19 @@ connection_send_socks5_connect(connection_t *conn)
conn->proxy_state = PROXY_SOCKS5_WANT_CONNECT_OK;
}
+/** DOCDOC */
+static int
+connection_fetch_from_buf_socks_client(connection_t *conn,
+ int state, char **reason)
+{
+ IF_HAS_BUFFEREVENT(conn, {
+ struct evbuffer *input = bufferevent_get_input(conn->bufev);
+ return fetch_from_evbuffer_socks_client(input, state, reason);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return fetch_from_buf_socks_client(conn->inbuf, state, reason);
+ }
+}
+
/** Call this from connection_*_process_inbuf() to advance the proxy
* handshake.
*
@@ -1668,17 +1659,17 @@ connection_read_proxy_handshake(connection_t *conn)
break;
case PROXY_SOCKS4_WANT_CONNECT_OK:
- ret = fetch_from_buf_socks_client(conn->inbuf,
- conn->proxy_state,
- &reason);
+ ret = connection_fetch_from_buf_socks_client(conn,
+ conn->proxy_state,
+ &reason);
if (ret == 1)
conn->proxy_state = PROXY_CONNECTED;
break;
case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
- ret = fetch_from_buf_socks_client(conn->inbuf,
- conn->proxy_state,
- &reason);
+ ret = connection_fetch_from_buf_socks_client(conn,
+ conn->proxy_state,
+ &reason);
/* no auth needed, do connect */
if (ret == 1) {
connection_send_socks5_connect(conn);
@@ -1687,9 +1678,9 @@ connection_read_proxy_handshake(connection_t *conn)
break;
case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
- ret = fetch_from_buf_socks_client(conn->inbuf,
- conn->proxy_state,
- &reason);
+ ret = connection_fetch_from_buf_socks_client(conn,
+ conn->proxy_state,
+ &reason);
/* send auth if needed, otherwise do connect */
if (ret == 1) {
@@ -1724,9 +1715,9 @@ connection_read_proxy_handshake(connection_t *conn)
break;
case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
- ret = fetch_from_buf_socks_client(conn->inbuf,
- conn->proxy_state,
- &reason);
+ ret = connection_fetch_from_buf_socks_client(conn,
+ conn->proxy_state,
+ &reason);
/* send the connect request */
if (ret == 1) {
connection_send_socks5_connect(conn);
@@ -1735,9 +1726,9 @@ connection_read_proxy_handshake(connection_t *conn)
break;
case PROXY_SOCKS5_WANT_CONNECT_OK:
- ret = fetch_from_buf_socks_client(conn->inbuf,
- conn->proxy_state,
- &reason);
+ ret = connection_fetch_from_buf_socks_client(conn,
+ conn->proxy_state,
+ &reason);
if (ret == 1)
conn->proxy_state = PROXY_CONNECTED;
break;
@@ -1770,6 +1761,113 @@ connection_read_proxy_handshake(connection_t *conn)
return ret;
}
+/** Given a list of listener connections in <b>old_conns</b>, and list of
+ * port_cfg_t entries in <b>ports</b>, open a new listener for every port in
+ * <b>ports</b> that does not already have a listener in <b>old_conns</b>.
+ *
+ * Remove from <b>old_conns</b> every connection that has a corresponding
+ * entry in <b>ports</b>. Add to <b>new_conns</b> new every connection we
+ * launch.
+ *
+ * Return 0 on success, -1 on failure.
+ **/
+static int
+retry_listener_ports(smartlist_t *old_conns,
+ const smartlist_t *ports,
+ smartlist_t *new_conns)
+{
+ smartlist_t *launch = smartlist_create();
+ int r = 0;
+
+ smartlist_add_all(launch, ports);
+
+ /* Iterate through old_conns, comparing it to launch: remove from both lists
+ * each pair of elements that corresponds to the same port. */
+ SMARTLIST_FOREACH_BEGIN(old_conns, connection_t *, conn) {
+ const port_cfg_t *found_port = NULL;
+
+ /* Okay, so this is a listener. Is it configured? */
+ SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, wanted) {
+ if (conn->type != wanted->type)
+ continue;
+ if ((conn->socket_family != AF_UNIX && wanted->is_unix_addr) ||
+ (conn->socket_family == AF_UNIX && ! wanted->is_unix_addr))
+ continue;
+
+ if (wanted->is_unix_addr) {
+ if (conn->socket_family == AF_UNIX &&
+ !strcmp(wanted->unix_addr, conn->address)) {
+ found_port = wanted;
+ break;
+ }
+ } else {
+ int port_matches;
+ if (wanted->port == CFG_AUTO_PORT) {
+ port_matches = 1;
+ } else {
+ port_matches = (wanted->port == conn->port);
+ }
+ if (port_matches && tor_addr_eq(&wanted->addr, &conn->addr)) {
+ found_port = wanted;
+ break;
+ }
+ }
+ } SMARTLIST_FOREACH_END(wanted);
+
+ if (found_port) {
+ /* This listener is already running; we don't need to launch it. */
+ //log_debug(LD_NET, "Already have %s on %s:%d",
+ // conn_type_to_string(found_port->type), conn->address, conn->port);
+ smartlist_remove(launch, found_port);
+ /* And we can remove the connection from old_conns too. */
+ SMARTLIST_DEL_CURRENT(old_conns, conn);
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ /* Now open all the listeners that are configured but not opened. */
+ SMARTLIST_FOREACH_BEGIN(launch, const port_cfg_t *, port) {
+ struct sockaddr *listensockaddr;
+ socklen_t listensocklen = 0;
+ char *address=NULL;
+ connection_t *conn;
+ int real_port = port->port == CFG_AUTO_PORT ? 0 : port->port;
+ tor_assert(real_port <= UINT16_MAX);
+
+ if (port->is_unix_addr) {
+ listensockaddr = (struct sockaddr *)
+ create_unix_sockaddr(port->unix_addr,
+ &address, &listensocklen);
+ } else {
+ listensockaddr = tor_malloc(sizeof(struct sockaddr_storage));
+ listensocklen = tor_addr_to_sockaddr(&port->addr,
+ real_port,
+ listensockaddr,
+ sizeof(struct sockaddr_storage));
+ address = tor_dup_addr(&port->addr);
+ }
+
+ if (listensockaddr) {
+ conn = connection_create_listener(listensockaddr, listensocklen,
+ port->type, address, port);
+ tor_free(listensockaddr);
+ tor_free(address);
+ } else {
+ conn = NULL;
+ }
+
+ if (!conn) {
+ r = -1;
+ } else {
+ if (new_conns)
+ smartlist_add(new_conns, conn);
+ }
+ } SMARTLIST_FOREACH_END(port);
+
+ smartlist_free(launch);
+
+ return r;
+}
+
/**
* Launch any configured listener connections of type <b>type</b>. (A
* listener is configured if <b>port_option</b> is non-zero. If any
@@ -1777,168 +1875,73 @@ connection_read_proxy_handshake(connection_t *conn)
* connection binding to each one. Otherwise, create a single
* connection binding to the address <b>default_addr</b>.)
*
- * Only launch the listeners of this type that are not already open, and
- * only close listeners that are no longer wanted. Existing listeners
- * that are still configured are not touched.
- *
- * If <b>disable_all_conns</b> is set, then never open new conns, and
- * close the existing ones.
+ * We assume that we're starting with a list of existing listener connection_t
+ * pointers in <b>old_conns</b>: we do not launch listeners that are already
+ * in that list. Instead, we just remove them from the list.
*
- * Add all old conns that should be closed to <b>replaced_conns</b>.
- * Add all new connections to <b>new_conns</b>.
+ * All new connections we launch are added to <b>new_conns</b>.
*/
static int
-retry_listeners(int type, config_line_t *cfg,
+retry_listeners(smartlist_t *old_conns,
+ int type, const config_line_t *cfg,
int port_option, const char *default_addr,
- smartlist_t *replaced_conns,
smartlist_t *new_conns,
- int disable_all_conns,
- int socket_family)
+ int is_sockaddr_un)
{
- smartlist_t *launch = smartlist_create(), *conns;
- int free_launch_elts = 1;
- int r;
- config_line_t *c;
- connection_t *conn;
- config_line_t *line;
-
- tor_assert(socket_family == AF_INET || socket_family == AF_UNIX);
+ smartlist_t *ports = smartlist_create();
+ tor_addr_t dflt_addr;
+ int retval = 0;
- if (cfg && port_option) {
- for (c = cfg; c; c = c->next) {
- smartlist_add(launch, c);
- }
- free_launch_elts = 0;
- } else if (port_option) {
- line = tor_malloc_zero(sizeof(config_line_t));
- line->key = tor_strdup("");
- line->value = tor_strdup(default_addr);
- smartlist_add(launch, line);
+ if (default_addr) {
+ tor_addr_parse(&dflt_addr, default_addr);
+ } else {
+ tor_addr_make_unspec(&dflt_addr);
}
- /*
- SMARTLIST_FOREACH(launch, config_line_t *, l,
- log_fn(LOG_NOTICE, "#%s#%s", l->key, l->value));
- */
-
- conns = get_connection_array();
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
- if (conn->type != type ||
- conn->socket_family != socket_family ||
- conn->marked_for_close)
- continue;
- /* Okay, so this is a listener. Is it configured? */
- line = NULL;
- SMARTLIST_FOREACH(launch, config_line_t *, wanted,
- {
- char *address=NULL;
- uint16_t port;
- switch (socket_family) {
- case AF_INET:
- if (!parse_addr_port(LOG_WARN,
- wanted->value, &address, NULL, &port)) {
- int addr_matches = !strcasecmp(address, conn->address);
- int port_matches;
- tor_free(address);
- if (port) {
- /* The Listener line has a port */
- port_matches = (port == conn->port);
- } else if (port_option == CFG_AUTO_PORT) {
- /* The Listener line has no port, and the Port line is "auto".
- * "auto" matches anything; transitions from any port to
- * "auto" succeed. */
- port_matches = 1;
- } else {
- /* The Listener line has no port, and the Port line is "auto".
- * "auto" matches anything; transitions from any port to
- * "auto" succeed. */
- port_matches = (port_option == conn->port);
- }
- if (port_matches && addr_matches) {
- line = wanted;
- break;
- }
- }
- break;
- case AF_UNIX:
- if (!strcasecmp(wanted->value, conn->address)) {
- line = wanted;
- break;
- }
- break;
- default:
- tor_assert(0);
- }
- });
- if (!line || disable_all_conns) {
- /* This one isn't configured. Close it. */
- log_notice(LD_NET, "Closing no-longer-configured %s on %s:%d",
- conn_type_to_string(type), conn->address, conn->port);
- if (replaced_conns) {
- smartlist_add(replaced_conns, conn);
- } else {
- connection_close_immediate(conn);
- connection_mark_for_close(conn);
- }
+ if (port_option) {
+ if (!cfg) {
+ port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t));
+ tor_addr_copy(&port->addr, &dflt_addr);
+ port->port = port_option;
+ port->type = type;
+ smartlist_add(ports, port);
} else {
- /* It's configured; we don't need to launch it. */
-// log_debug(LD_NET, "Already have %s on %s:%d",
-// conn_type_to_string(type), conn->address, conn->port);
- smartlist_remove(launch, line);
- if (free_launch_elts)
- config_free_lines(line);
- }
- });
-
- /* Now open all the listeners that are configured but not opened. */
- r = 0;
- if (!disable_all_conns) {
- SMARTLIST_FOREACH_BEGIN(launch, config_line_t *, cfg_line) {
- char *address = NULL;
- struct sockaddr *listensockaddr;
- socklen_t listensocklen = 0;
-
- switch (socket_family) {
- case AF_INET:
- listensockaddr = (struct sockaddr *)
- create_inet_sockaddr(cfg_line->value,
- port_option,
- &address, &listensocklen);
- break;
- case AF_UNIX:
- listensockaddr = (struct sockaddr *)
- create_unix_sockaddr(cfg_line->value,
- &address, &listensocklen);
- break;
- default:
- tor_assert(0);
- }
-
- if (listensockaddr) {
- conn = connection_create_listener(listensockaddr, listensocklen,
- type, address);
- tor_free(listensockaddr);
- tor_free(address);
- } else
- conn = NULL;
-
- if (!conn) {
- r = -1;
+ const config_line_t *c;
+ for (c = cfg; c; c = c->next) {
+ port_cfg_t *port;
+ tor_addr_t addr;
+ uint16_t portval = 0;
+ if (is_sockaddr_un) {
+ size_t len = strlen(c->value);
+ port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1);
+ port->is_unix_addr = 1;
+ memcpy(port->unix_addr, c->value, len+1);
} else {
- if (new_conns)
- smartlist_add(new_conns, conn);
+ if (tor_addr_port_lookup(c->value, &addr, &portval) < 0) {
+ log_warn(LD_CONFIG, "Can't parse/resolve %s %s",
+ c->key, c->value);
+ retval = -1;
+ continue;
+ }
+ port = tor_malloc_zero(sizeof(port_cfg_t));
+ tor_addr_copy(&port->addr, &addr);
}
- } SMARTLIST_FOREACH_END(cfg_line);
+ port->type = type;
+ port->port = portval ? portval : port_option;
+ smartlist_add(ports, port);
+ }
+ }
}
- if (free_launch_elts) {
- SMARTLIST_FOREACH(launch, config_line_t *, cfg_line,
- config_free_lines(cfg_line));
- }
- smartlist_free(launch);
+ if (retval == -1)
+ goto cleanup;
- return r;
+ retval = retry_listener_ports(old_conns, ports, new_conns);
+
+ cleanup:
+ SMARTLIST_FOREACH(ports, port_cfg_t *, p, tor_free(p));
+ smartlist_free(ports);
+ return retval;
}
/** Launch listeners for each port you should have open. Only launch
@@ -1952,53 +1955,61 @@ int
retry_all_listeners(smartlist_t *replaced_conns,
smartlist_t *new_conns)
{
- or_options_t *options = get_options();
+ smartlist_t *listeners = smartlist_create();
+ const or_options_t *options = get_options();
int retval = 0;
const uint16_t old_or_port = router_get_advertised_or_port(options);
const uint16_t old_dir_port = router_get_advertised_dir_port(options, 0);
- if (retry_listeners(CONN_TYPE_OR_LISTENER, options->ORListenAddress,
- options->ORPort, "0.0.0.0",
- replaced_conns, new_conns, options->ClientOnly,
- AF_INET)<0)
- retval = -1;
- if (retry_listeners(CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
- options->DirPort, "0.0.0.0",
- replaced_conns, new_conns, options->ClientOnly,
- AF_INET)<0)
- retval = -1;
- if (retry_listeners(CONN_TYPE_AP_LISTENER, options->SocksListenAddress,
- options->SocksPort, "127.0.0.1",
- replaced_conns, new_conns, 0,
- AF_INET)<0)
- retval = -1;
- if (retry_listeners(CONN_TYPE_AP_TRANS_LISTENER, options->TransListenAddress,
- options->TransPort, "127.0.0.1",
- replaced_conns, new_conns, 0,
- AF_INET)<0)
- retval = -1;
- if (retry_listeners(CONN_TYPE_AP_NATD_LISTENER, options->NATDListenAddress,
- options->NATDPort, "127.0.0.1",
- replaced_conns, new_conns, 0,
- AF_INET)<0)
- retval = -1;
- if (retry_listeners(CONN_TYPE_AP_DNS_LISTENER, options->DNSListenAddress,
- options->DNSPort, "127.0.0.1",
- replaced_conns, new_conns, 0,
- AF_INET)<0)
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ if (connection_is_listener(conn) && !conn->marked_for_close)
+ smartlist_add(listeners, conn);
+ } SMARTLIST_FOREACH_END(conn);
+
+ if (! options->ClientOnly) {
+ if (retry_listeners(listeners,
+ CONN_TYPE_OR_LISTENER, options->ORListenAddress,
+ options->ORPort, "0.0.0.0",
+ new_conns, 0) < 0)
+ retval = -1;
+ if (retry_listeners(listeners,
+ CONN_TYPE_DIR_LISTENER, options->DirListenAddress,
+ options->DirPort, "0.0.0.0",
+ new_conns, 0) < 0)
+ retval = -1;
+ }
+
+ if (retry_listener_ports(listeners,
+ get_configured_client_ports(),
+ new_conns) < 0)
retval = -1;
- if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
+ if (retry_listeners(listeners,
+ CONN_TYPE_CONTROL_LISTENER,
options->ControlListenAddress,
options->ControlPort, "127.0.0.1",
- replaced_conns, new_conns, 0,
- AF_INET)<0)
- return -1;
- if (retry_listeners(CONN_TYPE_CONTROL_LISTENER,
+ new_conns, 0) < 0)
+ retval = -1;
+ if (retry_listeners(listeners,
+ CONN_TYPE_CONTROL_LISTENER,
options->ControlSocket,
options->ControlSocket ? 1 : 0, NULL,
- replaced_conns, new_conns, 0,
- AF_UNIX)<0)
- return -1;
+ new_conns, 1) < 0)
+ retval = -1;
+
+ /* Any members that were still in 'listeners' don't correspond to
+ * any configured port. Kill 'em. */
+ SMARTLIST_FOREACH_BEGIN(listeners, connection_t *, conn) {
+ log_notice(LD_NET, "Closing no-longer-configured %s on %s:%d",
+ conn_type_to_string(conn->type), conn->address, conn->port);
+ if (replaced_conns) {
+ smartlist_add(replaced_conns, conn);
+ } else {
+ connection_close_immediate(conn);
+ connection_mark_for_close(conn);
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ smartlist_free(listeners);
if (old_or_port != router_get_advertised_or_port(options) ||
old_dir_port != router_get_advertised_dir_port(options, 0)) {
@@ -2012,20 +2023,29 @@ retry_all_listeners(smartlist_t *replaced_conns,
return retval;
}
-/** Return 1 if we should apply rate limiting to <b>conn</b>,
- * and 0 otherwise. Right now this just checks if it's an internal
- * IP address or an internal connection. */
+/** Return 1 if we should apply rate limiting to <b>conn</b>, and 0
+ * otherwise.
+ * Right now this just checks if it's an internal IP address or an
+ * internal connection. We also check if the connection uses pluggable
+ * transports, since we should then limit it even if it comes from an
+ * internal IP address. */
static int
connection_is_rate_limited(connection_t *conn)
{
- if (conn->linked || /* internal connection */
- tor_addr_family(&conn->addr) == AF_UNSPEC || /* no address */
- tor_addr_is_internal(&conn->addr, 0)) /* internal address */
- return 0;
+ const or_options_t *options = get_options();
+ if (conn->linked)
+ return 0; /* Internal connection */
+ else if (! options->CountPrivateBandwidth &&
+ (tor_addr_family(&conn->addr) == AF_UNSPEC || /* no address */
+ tor_addr_is_internal(&conn->addr, 0)))
+ return 0; /* Internal address */
else
return 1;
}
+#ifdef USE_BUFFEREVENTS
+static struct bufferevent_rate_limit_group *global_rate_limit = NULL;
+#else
extern int global_read_bucket, global_write_bucket;
extern int global_relayed_read_bucket, global_relayed_write_bucket;
@@ -2033,11 +2053,13 @@ extern int global_relayed_read_bucket, global_relayed_write_bucket;
* we are likely to run dry again this second, so be stingy with the
* tokens we just put in. */
static int write_buckets_empty_last_second = 0;
+#endif
/** How many seconds of no active local circuits will make the
* connection revert to the "relayed" bandwidth class? */
#define CLIENT_IDLE_TIME_FOR_PRIORITY 30
+#ifndef USE_BUFFEREVENTS
/** Return 1 if <b>conn</b> should use tokens from the "relayed"
* bandwidth rates, else 0. Currently, only OR conns with bandwidth
* class 1, and directory conns that are serving data out, count.
@@ -2148,6 +2170,20 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
return connection_bucket_round_robin(base, priority,
global_bucket, conn_bucket);
}
+#else
+static ssize_t
+connection_bucket_read_limit(connection_t *conn, time_t now)
+{
+ (void) now;
+ return bufferevent_get_max_to_read(conn->bufev);
+}
+ssize_t
+connection_bucket_write_limit(connection_t *conn, time_t now)
+{
+ (void) now;
+ return bufferevent_get_max_to_write(conn->bufev);
+}
+#endif
/** Return 1 if the global write buckets are low enough that we
* shouldn't send <b>attempt</b> bytes of low-priority directory stuff
@@ -2172,8 +2208,12 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
int
global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
{
+#ifdef USE_BUFFEREVENTS
+ ssize_t smaller_bucket = bufferevent_get_max_to_write(conn->bufev);
+#else
int smaller_bucket = global_write_bucket < global_relayed_write_bucket ?
global_write_bucket : global_relayed_write_bucket;
+#endif
if (authdir_mode(get_options()) && priority>1)
return 0; /* there's always room to answer v2 if we're an auth dir */
@@ -2183,12 +2223,14 @@ global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
if (smaller_bucket < (int)attempt)
return 1; /* not enough space no matter the priority */
+#ifndef USE_BUFFEREVENTS
if (write_buckets_empty_last_second)
return 1; /* we're already hitting our limits, no more please */
+#endif
if (priority == 1) { /* old-style v1 query */
/* Could we handle *two* of these requests within the next two seconds? */
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int64_t can_write = (int64_t)smaller_bucket
+ 2*(options->RelayBandwidthRate ? options->RelayBandwidthRate :
options->BandwidthRate);
@@ -2200,23 +2242,11 @@ global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
return 0;
}
-/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes
- * onto <b>conn</b>. Decrement buckets appropriately. */
+/** DOCDOC */
static void
-connection_buckets_decrement(connection_t *conn, time_t now,
- size_t num_read, size_t num_written)
+record_num_bytes_transferred_impl(connection_t *conn,
+ time_t now, size_t num_read, size_t num_written)
{
- if (num_written >= INT_MAX || num_read >= INT_MAX) {
- log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, "
- "connection type=%s, state=%s",
- (unsigned long)num_read, (unsigned long)num_written,
- conn_type_to_string(conn->type),
- conn_state_to_string(conn->type, conn->state));
- if (num_written >= INT_MAX) num_written = 1;
- if (num_read >= INT_MAX) num_read = 1;
- tor_fragile_assert();
- }
-
/* Count bytes of answering direct and tunneled directory requests */
if (conn->type == CONN_TYPE_DIR && conn->purpose == DIR_PURPOSE_SERVER) {
if (num_read > 0)
@@ -2227,6 +2257,11 @@ connection_buckets_decrement(connection_t *conn, time_t now,
if (!connection_is_rate_limited(conn))
return; /* local IPs are free */
+
+ if (conn->type == CONN_TYPE_OR)
+ rep_hist_note_or_conn_bytes(conn->global_identifier, num_read,
+ num_written, now);
+
if (num_read > 0) {
rep_hist_note_bytes_read(num_read, now);
}
@@ -2235,6 +2270,52 @@ connection_buckets_decrement(connection_t *conn, time_t now,
}
if (conn->type == CONN_TYPE_EXIT)
rep_hist_note_exit_bytes(conn->port, num_written, num_read);
+}
+
+#ifdef USE_BUFFEREVENTS
+/** DOCDOC */
+static void
+record_num_bytes_transferred(connection_t *conn,
+ time_t now, size_t num_read, size_t num_written)
+{
+ /* XXX023 check if this is necessary */
+ if (num_written >= INT_MAX || num_read >= INT_MAX) {
+ log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, "
+ "connection type=%s, state=%s",
+ (unsigned long)num_read, (unsigned long)num_written,
+ conn_type_to_string(conn->type),
+ conn_state_to_string(conn->type, conn->state));
+ if (num_written >= INT_MAX) num_written = 1;
+ if (num_read >= INT_MAX) num_read = 1;
+ tor_fragile_assert();
+ }
+
+ record_num_bytes_transferred_impl(conn,now,num_read,num_written);
+}
+#endif
+
+#ifndef USE_BUFFEREVENTS
+/** We just read <b>num_read</b> and wrote <b>num_written</b> bytes
+ * onto <b>conn</b>. Decrement buckets appropriately. */
+static void
+connection_buckets_decrement(connection_t *conn, time_t now,
+ size_t num_read, size_t num_written)
+{
+ if (num_written >= INT_MAX || num_read >= INT_MAX) {
+ log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, "
+ "connection type=%s, state=%s",
+ (unsigned long)num_read, (unsigned long)num_written,
+ conn_type_to_string(conn->type),
+ conn_state_to_string(conn->type, conn->state));
+ if (num_written >= INT_MAX) num_written = 1;
+ if (num_read >= INT_MAX) num_read = 1;
+ tor_fragile_assert();
+ }
+
+ record_num_bytes_transferred_impl(conn, now, num_read, num_written);
+
+ if (!connection_is_rate_limited(conn))
+ return; /* local IPs are free */
if (connection_counts_as_relayed_traffic(conn, now)) {
global_relayed_read_bucket -= (int)num_read;
@@ -2300,7 +2381,7 @@ connection_consider_empty_write_buckets(connection_t *conn)
void
connection_bucket_init(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
/* start it at max traffic */
global_read_bucket = (int)options->BandwidthBurst;
global_write_bucket = (int)options->BandwidthBurst;
@@ -2313,22 +2394,23 @@ connection_bucket_init(void)
}
}
-/** Refill a single <b>bucket</b> called <b>name</b> with bandwidth rate
- * <b>rate</b> and bandwidth burst <b>burst</b>, assuming that
- * <b>seconds_elapsed</b> seconds have passed since the last call.
- **/
+/** Refill a single <b>bucket</b> called <b>name</b> with bandwidth rate per
+ * second <b>rate</b> and bandwidth burst <b>burst</b>, assuming that
+ * <b>milliseconds_elapsed</b> milliseconds have passed since the last
+ * call. */
static void
connection_bucket_refill_helper(int *bucket, int rate, int burst,
- int seconds_elapsed, const char *name)
+ int milliseconds_elapsed,
+ const char *name)
{
int starting_bucket = *bucket;
- if (starting_bucket < burst && seconds_elapsed) {
- if (((burst - starting_bucket)/seconds_elapsed) < rate) {
+ if (starting_bucket < burst && milliseconds_elapsed > 0) {
+ int64_t incr = (((int64_t)rate) * milliseconds_elapsed) / 1000;
+ if ((burst - starting_bucket) < incr) {
*bucket = burst; /* We would overflow the bucket; just set it to
* the maximum. */
} else {
- int incr = rate*seconds_elapsed;
- *bucket += incr;
+ *bucket += (int)incr;
if (*bucket > burst || *bucket < starting_bucket) {
/* If we overflow the burst, or underflow our starting bucket,
* cap the bucket value to burst. */
@@ -2341,41 +2423,46 @@ connection_bucket_refill_helper(int *bucket, int rate, int burst,
}
}
-/** A second has rolled over; increment buckets appropriately. */
+/** Time has passed; increment buckets appropriately. */
void
-connection_bucket_refill(int seconds_elapsed, time_t now)
+connection_bucket_refill(int milliseconds_elapsed, time_t now)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
smartlist_t *conns = get_connection_array();
- int relayrate, relayburst;
+ int bandwidthrate, bandwidthburst, relayrate, relayburst;
+
+ bandwidthrate = (int)options->BandwidthRate;
+ bandwidthburst = (int)options->BandwidthBurst;
if (options->RelayBandwidthRate) {
relayrate = (int)options->RelayBandwidthRate;
relayburst = (int)options->RelayBandwidthBurst;
} else {
- relayrate = (int)options->BandwidthRate;
- relayburst = (int)options->BandwidthBurst;
+ relayrate = bandwidthrate;
+ relayburst = bandwidthburst;
}
- tor_assert(seconds_elapsed >= 0);
+ tor_assert(milliseconds_elapsed >= 0);
write_buckets_empty_last_second =
global_relayed_write_bucket <= 0 || global_write_bucket <= 0;
/* refill the global buckets */
connection_bucket_refill_helper(&global_read_bucket,
- (int)options->BandwidthRate,
- (int)options->BandwidthBurst,
- seconds_elapsed, "global_read_bucket");
+ bandwidthrate, bandwidthburst,
+ milliseconds_elapsed,
+ "global_read_bucket");
connection_bucket_refill_helper(&global_write_bucket,
- (int)options->BandwidthRate,
- (int)options->BandwidthBurst,
- seconds_elapsed, "global_write_bucket");
+ bandwidthrate, bandwidthburst,
+ milliseconds_elapsed,
+ "global_write_bucket");
connection_bucket_refill_helper(&global_relayed_read_bucket,
- relayrate, relayburst, seconds_elapsed,
+ relayrate, relayburst,
+ milliseconds_elapsed,
"global_relayed_read_bucket");
connection_bucket_refill_helper(&global_relayed_write_bucket,
- relayrate, relayburst, seconds_elapsed,
+ relayrate, relayburst,
+ milliseconds_elapsed,
"global_relayed_write_bucket");
/* refill the per-connection buckets */
@@ -2383,18 +2470,20 @@ connection_bucket_refill(int seconds_elapsed, time_t now)
{
if (connection_speaks_cells(conn)) {
or_connection_t *or_conn = TO_OR_CONN(conn);
+ int orbandwidthrate = or_conn->bandwidthrate;
+ int orbandwidthburst = or_conn->bandwidthburst;
if (connection_bucket_should_increase(or_conn->read_bucket, or_conn)) {
connection_bucket_refill_helper(&or_conn->read_bucket,
- or_conn->bandwidthrate,
- or_conn->bandwidthburst,
- seconds_elapsed,
+ orbandwidthrate,
+ orbandwidthburst,
+ milliseconds_elapsed,
"or_conn->read_bucket");
}
if (connection_bucket_should_increase(or_conn->write_bucket, or_conn)) {
connection_bucket_refill_helper(&or_conn->write_bucket,
- or_conn->bandwidthrate,
- or_conn->bandwidthburst,
- seconds_elapsed,
+ orbandwidthrate,
+ orbandwidthburst,
+ milliseconds_elapsed,
"or_conn->write_bucket");
}
}
@@ -2443,6 +2532,91 @@ connection_bucket_should_increase(int bucket, or_connection_t *conn)
return 1;
}
+#else
+static void
+connection_buckets_decrement(connection_t *conn, time_t now,
+ size_t num_read, size_t num_written)
+{
+ (void) conn;
+ (void) now;
+ (void) num_read;
+ (void) num_written;
+ /* Libevent does this for us. */
+}
+
+void
+connection_bucket_refill(int seconds_elapsed, time_t now)
+{
+ (void) seconds_elapsed;
+ (void) now;
+ /* Libevent does this for us. */
+}
+void
+connection_bucket_init(void)
+{
+ const or_options_t *options = get_options();
+ const struct timeval *tick = tor_libevent_get_one_tick_timeout();
+ struct ev_token_bucket_cfg *bucket_cfg;
+
+ uint64_t rate, burst;
+ if (options->RelayBandwidthRate) {
+ rate = options->RelayBandwidthRate;
+ burst = options->RelayBandwidthBurst;
+ } else {
+ rate = options->BandwidthRate;
+ burst = options->BandwidthBurst;
+ }
+
+ /* This can't overflow, since TokenBucketRefillInterval <= 1000,
+ * and rate started out less than INT32_MAX. */
+ rate = (rate * options->TokenBucketRefillInterval) / 1000;
+
+ bucket_cfg = ev_token_bucket_cfg_new((uint32_t)rate, (uint32_t)burst,
+ (uint32_t)rate, (uint32_t)burst,
+ tick);
+
+ if (!global_rate_limit) {
+ global_rate_limit =
+ bufferevent_rate_limit_group_new(tor_libevent_get_base(), bucket_cfg);
+ } else {
+ bufferevent_rate_limit_group_set_cfg(global_rate_limit, bucket_cfg);
+ }
+ ev_token_bucket_cfg_free(bucket_cfg);
+}
+
+void
+connection_get_rate_limit_totals(uint64_t *read_out, uint64_t *written_out)
+{
+ if (global_rate_limit == NULL) {
+ *read_out = *written_out = 0;
+ } else {
+ bufferevent_rate_limit_group_get_totals(
+ global_rate_limit, read_out, written_out);
+ }
+}
+
+/** DOCDOC */
+void
+connection_enable_rate_limiting(connection_t *conn)
+{
+ if (conn->bufev) {
+ if (!global_rate_limit)
+ connection_bucket_init();
+ tor_add_bufferevent_to_rate_limit_group(conn->bufev, global_rate_limit);
+ }
+}
+
+static void
+connection_consider_empty_write_buckets(connection_t *conn)
+{
+ (void) conn;
+}
+static void
+connection_consider_empty_read_buckets(connection_t *conn)
+{
+ (void) conn;
+}
+#endif
/** Read bytes from conn-\>s and process them.
*
@@ -2501,8 +2675,10 @@ connection_handle_read_impl(connection_t *conn)
if (CONN_IS_EDGE(conn)) {
edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
connection_edge_end_errno(edge_conn);
- if (edge_conn->socks_request) /* broken, don't send a socks reply back */
- edge_conn->socks_request->has_finished = 1;
+ if (conn->type == CONN_TYPE_AP && TO_ENTRY_CONN(conn)->socks_request) {
+ /* broken, don't send a socks reply back */
+ TO_ENTRY_CONN(conn)->socks_request->has_finished = 1;
+ }
}
connection_close_immediate(conn); /* Don't flush; connection is dead. */
connection_mark_for_close(conn);
@@ -2697,7 +2873,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
}
if (n_read > 0) {
- /* change *max_to_read */
+ /* change *max_to_read */
*max_to_read = at_most - n_read;
/* Update edge_conn->n_read */
@@ -2729,11 +2905,212 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
return 0;
}
+#ifdef USE_BUFFEREVENTS
+/* XXXX These generic versions could be simplified by making them
+ type-specific */
+
+/** Callback: Invoked whenever bytes are added to or drained from an input
+ * evbuffer. Used to track the number of bytes read. */
+static void
+evbuffer_inbuf_callback(struct evbuffer *buf,
+ const struct evbuffer_cb_info *info, void *arg)
+{
+ connection_t *conn = arg;
+ (void) buf;
+ /* XXXX These need to get real counts on the non-nested TLS case. - NM */
+ if (info->n_added) {
+ time_t now = approx_time();
+ conn->timestamp_lastread = now;
+ record_num_bytes_transferred(conn, now, info->n_added, 0);
+ connection_consider_empty_read_buckets(conn);
+ if (conn->type == CONN_TYPE_AP) {
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /*XXXX022 check for overflow*/
+ edge_conn->n_read += (int)info->n_added;
+ }
+ }
+}
+
+/** Callback: Invoked whenever bytes are added to or drained from an output
+ * evbuffer. Used to track the number of bytes written. */
+static void
+evbuffer_outbuf_callback(struct evbuffer *buf,
+ const struct evbuffer_cb_info *info, void *arg)
+{
+ connection_t *conn = arg;
+ (void)buf;
+ if (info->n_deleted) {
+ time_t now = approx_time();
+ conn->timestamp_lastwritten = now;
+ record_num_bytes_transferred(conn, now, 0, info->n_deleted);
+ connection_consider_empty_write_buckets(conn);
+ if (conn->type == CONN_TYPE_AP) {
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ /*XXXX022 check for overflow*/
+ edge_conn->n_written += (int)info->n_deleted;
+ }
+ }
+}
+
+/** Callback: invoked whenever a bufferevent has read data. */
+void
+connection_handle_read_cb(struct bufferevent *bufev, void *arg)
+{
+ connection_t *conn = arg;
+ (void) bufev;
+ if (!conn->marked_for_close) {
+ if (connection_process_inbuf(conn, 1)<0) /* XXXX Always 1? */
+ if (!conn->marked_for_close)
+ connection_mark_for_close(conn);
+ }
+}
+
+/** Callback: invoked whenever a bufferevent has written data. */
+void
+connection_handle_write_cb(struct bufferevent *bufev, void *arg)
+{
+ connection_t *conn = arg;
+ struct evbuffer *output;
+ if (connection_flushed_some(conn)<0) {
+ if (!conn->marked_for_close)
+ connection_mark_for_close(conn);
+ return;
+ }
+
+ output = bufferevent_get_output(bufev);
+ if (!evbuffer_get_length(output)) {
+ connection_finished_flushing(conn);
+ if (conn->marked_for_close && conn->hold_open_until_flushed) {
+ conn->hold_open_until_flushed = 0;
+ if (conn->linked) {
+ /* send eof */
+ bufferevent_flush(conn->bufev, EV_WRITE, BEV_FINISHED);
+ }
+ }
+ }
+}
+
+/** Callback: invoked whenever a bufferevent has had an event (like a
+ * connection, or an eof, or an error) occur. */
+void
+connection_handle_event_cb(struct bufferevent *bufev, short event, void *arg)
+{
+ connection_t *conn = arg;
+ (void) bufev;
+ if (event & BEV_EVENT_CONNECTED) {
+ tor_assert(connection_state_is_connecting(conn));
+ if (connection_finished_connecting(conn)<0)
+ return;
+ }
+ if (event & BEV_EVENT_EOF) {
+ if (!conn->marked_for_close) {
+ conn->inbuf_reached_eof = 1;
+ if (connection_reached_eof(conn)<0)
+ return;
+ }
+ }
+ if (event & BEV_EVENT_ERROR) {
+ int socket_error = evutil_socket_geterror(conn->s);
+ if (conn->type == CONN_TYPE_OR &&
+ conn->state == OR_CONN_STATE_CONNECTING) {
+ connection_or_connect_failed(TO_OR_CONN(conn),
+ errno_to_orconn_end_reason(socket_error),
+ tor_socket_strerror(socket_error));
+ } else if (CONN_IS_EDGE(conn)) {
+ edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ if (!edge_conn->edge_has_sent_end)
+ connection_edge_end_errno(edge_conn);
+ if (conn->type == CONN_TYPE_AP && TO_ENTRY_CONN(conn)->socks_request) {
+ /* broken, don't send a socks reply back */
+ TO_ENTRY_CONN(conn)->socks_request->has_finished = 1;
+ }
+ }
+ connection_close_immediate(conn); /* Connection is dead. */
+ if (!conn->marked_for_close)
+ connection_mark_for_close(conn);
+ }
+}
+
+/** Set up the generic callbacks for the bufferevent on <b>conn</b>. */
+void
+connection_configure_bufferevent_callbacks(connection_t *conn)
+{
+ struct bufferevent *bufev;
+ struct evbuffer *input, *output;
+ tor_assert(conn->bufev);
+ bufev = conn->bufev;
+ bufferevent_setcb(bufev,
+ connection_handle_read_cb,
+ connection_handle_write_cb,
+ connection_handle_event_cb,
+ conn);
+ /* Set a fairly high write low-watermark so that we get the write callback
+ called whenever data is written to bring us under 128K. Leave the
+ high-watermark at 0.
+ */
+ bufferevent_setwatermark(bufev, EV_WRITE, 128*1024, 0);
+
+ input = bufferevent_get_input(bufev);
+ output = bufferevent_get_output(bufev);
+ evbuffer_add_cb(input, evbuffer_inbuf_callback, conn);
+ evbuffer_add_cb(output, evbuffer_outbuf_callback, conn);
+}
+#endif
+
/** A pass-through to fetch_from_buf. */
int
connection_fetch_from_buf(char *string, size_t len, connection_t *conn)
{
- return fetch_from_buf(string, len, conn->inbuf);
+ IF_HAS_BUFFEREVENT(conn, {
+ /* XXX overflow -seb */
+ return (int)bufferevent_read(conn->bufev, string, len);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return fetch_from_buf(string, len, conn->inbuf);
+ }
+}
+
+/** As fetch_from_buf_line(), but read from a connection's input buffer. */
+int
+connection_fetch_from_buf_line(connection_t *conn, char *data,
+ size_t *data_len)
+{
+ IF_HAS_BUFFEREVENT(conn, {
+ int r;
+ size_t eol_len=0;
+ struct evbuffer *input = bufferevent_get_input(conn->bufev);
+ struct evbuffer_ptr ptr =
+ evbuffer_search_eol(input, NULL, &eol_len, EVBUFFER_EOL_LF);
+ if (ptr.pos == -1)
+ return 0; /* No EOL found. */
+ if ((size_t)ptr.pos+eol_len >= *data_len) {
+ return -1; /* Too long */
+ }
+ *data_len = ptr.pos+eol_len;
+ r = evbuffer_remove(input, data, ptr.pos+eol_len);
+ tor_assert(r >= 0);
+ data[ptr.pos+eol_len] = '\0';
+ return 1;
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return fetch_from_buf_line(conn->inbuf, data, data_len);
+ }
+}
+
+/** As fetch_from_buf_http, but fetches from a conncetion's input buffer_t or
+ * its bufferevent as appropriate. */
+int
+connection_fetch_from_buf_http(connection_t *conn,
+ char **headers_out, size_t max_headerlen,
+ char **body_out, size_t *body_used,
+ size_t max_bodylen, int force_complete)
+{
+ IF_HAS_BUFFEREVENT(conn, {
+ struct evbuffer *input = bufferevent_get_input(conn->bufev);
+ return fetch_from_evbuffer_http(input, headers_out, max_headerlen,
+ body_out, body_used, max_bodylen, force_complete);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return fetch_from_buf_http(conn->inbuf, headers_out, max_headerlen,
+ body_out, body_used, max_bodylen, force_complete);
+ }
}
/** Return conn-\>outbuf_flushlen: how many bytes conn wants to flush
@@ -2854,6 +3231,7 @@ connection_handle_write_impl(connection_t *conn, int force)
/* If we just flushed the last bytes, check if this tunneled dir
* request is done. */
+ /* XXXX move this to flushed_some or finished_flushing -NM */
if (buf_datalen(conn->outbuf) == 0 && conn->dirreq_id)
geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED,
DIRREQ_OR_CONN_BUFFER_FLUSHED);
@@ -2909,6 +3287,7 @@ connection_handle_write_impl(connection_t *conn, int force)
if (n_written && conn->type == CONN_TYPE_AP) {
edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+
/* Check for overflow: */
if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_written > n_written))
edge_conn->n_written += (int)n_written;
@@ -2952,6 +3331,25 @@ connection_handle_write(connection_t *conn, int force)
return res;
}
+/**
+ * Try to flush data that's waiting for a write on <b>conn</b>. Return
+ * -1 on failure, 0 on success.
+ *
+ * Don't use this function for regular writing; the buffers/bufferevents
+ * system should be good enough at scheduling writes there. Instead, this
+ * function is for cases when we're about to exit or something and we want
+ * to report it right away.
+ */
+int
+connection_flush(connection_t *conn)
+{
+ IF_HAS_BUFFEREVENT(conn, {
+ int r = bufferevent_flush(conn->bufev, EV_WRITE, BEV_FLUSH);
+ return (r < 0) ? -1 : 0;
+ });
+ return connection_handle_write(conn, 1);
+}
+
/** OpenSSL TLS record size is 16383; this is close. The goal here is to
* push data out as soon as we know there's enough for a TLS record, so
* during periods of high load we won't read entire megabytes from
@@ -2985,6 +3383,22 @@ _connection_write_to_buf_impl(const char *string, size_t len,
if (conn->marked_for_close && !conn->hold_open_until_flushed)
return;
+ IF_HAS_BUFFEREVENT(conn, {
+ if (zlib) {
+ int done = zlib < 0;
+ r = write_to_evbuffer_zlib(bufferevent_get_output(conn->bufev),
+ TO_DIR_CONN(conn)->zlib_state,
+ string, len, done);
+ } else {
+ r = bufferevent_write(conn->bufev, string, len);
+ }
+ if (r < 0) {
+ /* XXXX mark for close? */
+ log_warn(LD_NET, "bufferevent_write failed! That shouldn't happen.");
+ }
+ return;
+ });
+
old_datalen = buf_datalen(conn->outbuf);
if (zlib) {
dir_connection_t *dir_conn = TO_DIR_CONN(conn);
@@ -3012,7 +3426,13 @@ _connection_write_to_buf_impl(const char *string, size_t len,
return;
}
- connection_start_writing(conn);
+ /* If we receive optimistic data in the EXIT_CONN_STATE_RESOLVING
+ * state, we don't want to try to write it right away, since
+ * conn->write_event won't be set yet. Otherwise, write data from
+ * this conn as the socket is available. */
+ if (conn->write_event) {
+ connection_start_writing(conn);
+ }
if (zlib) {
conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen;
} else {
@@ -3138,8 +3558,7 @@ connection_get_by_type_state_rendquery(int type, int state,
type == CONN_TYPE_AP || type == CONN_TYPE_EXIT);
tor_assert(rendquery);
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
if (conn->type == type &&
!conn->marked_for_close &&
(!state || state == conn->state)) {
@@ -3154,7 +3573,33 @@ connection_get_by_type_state_rendquery(int type, int state,
TO_EDGE_CONN(conn)->rend_data->onion_address))
return conn;
}
- });
+ } SMARTLIST_FOREACH_END(conn);
+ return NULL;
+}
+
+/** Return a directory connection (if any one exists) that is fetching
+ * the item described by <b>state</b>/<b>resource</b> */
+dir_connection_t *
+connection_dir_get_by_purpose_and_resource(int purpose,
+ const char *resource)
+{
+ smartlist_t *conns = get_connection_array();
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
+ dir_connection_t *dirconn;
+ if (conn->type != CONN_TYPE_DIR || conn->marked_for_close ||
+ conn->purpose != purpose)
+ continue;
+ dirconn = TO_DIR_CONN(conn);
+ if (dirconn->requested_resource == NULL) {
+ if (resource == NULL)
+ return dirconn;
+ } else if (resource) {
+ if (0 == strcmp(resource, dirconn->requested_resource))
+ return dirconn;
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
return NULL;
}
@@ -3408,6 +3853,9 @@ connection_finished_flushing(connection_t *conn)
// log_fn(LOG_DEBUG,"entered. Socket %u.", conn->s);
+ IF_HAS_NO_BUFFEREVENT(conn)
+ connection_stop_writing(conn);
+
switch (conn->type) {
case CONN_TYPE_OR:
return connection_or_finished_flushing(TO_OR_CONN(conn));
@@ -3533,11 +3981,23 @@ assert_connection_ok(connection_t *conn, time_t now)
tor_assert(conn);
tor_assert(conn->type >= _CONN_TYPE_MIN);
tor_assert(conn->type <= _CONN_TYPE_MAX);
+
+#ifdef USE_BUFFEREVENTS
+ if (conn->bufev) {
+ tor_assert(conn->read_event == NULL);
+ tor_assert(conn->write_event == NULL);
+ tor_assert(conn->inbuf == NULL);
+ tor_assert(conn->outbuf == NULL);
+ }
+#endif
+
switch (conn->type) {
case CONN_TYPE_OR:
tor_assert(conn->magic == OR_CONNECTION_MAGIC);
break;
case CONN_TYPE_AP:
+ tor_assert(conn->magic == ENTRY_CONNECTION_MAGIC);
+ break;
case CONN_TYPE_EXIT:
tor_assert(conn->magic == EDGE_CONNECTION_MAGIC);
break;
@@ -3547,6 +4007,9 @@ assert_connection_ok(connection_t *conn, time_t now)
case CONN_TYPE_CONTROL:
tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC);
break;
+ CASE_ANY_LISTENER_TYPE:
+ tor_assert(conn->magic == LISTENER_CONNECTION_MAGIC);
+ break;
default:
tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
break;
@@ -3560,8 +4023,15 @@ assert_connection_ok(connection_t *conn, time_t now)
tor_assert(!SOCKET_OK(conn->s));
if (conn->outbuf_flushlen > 0) {
- tor_assert(connection_is_writing(conn) || conn->write_blocked_on_bw ||
- (CONN_IS_EDGE(conn) && TO_EDGE_CONN(conn)->edge_blocked_on_circ));
+ /* With optimistic data, we may have queued data in
+ * EXIT_CONN_STATE_RESOLVING while the conn is not yet marked to writing.
+ * */
+ tor_assert((conn->type == CONN_TYPE_EXIT &&
+ conn->state == EXIT_CONN_STATE_RESOLVING) ||
+ connection_is_writing(conn) ||
+ conn->write_blocked_on_bw ||
+ (CONN_IS_EDGE(conn) &&
+ TO_EDGE_CONN(conn)->edge_blocked_on_circ));
}
if (conn->hold_open_until_flushed)
@@ -3571,10 +4041,10 @@ assert_connection_ok(connection_t *conn, time_t now)
* marked_for_close. */
/* buffers */
- if (!connection_is_listener(conn)) {
+ if (conn->inbuf)
assert_buf_ok(conn->inbuf);
+ if (conn->outbuf)
assert_buf_ok(conn->outbuf);
- }
if (conn->type == CONN_TYPE_OR) {
or_connection_t *or_conn = TO_OR_CONN(conn);
@@ -3594,21 +4064,18 @@ assert_connection_ok(connection_t *conn, time_t now)
}
if (CONN_IS_EDGE(conn)) {
- edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
- if (edge_conn->chosen_exit_optional || edge_conn->chosen_exit_retries) {
- tor_assert(conn->type == CONN_TYPE_AP);
- tor_assert(edge_conn->chosen_exit_name);
- }
-
/* XXX unchecked: package window, deliver window. */
if (conn->type == CONN_TYPE_AP) {
+ entry_connection_t *entry_conn = TO_ENTRY_CONN(conn);
+ if (entry_conn->chosen_exit_optional || entry_conn->chosen_exit_retries)
+ tor_assert(entry_conn->chosen_exit_name);
- tor_assert(edge_conn->socks_request);
+ tor_assert(entry_conn->socks_request);
if (conn->state == AP_CONN_STATE_OPEN) {
- tor_assert(edge_conn->socks_request->has_finished);
+ tor_assert(entry_conn->socks_request->has_finished);
if (!conn->marked_for_close) {
- tor_assert(edge_conn->cpath_layer);
- assert_cpath_layer_ok(edge_conn->cpath_layer);
+ tor_assert(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer);
+ assert_cpath_layer_ok(ENTRY_TO_EDGE_CONN(entry_conn)->cpath_layer);
}
}
}
@@ -3624,13 +4091,7 @@ assert_connection_ok(connection_t *conn, time_t now)
switch (conn->type)
{
- case CONN_TYPE_OR_LISTENER:
- case CONN_TYPE_AP_LISTENER:
- case CONN_TYPE_AP_TRANS_LISTENER:
- case CONN_TYPE_AP_NATD_LISTENER:
- case CONN_TYPE_DIR_LISTENER:
- case CONN_TYPE_CONTROL_LISTENER:
- case CONN_TYPE_AP_DNS_LISTENER:
+ CASE_ANY_LISTENER_TYPE:
tor_assert(conn->state == LISTENER_STATE_READY);
break;
case CONN_TYPE_OR:
@@ -3647,7 +4108,7 @@ assert_connection_ok(connection_t *conn, time_t now)
case CONN_TYPE_AP:
tor_assert(conn->state >= _AP_CONN_STATE_MIN);
tor_assert(conn->state <= _AP_CONN_STATE_MAX);
- tor_assert(TO_EDGE_CONN(conn)->socks_request);
+ tor_assert(TO_ENTRY_CONN(conn)->socks_request);
break;
case CONN_TYPE_DIR:
tor_assert(conn->state >= _DIR_CONN_STATE_MIN);
@@ -3668,3 +4129,141 @@ assert_connection_ok(connection_t *conn, time_t now)
}
}
+/** Fills <b>addr</b> and <b>port</b> with the details of the global
+ * proxy server we are using.
+ * <b>conn</b> contains the connection we are using the proxy for.
+ *
+ * Return 0 on success, -1 on failure.
+ */
+int
+get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
+ const connection_t *conn)
+{
+ const or_options_t *options = get_options();
+
+ if (options->HTTPSProxy) {
+ tor_addr_copy(addr, &options->HTTPSProxyAddr);
+ *port = options->HTTPSProxyPort;
+ *proxy_type = PROXY_CONNECT;
+ return 0;
+ } else if (options->Socks4Proxy) {
+ tor_addr_copy(addr, &options->Socks4ProxyAddr);
+ *port = options->Socks4ProxyPort;
+ *proxy_type = PROXY_SOCKS4;
+ return 0;
+ } else if (options->Socks5Proxy) {
+ tor_addr_copy(addr, &options->Socks5ProxyAddr);
+ *port = options->Socks5ProxyPort;
+ *proxy_type = PROXY_SOCKS5;
+ return 0;
+ } else if (options->ClientTransportPlugin ||
+ options->Bridges) {
+ const transport_t *transport = NULL;
+ int r;
+ r = find_transport_by_bridge_addrport(&conn->addr, conn->port, &transport);
+ if (r<0)
+ return -1;
+ if (transport) { /* transport found */
+ tor_addr_copy(addr, &transport->addr);
+ *port = transport->port;
+ *proxy_type = transport->socks_version;
+ return 0;
+ }
+ }
+
+ *proxy_type = PROXY_NONE;
+ return 0;
+}
+
+/** Returns the global proxy type used by tor. */
+static int
+get_proxy_type(void)
+{
+ const or_options_t *options = get_options();
+
+ if (options->HTTPSProxy)
+ return PROXY_CONNECT;
+ else if (options->Socks4Proxy)
+ return PROXY_SOCKS4;
+ else if (options->Socks5Proxy)
+ return PROXY_SOCKS5;
+ else if (options->ClientTransportPlugin)
+ return PROXY_PLUGGABLE;
+ else
+ return PROXY_NONE;
+}
+
+/** Log a failed connection to a proxy server.
+ * <b>conn</b> is the connection we use the proxy server for. */
+void
+log_failed_proxy_connection(connection_t *conn)
+{
+ tor_addr_t proxy_addr;
+ uint16_t proxy_port;
+ int proxy_type;
+
+ if (get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, conn) != 0)
+ return; /* if we have no proxy set up, leave this function. */
+
+ log_warn(LD_NET,
+ "The connection to the %s proxy server at %s:%u just failed. "
+ "Make sure that the proxy server is up and running.",
+ proxy_type_to_string(get_proxy_type()), fmt_addr(&proxy_addr),
+ proxy_port);
+}
+
+/** Return string representation of <b>proxy_type</b>. */
+static const char *
+proxy_type_to_string(int proxy_type)
+{
+ switch (proxy_type) {
+ case PROXY_CONNECT: return "HTTP";
+ case PROXY_SOCKS4: return "SOCKS4";
+ case PROXY_SOCKS5: return "SOCKS5";
+ case PROXY_PLUGGABLE: return "pluggable transports SOCKS";
+ case PROXY_NONE: return "NULL";
+ default: tor_assert(0);
+ }
+ return NULL; /*Unreached*/
+}
+
+/** Call _connection_free() on every connection in our array, and release all
+ * storage held by connection.c. This is used by cpuworkers and dnsworkers
+ * when they fork, so they don't keep resources held open (especially
+ * sockets).
+ *
+ * Don't do the checks in connection_free(), because they will
+ * fail.
+ */
+void
+connection_free_all(void)
+{
+ smartlist_t *conns = get_connection_array();
+
+ /* We don't want to log any messages to controllers. */
+ SMARTLIST_FOREACH(conns, connection_t *, conn,
+ if (conn->type == CONN_TYPE_CONTROL)
+ TO_CONTROL_CONN(conn)->event_mask = 0);
+
+ control_update_global_event_mask();
+
+ /* Unlink everything from the identity map. */
+ connection_or_clear_identity_map();
+
+ /* Clear out our list of broken connections */
+ clear_broken_connection_map(0);
+
+ SMARTLIST_FOREACH(conns, connection_t *, conn, _connection_free(conn));
+
+ if (outgoing_addrs) {
+ SMARTLIST_FOREACH(outgoing_addrs, void*, addr, tor_free(addr));
+ smartlist_free(outgoing_addrs);
+ outgoing_addrs = NULL;
+ }
+
+#ifdef USE_BUFFEREVENTS
+ if (global_rate_limit)
+ bufferevent_rate_limit_group_free(global_rate_limit);
+#endif
+}
+
diff --git a/src/or/connection.h b/src/or/connection.h
index 576d3a63e..9f1148972 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -12,13 +12,18 @@
#ifndef _TOR_CONNECTION_H
#define _TOR_CONNECTION_H
+/* XXXX For buf_datalen in inline function */
+#include "buffers.h"
+
const char *conn_type_to_string(int type);
const char *conn_state_to_string(int type, int state);
dir_connection_t *dir_connection_new(int socket_family);
or_connection_t *or_connection_new(int socket_family);
edge_connection_t *edge_connection_new(int type, int socket_family);
+entry_connection_t *entry_connection_new(int type, int socket_family);
control_connection_t *control_connection_new(int socket_family);
+listener_connection_t *listener_connection_new(int type, int socket_family);
connection_t *connection_new(int type, int socket_family);
void connection_link_connections(connection_t *conn_a, connection_t *conn_b);
@@ -31,6 +36,21 @@ void _connection_mark_for_close(connection_t *conn,int line, const char *file);
#define connection_mark_for_close(c) \
_connection_mark_for_close((c), __LINE__, _SHORT_FILE_)
+/**
+ * Mark 'c' for close, but try to hold it open until all the data is written.
+ */
+#define _connection_mark_and_flush(c,line,file) \
+ do { \
+ connection_t *tmp_conn_ = (c); \
+ _connection_mark_for_close(tmp_conn_, (line), (file)); \
+ tmp_conn_->hold_open_until_flushed = 1; \
+ IF_HAS_BUFFEREVENT(tmp_conn_, \
+ connection_start_writing(tmp_conn_)); \
+ } while (0)
+
+#define connection_mark_and_flush(c) \
+ _connection_mark_and_flush((c), __LINE__, _SHORT_FILE_)
+
void connection_expire_held_open(void);
int connection_connect(connection_t *conn, const char *address,
@@ -39,6 +59,9 @@ int connection_connect(connection_t *conn, const char *address,
int connection_proxy_connect(connection_t *conn, int type);
int connection_read_proxy_handshake(connection_t *conn);
+void log_failed_proxy_connection(connection_t *conn);
+int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
+ const connection_t *conn);
int retry_all_listeners(smartlist_t *replaced_conns,
smartlist_t *new_conns);
@@ -51,10 +74,18 @@ void connection_bucket_refill(int seconds_elapsed, time_t now);
int connection_handle_read(connection_t *conn);
int connection_fetch_from_buf(char *string, size_t len, connection_t *conn);
+int connection_fetch_from_buf_line(connection_t *conn, char *data,
+ size_t *data_len);
+int connection_fetch_from_buf_http(connection_t *conn,
+ char **headers_out, size_t max_headerlen,
+ char **body_out, size_t *body_used,
+ size_t max_bodylen, int force_complete);
int connection_wants_to_flush(connection_t *conn);
int connection_outbuf_too_full(connection_t *conn);
int connection_handle_write(connection_t *conn, int force);
+int connection_flush(connection_t *conn);
+
void _connection_write_to_buf_impl(const char *string, size_t len,
connection_t *conn, int zlib);
static void connection_write_to_buf(const char *string, size_t len,
@@ -73,6 +104,29 @@ connection_write_to_buf_zlib(const char *string, size_t len,
_connection_write_to_buf_impl(string, len, TO_CONN(conn), done ? -1 : 1);
}
+static size_t connection_get_inbuf_len(connection_t *conn);
+static size_t connection_get_outbuf_len(connection_t *conn);
+
+static INLINE size_t
+connection_get_inbuf_len(connection_t *conn)
+{
+ IF_HAS_BUFFEREVENT(conn, {
+ return evbuffer_get_length(bufferevent_get_input(conn->bufev));
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return conn->inbuf ? buf_datalen(conn->inbuf) : 0;
+ }
+}
+
+static INLINE size_t
+connection_get_outbuf_len(connection_t *conn)
+{
+ IF_HAS_BUFFEREVENT(conn, {
+ return evbuffer_get_length(bufferevent_get_output(conn->bufev));
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return conn->outbuf ? buf_datalen(conn->outbuf) : 0;
+ }
+}
+
connection_t *connection_get_by_global_id(uint64_t id);
connection_t *connection_get_by_type(int type);
@@ -83,6 +137,8 @@ connection_t *connection_get_by_type_addr_port_purpose(int type,
connection_t *connection_get_by_type_state(int type, int state);
connection_t *connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery);
+dir_connection_t *connection_dir_get_by_purpose_and_resource(
+ int state, const char *resource);
#define connection_speaks_cells(conn) ((conn)->type == CONN_TYPE_OR)
int connection_is_listener(connection_t *conn);
@@ -96,5 +152,19 @@ int connection_or_nonopen_was_started_here(or_connection_t *conn);
void connection_dump_buffer_mem_stats(int severity);
void remove_file_if_very_old(const char *fname, time_t now);
+#ifdef USE_BUFFEREVENTS
+int connection_type_uses_bufferevent(connection_t *conn);
+void connection_configure_bufferevent_callbacks(connection_t *conn);
+void connection_handle_read_cb(struct bufferevent *bufev, void *arg);
+void connection_handle_write_cb(struct bufferevent *bufev, void *arg);
+void connection_handle_event_cb(struct bufferevent *bufev, short event,
+ void *arg);
+void connection_get_rate_limit_totals(uint64_t *read_out,
+ uint64_t *written_out);
+void connection_enable_rate_limiting(connection_t *conn);
+#else
+#define connection_type_uses_bufferevent(c) (0)
+#endif
+
#endif
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 4763bf59a..efaad79b6 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -23,6 +23,7 @@
#include "dirserv.h"
#include "hibernate.h"
#include "main.h"
+#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
#include "relay.h"
@@ -50,27 +51,43 @@
#define SOCKS4_GRANTED 90
#define SOCKS4_REJECT 91
-static int connection_ap_handshake_process_socks(edge_connection_t *conn);
-static int connection_ap_process_natd(edge_connection_t *conn);
+static int connection_ap_handshake_process_socks(entry_connection_t *conn);
+static int connection_ap_process_natd(entry_connection_t *conn);
static int connection_exit_connect_dir(edge_connection_t *exitconn);
static int address_is_in_virtual_range(const char *addr);
-static int consider_plaintext_ports(edge_connection_t *conn, uint16_t port);
+static int consider_plaintext_ports(entry_connection_t *conn, uint16_t port);
static void clear_trackexithost_mappings(const char *exitname);
+static int connection_ap_supports_optimistic_data(const entry_connection_t *);
/** An AP stream has failed/finished. If it hasn't already sent back
* a socks reply, send one now (based on endreason). Also set
* has_sent_end to 1, and mark the conn.
*/
void
-_connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
+_connection_mark_unattached_ap(entry_connection_t *conn, int endreason,
int line, const char *file)
{
- tor_assert(conn->_base.type == CONN_TYPE_AP);
- conn->edge_has_sent_end = 1; /* no circ yet */
-
- if (conn->_base.marked_for_close) {
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
+ tor_assert(base_conn->type == CONN_TYPE_AP);
+ ENTRY_TO_EDGE_CONN(conn)->edge_has_sent_end = 1; /* no circ yet */
+
+ /* If this is a rendezvous stream and it is failing without ever
+ * being attached to a circuit, assume that an attempt to connect to
+ * the destination hidden service has just ended.
+ *
+ * XXX023 This condition doesn't limit to only streams failing
+ * without ever being attached. That sloppiness should be harmless,
+ * but we should fix it someday anyway. */
+ if ((edge_conn->on_circuit != NULL || edge_conn->edge_has_sent_end) &&
+ connection_edge_is_rendezvous_stream(edge_conn)) {
+ rend_client_note_connection_attempt_ended(
+ edge_conn->rend_data->onion_address);
+ }
+
+ if (base_conn->marked_for_close) {
/* This call will warn as appropriate. */
- _connection_mark_for_close(TO_CONN(conn), line, file);
+ _connection_mark_for_close(base_conn, line, file);
return;
}
@@ -90,9 +107,9 @@ _connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
conn->socks_request->has_finished = 1;
}
- _connection_mark_for_close(TO_CONN(conn), line, file);
- conn->_base.hold_open_until_flushed = 1;
- conn->end_reason = endreason;
+ _connection_mark_and_flush(base_conn, line, file);
+
+ ENTRY_TO_EDGE_CONN(conn)->end_reason = endreason;
}
/** There was an EOF. Send an end and mark the connection for close.
@@ -100,7 +117,7 @@ _connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
int
connection_edge_reached_eof(edge_connection_t *conn)
{
- if (buf_datalen(conn->_base.inbuf) &&
+ if (connection_get_inbuf_len(TO_CONN(conn)) &&
connection_state_is_open(TO_CONN(conn))) {
/* it still has stuff to process. don't let it die yet. */
return 0;
@@ -110,8 +127,11 @@ connection_edge_reached_eof(edge_connection_t *conn)
/* only mark it if not already marked. it's possible to
* get the 'end' right around when the client hangs up on us. */
connection_edge_end(conn, END_STREAM_REASON_DONE);
- if (conn->socks_request) /* eof, so don't send a socks reply back */
- conn->socks_request->has_finished = 1;
+ if (conn->_base.type == CONN_TYPE_AP) {
+ /* eof, so don't send a socks reply back */
+ if (EDGE_TO_ENTRY_CONN(conn)->socks_request)
+ EDGE_TO_ENTRY_CONN(conn)->socks_request->has_finished = 1;
+ }
connection_mark_for_close(TO_CONN(conn));
}
return 0;
@@ -134,13 +154,13 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
switch (conn->_base.state) {
case AP_CONN_STATE_SOCKS_WAIT:
- if (connection_ap_handshake_process_socks(conn) < 0) {
+ if (connection_ap_handshake_process_socks(EDGE_TO_ENTRY_CONN(conn)) <0) {
/* already marked */
return -1;
}
return 0;
case AP_CONN_STATE_NATD_WAIT:
- if (connection_ap_process_natd(conn) < 0) {
+ if (connection_ap_process_natd(EDGE_TO_ENTRY_CONN(conn)) < 0) {
/* already marked */
return -1;
}
@@ -153,10 +173,26 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
return -1;
}
return 0;
+ case AP_CONN_STATE_CONNECT_WAIT:
+ if (connection_ap_supports_optimistic_data(EDGE_TO_ENTRY_CONN(conn))) {
+ log_info(LD_EDGE,
+ "data from edge while in '%s' state. Sending it anyway. "
+ "package_partial=%d, buflen=%ld",
+ conn_state_to_string(conn->_base.type, conn->_base.state),
+ package_partial,
+ (long)connection_get_inbuf_len(TO_CONN(conn)));
+ if (connection_edge_package_raw_inbuf(conn, package_partial, NULL)<0) {
+ /* (We already sent an end cell if possible) */
+ connection_mark_for_close(TO_CONN(conn));
+ return -1;
+ }
+ return 0;
+ }
+ /* Fall through if the connection is on a circuit without optimistic
+ * data support. */
case EXIT_CONN_STATE_CONNECTING:
case AP_CONN_STATE_RENDDESC_WAIT:
case AP_CONN_STATE_CIRCUIT_WAIT:
- case AP_CONN_STATE_CONNECT_WAIT:
case AP_CONN_STATE_RESOLVE_WAIT:
case AP_CONN_STATE_CONTROLLER_WAIT:
log_info(LD_EDGE,
@@ -181,9 +217,10 @@ connection_edge_destroy(circid_t circ_id, edge_connection_t *conn)
log_info(LD_EDGE,
"CircID %d: At an edge. Marking connection for close.", circ_id);
if (conn->_base.type == CONN_TYPE_AP) {
- connection_mark_unattached_ap(conn, END_STREAM_REASON_DESTROY);
+ entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_DESTROY);
control_event_stream_bandwidth(conn);
- control_event_stream_status(conn, STREAM_EVENT_CLOSED,
+ control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED,
END_STREAM_REASON_DESTROY);
conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED;
} else {
@@ -191,8 +228,7 @@ connection_edge_destroy(circid_t circ_id, edge_connection_t *conn)
conn->edge_has_sent_end = 1;
conn->end_reason = END_STREAM_REASON_DESTROY;
conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED;
- connection_mark_for_close(TO_CONN(conn));
- conn->_base.hold_open_until_flushed = 1;
+ connection_mark_and_flush(TO_CONN(conn));
}
}
conn->cpath_layer = NULL;
@@ -336,7 +372,6 @@ connection_edge_finished_flushing(edge_connection_t *conn)
switch (conn->_base.state) {
case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN:
- connection_stop_writing(TO_CONN(conn));
connection_edge_consider_sending_sendme(conn);
return 0;
case AP_CONN_STATE_SOCKS_WAIT:
@@ -345,7 +380,7 @@ connection_edge_finished_flushing(edge_connection_t *conn)
case AP_CONN_STATE_CIRCUIT_WAIT:
case AP_CONN_STATE_CONNECT_WAIT:
case AP_CONN_STATE_CONTROLLER_WAIT:
- connection_stop_writing(TO_CONN(conn));
+ case AP_CONN_STATE_RESOLVE_WAIT:
return 0;
default:
log_warn(LD_BUG, "Called in unexpected state %d.",conn->_base.state);
@@ -375,8 +410,9 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
rep_hist_note_exit_stream_opened(conn->port);
conn->state = EXIT_CONN_STATE_OPEN;
- connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
- if (connection_wants_to_flush(conn)) /* in case there are any queued relay
+ IF_HAS_NO_BUFFEREVENT(conn)
+ connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
+ if (connection_get_outbuf_len(conn)) /* in case there are any queued relay
* cells */
connection_start_writing(conn);
/* deliver a 'connected' relay cell back through the circuit. */
@@ -408,13 +444,79 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
return connection_edge_process_inbuf(edge_conn, 1);
}
+/** Common code to connection_(ap|exit)_about_to_close. */
+static void
+connection_edge_about_to_close(edge_connection_t *edge_conn)
+{
+ if (!edge_conn->edge_has_sent_end) {
+ connection_t *conn = TO_CONN(edge_conn);
+ log_warn(LD_BUG, "(Harmless.) Edge connection (marked at %s:%d) "
+ "hasn't sent end yet?",
+ conn->marked_for_close_file, conn->marked_for_close);
+ tor_fragile_assert();
+ }
+}
+
+/* Called when we're about to finally unlink and free an AP (client)
+ * connection: perform necessary accounting and cleanup */
+void
+connection_ap_about_to_close(entry_connection_t *entry_conn)
+{
+ circuit_t *circ;
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
+ connection_t *conn = ENTRY_TO_CONN(entry_conn);
+
+ if (entry_conn->socks_request->has_finished == 0) {
+ /* since conn gets removed right after this function finishes,
+ * there's no point trying to send back a reply at this point. */
+ log_warn(LD_BUG,"Closing stream (marked at %s:%d) without sending"
+ " back a socks reply.",
+ conn->marked_for_close_file, conn->marked_for_close);
+ }
+ if (!edge_conn->end_reason) {
+ log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having"
+ " set end_reason.",
+ conn->marked_for_close_file, conn->marked_for_close);
+ }
+ if (entry_conn->dns_server_request) {
+ log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having"
+ " replied to DNS request.",
+ conn->marked_for_close_file, conn->marked_for_close);
+ dnsserv_reject_request(entry_conn);
+ }
+ control_event_stream_bandwidth(edge_conn);
+ control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED,
+ edge_conn->end_reason);
+ circ = circuit_get_by_edge_conn(edge_conn);
+ if (circ)
+ circuit_detach_stream(circ, edge_conn);
+}
+
+/* Called when we're about to finally unlink and free an exit
+ * connection: perform necessary accounting and cleanup */
+void
+connection_exit_about_to_close(edge_connection_t *edge_conn)
+{
+ circuit_t *circ;
+ connection_t *conn = TO_CONN(edge_conn);
+
+ connection_edge_about_to_close(edge_conn);
+
+ circ = circuit_get_by_edge_conn(edge_conn);
+ if (circ)
+ circuit_detach_stream(circ, edge_conn);
+ if (conn->state == EXIT_CONN_STATE_RESOLVING) {
+ connection_dns_remove(edge_conn);
+ }
+}
+
/** Define a schedule for how long to wait between retrying
* application connections. Rather than waiting a fixed amount of
* time between each retry, we wait 10 seconds each for the first
* two tries, and 15 seconds for each retry after
* that. Hopefully this will improve the expected user experience. */
static int
-compute_retry_timeout(edge_connection_t *conn)
+compute_retry_timeout(entry_connection_t *conn)
{
int timeout = get_options()->CircuitStreamTimeout;
if (timeout) /* if our config options override the default, use them */
@@ -437,41 +539,43 @@ void
connection_ap_expire_beginning(void)
{
edge_connection_t *conn;
+ entry_connection_t *entry_conn;
circuit_t *circ;
time_t now = time(NULL);
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int severity;
int cutoff;
int seconds_idle, seconds_since_born;
smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) {
- if (c->type != CONN_TYPE_AP || c->marked_for_close)
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
+ if (base_conn->type != CONN_TYPE_AP || base_conn->marked_for_close)
continue;
- conn = TO_EDGE_CONN(c);
+ entry_conn = TO_ENTRY_CONN(base_conn);
+ conn = ENTRY_TO_EDGE_CONN(entry_conn);
/* if it's an internal linked connection, don't yell its status. */
- severity = (tor_addr_is_null(&conn->_base.addr) && !conn->_base.port)
+ severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port)
? LOG_INFO : LOG_NOTICE;
- seconds_idle = (int)( now - conn->_base.timestamp_lastread );
- seconds_since_born = (int)( now - conn->_base.timestamp_created );
+ seconds_idle = (int)( now - base_conn->timestamp_lastread );
+ seconds_since_born = (int)( now - base_conn->timestamp_created );
- if (conn->_base.state == AP_CONN_STATE_OPEN)
+ if (base_conn->state == AP_CONN_STATE_OPEN)
continue;
/* We already consider SocksTimeout in
* connection_ap_handshake_attach_circuit(), but we need to consider
* it here too because controllers that put streams in controller_wait
* state never ask Tor to attach the circuit. */
- if (AP_CONN_STATE_IS_UNATTACHED(conn->_base.state)) {
+ if (AP_CONN_STATE_IS_UNATTACHED(base_conn->state)) {
if (seconds_since_born >= options->SocksTimeout) {
log_fn(severity, LD_APP,
"Tried for %d seconds to get a connection to %s:%d. "
"Giving up. (%s)",
seconds_since_born,
- safe_str_client(conn->socks_request->address),
- conn->socks_request->port,
- conn_state_to_string(CONN_TYPE_AP, conn->_base.state));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
+ safe_str_client(entry_conn->socks_request->address),
+ entry_conn->socks_request->port,
+ conn_state_to_string(CONN_TYPE_AP, base_conn->state));
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
}
continue;
}
@@ -479,14 +583,14 @@ connection_ap_expire_beginning(void)
/* We're in state connect_wait or resolve_wait now -- waiting for a
* reply to our relay cell. See if we want to retry/give up. */
- cutoff = compute_retry_timeout(conn);
+ cutoff = compute_retry_timeout(entry_conn);
if (seconds_idle < cutoff)
continue;
circ = circuit_get_by_edge_conn(conn);
if (!circ) { /* it's vanished? */
log_info(LD_APP,"Conn is waiting (address %s), but lost its circ.",
- safe_str_client(conn->socks_request->address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
+ safe_str_client(entry_conn->socks_request->address));
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
continue;
}
if (circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
@@ -495,9 +599,9 @@ connection_ap_expire_beginning(void)
"Rend stream is %d seconds late. Giving up on address"
" '%s.onion'.",
seconds_idle,
- safe_str_client(conn->socks_request->address));
+ safe_str_client(entry_conn->socks_request->address));
connection_edge_end(conn, END_STREAM_REASON_TIMEOUT);
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
}
continue;
}
@@ -506,9 +610,10 @@ connection_ap_expire_beginning(void)
"We tried for %d seconds to connect to '%s' using exit %s."
" Retrying on a new circuit.",
seconds_idle,
- safe_str_client(conn->socks_request->address),
+ safe_str_client(entry_conn->socks_request->address),
conn->cpath_layer ?
- extend_info_describe(conn->cpath_layer->extend_info): "*unnamed*");
+ extend_info_describe(conn->cpath_layer->extend_info):
+ "*unnamed*");
/* send an end down the circuit */
connection_edge_end(conn, END_STREAM_REASON_TIMEOUT);
/* un-mark it as ending, since we're going to reuse it */
@@ -522,15 +627,16 @@ connection_ap_expire_beginning(void)
circ->timestamp_dirty -= options->MaxCircuitDirtiness;
/* give our stream another 'cutoff' seconds to try */
conn->_base.timestamp_lastread += cutoff;
- if (conn->num_socks_retries < 250) /* avoid overflow */
- conn->num_socks_retries++;
+ if (entry_conn->num_socks_retries < 250) /* avoid overflow */
+ entry_conn->num_socks_retries++;
/* move it back into 'pending' state, and try to attach. */
- if (connection_ap_detach_retriable(conn, TO_ORIGIN_CIRCUIT(circ),
+ if (connection_ap_detach_retriable(entry_conn, TO_ORIGIN_CIRCUIT(circ),
END_STREAM_REASON_TIMEOUT)<0) {
- if (!conn->_base.marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
+ if (!base_conn->marked_for_close)
+ connection_mark_unattached_ap(entry_conn,
+ END_STREAM_REASON_CANT_ATTACH);
}
- } SMARTLIST_FOREACH_END(conn);
+ } SMARTLIST_FOREACH_END(base_conn);
}
/** Tell any AP streams that are waiting for a new circuit to try again,
@@ -539,7 +645,7 @@ connection_ap_expire_beginning(void)
void
connection_ap_attach_pending(void)
{
- edge_connection_t *edge_conn;
+ entry_connection_t *entry_conn;
smartlist_t *conns = get_connection_array();
SMARTLIST_FOREACH(conns, connection_t *, conn,
{
@@ -547,10 +653,10 @@ connection_ap_attach_pending(void)
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (connection_ap_handshake_attach_circuit(edge_conn) < 0) {
- if (!edge_conn->_base.marked_for_close)
- connection_mark_unattached_ap(edge_conn,
+ entry_conn = TO_ENTRY_CONN(conn);
+ if (connection_ap_handshake_attach_circuit(entry_conn) < 0) {
+ if (!conn->marked_for_close)
+ connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_CANT_ATTACH);
}
});
@@ -565,7 +671,7 @@ void
connection_ap_fail_onehop(const char *failed_digest,
cpath_build_state_t *build_state)
{
- edge_connection_t *edge_conn;
+ entry_connection_t *entry_conn;
char digest[DIGEST_LEN];
smartlist_t *conns = get_connection_array();
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
@@ -573,27 +679,27 @@ connection_ap_fail_onehop(const char *failed_digest,
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (!edge_conn->want_onehop)
+ entry_conn = TO_ENTRY_CONN(conn);
+ if (!entry_conn->want_onehop)
continue;
- if (hexdigest_to_digest(edge_conn->chosen_exit_name, digest) < 0 ||
+ if (hexdigest_to_digest(entry_conn->chosen_exit_name, digest) < 0 ||
tor_memneq(digest, failed_digest, DIGEST_LEN))
continue;
if (tor_digest_is_zero(digest)) {
/* we don't know the digest; have to compare addr:port */
tor_addr_t addr;
if (!build_state || !build_state->chosen_exit ||
- !edge_conn->socks_request || !edge_conn->socks_request->address)
+ !entry_conn->socks_request || !entry_conn->socks_request->address)
continue;
- if (tor_addr_from_str(&addr, edge_conn->socks_request->address)<0 ||
+ if (tor_addr_parse(&addr, entry_conn->socks_request->address)<0 ||
!tor_addr_eq(&build_state->chosen_exit->addr, &addr) ||
- build_state->chosen_exit->port != edge_conn->socks_request->port)
+ build_state->chosen_exit->port != entry_conn->socks_request->port)
continue;
}
log_info(LD_APP, "Closing one-hop stream to '%s/%s' because the OR conn "
- "just failed.", edge_conn->chosen_exit_name,
- edge_conn->socks_request->address);
- connection_mark_unattached_ap(edge_conn, END_STREAM_REASON_TIMEOUT);
+ "just failed.", entry_conn->chosen_exit_name,
+ entry_conn->socks_request->address);
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
} SMARTLIST_FOREACH_END(conn);
}
@@ -604,8 +710,8 @@ connection_ap_fail_onehop(const char *failed_digest,
void
circuit_discard_optional_exit_enclaves(extend_info_t *info)
{
- edge_connection_t *edge_conn;
- routerinfo_t *r1, *r2;
+ entry_connection_t *entry_conn;
+ const node_t *r1, *r2;
smartlist_t *conns = get_connection_array();
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
@@ -613,32 +719,32 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (!edge_conn->chosen_exit_optional &&
- !edge_conn->chosen_exit_retries)
+ entry_conn = TO_ENTRY_CONN(conn);
+ if (!entry_conn->chosen_exit_optional &&
+ !entry_conn->chosen_exit_retries)
continue;
- r1 = router_get_by_nickname(edge_conn->chosen_exit_name, 0);
- r2 = router_get_by_digest(info->identity_digest);
+ r1 = node_get_by_nickname(entry_conn->chosen_exit_name, 0);
+ r2 = node_get_by_id(info->identity_digest);
if (!r1 || !r2 || r1 != r2)
continue;
- tor_assert(edge_conn->socks_request);
- if (edge_conn->chosen_exit_optional) {
+ tor_assert(entry_conn->socks_request);
+ if (entry_conn->chosen_exit_optional) {
log_info(LD_APP, "Giving up on enclave exit '%s' for destination %s.",
- safe_str_client(edge_conn->chosen_exit_name),
- escaped_safe_str_client(edge_conn->socks_request->address));
- edge_conn->chosen_exit_optional = 0;
- tor_free(edge_conn->chosen_exit_name); /* clears it */
+ safe_str_client(entry_conn->chosen_exit_name),
+ escaped_safe_str_client(entry_conn->socks_request->address));
+ entry_conn->chosen_exit_optional = 0;
+ tor_free(entry_conn->chosen_exit_name); /* clears it */
/* if this port is dangerous, warn or reject it now that we don't
* think it'll be using an enclave. */
- consider_plaintext_ports(edge_conn, edge_conn->socks_request->port);
+ consider_plaintext_ports(entry_conn, entry_conn->socks_request->port);
}
- if (edge_conn->chosen_exit_retries) {
- if (--edge_conn->chosen_exit_retries == 0) { /* give up! */
- clear_trackexithost_mappings(edge_conn->chosen_exit_name);
- tor_free(edge_conn->chosen_exit_name); /* clears it */
+ if (entry_conn->chosen_exit_retries) {
+ if (--entry_conn->chosen_exit_retries == 0) { /* give up! */
+ clear_trackexithost_mappings(entry_conn->chosen_exit_name);
+ tor_free(entry_conn->chosen_exit_name); /* clears it */
/* if this port is dangerous, warn or reject it now that we don't
* think it'll be using an enclave. */
- consider_plaintext_ports(edge_conn, edge_conn->socks_request->port);
+ consider_plaintext_ports(entry_conn, entry_conn->socks_request->port);
}
}
} SMARTLIST_FOREACH_END(conn);
@@ -652,20 +758,27 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
* Returns -1 on err, 1 on success, 0 on not-yet-sure.
*/
int
-connection_ap_detach_retriable(edge_connection_t *conn, origin_circuit_t *circ,
+connection_ap_detach_retriable(entry_connection_t *conn,
+ origin_circuit_t *circ,
int reason)
{
control_event_stream_status(conn, STREAM_EVENT_FAILED_RETRIABLE, reason);
- conn->_base.timestamp_lastread = time(NULL);
+ ENTRY_TO_CONN(conn)->timestamp_lastread = time(NULL);
+
+ if (conn->pending_optimistic_data) {
+ generic_buffer_set_to_copy(&conn->sending_optimistic_data,
+ conn->pending_optimistic_data);
+ }
+
if (!get_options()->LeaveStreamsUnattached || conn->use_begindir) {
/* If we're attaching streams ourself, or if this connection is
* a tunneled directory connection, then just attach it. */
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
- circuit_detach_stream(TO_CIRCUIT(circ),conn);
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
return connection_ap_handshake_attach_circuit(conn);
} else {
- conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
- circuit_detach_stream(TO_CIRCUIT(circ),conn);
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
+ circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
return 0;
}
}
@@ -813,7 +926,7 @@ clear_trackexithost_mappings(const char *exitname)
* host is unknown or no longer allowed, or for which the source address
* is no longer in trackexithosts. */
void
-addressmap_clear_excluded_trackexithosts(or_options_t *options)
+addressmap_clear_excluded_trackexithosts(const or_options_t *options)
{
const routerset_t *allow_nodes = options->ExitNodes;
const routerset_t *exclude_nodes = options->_ExcludeExitNodesUnion;
@@ -829,7 +942,7 @@ addressmap_clear_excluded_trackexithosts(or_options_t *options)
size_t len;
const char *target = ent->new_address, *dot;
char *nodename;
- routerinfo_t *ri; /* XXX023 Use node_t. */
+ const node_t *node;
if (!target) {
/* DNS resolving in progress */
@@ -849,11 +962,11 @@ addressmap_clear_excluded_trackexithosts(or_options_t *options)
dot--;
if (*dot == '.') dot++;
nodename = tor_strndup(dot, len-5-(dot-target));;
- ri = router_get_by_nickname(nodename, 0);
+ node = node_get_by_nickname(nodename, 0);
tor_free(nodename);
- if (!ri ||
- (allow_nodes && !routerset_contains_router(allow_nodes, ri)) ||
- routerset_contains_router(exclude_nodes, ri) ||
+ if (!node ||
+ (allow_nodes && !routerset_contains_node(allow_nodes, node)) ||
+ routerset_contains_node(exclude_nodes, node) ||
!hostname_in_track_host_exits(options, address)) {
/* We don't know this one, or we want to be rid of it. */
addressmap_ent_remove(address, ent);
@@ -867,7 +980,7 @@ addressmap_clear_excluded_trackexithosts(or_options_t *options)
* no longer allowed by AutomapHostsOnResolve, or for which the
* target address is no longer in the virtual network. */
void
-addressmap_clear_invalid_automaps(or_options_t *options)
+addressmap_clear_invalid_automaps(const or_options_t *options)
{
int clear_all = !options->AutomapHostsOnResolve;
const smartlist_t *suffixes = options->AutomapHostsSuffixes;
@@ -1314,7 +1427,6 @@ static char *
addressmap_get_virtual_address(int type)
{
char buf[64];
- struct in_addr in;
tor_assert(addressmap);
if (type == RESOLVED_TYPE_HOSTNAME) {
@@ -1328,6 +1440,7 @@ addressmap_get_virtual_address(int type)
} else if (type == RESOLVED_TYPE_IPV4) {
// This is an imperfect estimate of how many addresses are available, but
// that's ok.
+ struct in_addr in;
uint32_t available = 1u << (32-virtual_addr_netmask_bits);
while (available) {
/* Don't hand out any .0 or .255 address. */
@@ -1519,9 +1632,9 @@ addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
/** Check if <b>conn</b> is using a dangerous port. Then warn and/or
* reject depending on our config options. */
static int
-consider_plaintext_ports(edge_connection_t *conn, uint16_t port)
+consider_plaintext_ports(entry_connection_t *conn, uint16_t port)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int reject = smartlist_string_num_isin(options->RejectPlaintextPorts, port);
if (smartlist_string_num_isin(options->WarnPlaintextPorts, port)) {
@@ -1554,14 +1667,14 @@ consider_plaintext_ports(edge_connection_t *conn, uint16_t port)
* documentation for arguments and return value.
*/
int
-connection_ap_rewrite_and_attach_if_allowed(edge_connection_t *conn,
+connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (options->LeaveStreamsUnattached) {
- conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
return 0;
}
return connection_ap_handshake_rewrite_and_attach(conn, circ, cpath);
@@ -1583,13 +1696,13 @@ connection_ap_rewrite_and_attach_if_allowed(edge_connection_t *conn,
* <b>cpath</b> is NULL.
*/
int
-connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
+connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath)
{
socks_request_t *socks = conn->socks_request;
hostname_type_t addresstype;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
struct in_addr addr_tmp;
/* We set this to true if this is an address we should automatically
* remap to a local address in VirtualAddrNetwork */
@@ -1600,6 +1713,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
address, and we remap it to one because of an entry in the addressmap. */
int remapped_to_exit = 0;
time_t now = time(NULL);
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
tor_strlower(socks->address); /* normalize it */
strlcpy(orig_address, socks->address, sizeof(orig_address));
@@ -1607,6 +1721,9 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
safe_str_client(socks->address),
socks->port);
+ if (! conn->original_dest_address)
+ conn->original_dest_address = tor_strdup(conn->socks_request->address);
+
if (socks->command == SOCKS_COMMAND_RESOLVE &&
!tor_inet_aton(socks->address, &addr_tmp) &&
options->AutomapHostsOnResolve && options->AutomapHostsSuffixes) {
@@ -1652,7 +1769,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
/* Don't let people try to do a reverse lookup on 10.0.0.1. */
tor_addr_t addr;
int ok;
- ok = tor_addr_parse_reverse_lookup_name(
+ ok = tor_addr_parse_PTR_name(
&addr, socks->address, AF_UNSPEC, 1);
if (ok == 1 && tor_addr_is_internal(&addr, 0)) {
connection_ap_handshake_socks_resolved(conn, RESOLVED_TYPE_ERROR,
@@ -1712,15 +1829,14 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
/* If StrictNodes is not set, then .exit overrides ExcludeNodes. */
routerset_t *excludeset = options->StrictNodes ?
options->_ExcludeExitNodesUnion : options->ExcludeExitNodes;
- /*XXX023 make this a node_t. */
- routerinfo_t *router;
+ const node_t *node;
tor_assert(!automap);
if (s) {
/* The address was of the form "(stuff).(name).exit */
if (s[1] != '\0') {
conn->chosen_exit_name = tor_strdup(s+1);
- router = router_get_by_nickname(conn->chosen_exit_name, 1);
+ node = node_get_by_nickname(conn->chosen_exit_name, 1);
if (remapped_to_exit) /* 5 tries before it expires the addressmap */
conn->chosen_exit_retries = TRACKHOSTEXITS_RETRIES;
*s = 0;
@@ -1735,15 +1851,16 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
}
} else {
/* It looks like they just asked for "foo.exit". */
+
conn->chosen_exit_name = tor_strdup(socks->address);
- router = router_get_by_nickname(conn->chosen_exit_name, 1);
- if (router) {
+ node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ if (node) {
*socks->address = 0;
- strlcpy(socks->address, router->address, sizeof(socks->address));
+ node_get_address_string(node, socks->address, sizeof(socks->address));
}
}
/* Now make sure that the chosen exit exists... */
- if (!router) {
+ if (!node) {
log_warn(LD_APP,
"Unrecognized relay in exit address '%s.exit'. Refusing.",
safe_str_client(socks->address));
@@ -1751,7 +1868,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
return -1;
}
/* ...and make sure that it isn't excluded. */
- if (routerset_contains_router(excludeset, router)) {
+ if (routerset_contains_node(excludeset, node)) {
log_warn(LD_APP,
"Excluded relay in exit address '%s.exit'. Refusing.",
safe_str_client(socks->address));
@@ -1804,7 +1921,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
if (options->ClientRejectInternalAddresses &&
!conn->use_begindir && !conn->chosen_exit_name && !circ) {
tor_addr_t addr;
- if (tor_addr_from_str(&addr, socks->address) >= 0 &&
+ if (tor_addr_parse(&addr, socks->address) >= 0 &&
tor_addr_is_internal(&addr, 0)) {
/* If this is an explicit private address with no chosen exit node,
* then we really don't want to try to connect to it. That's
@@ -1826,17 +1943,16 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
if (!conn->use_begindir && !conn->chosen_exit_name && !circ) {
/* see if we can find a suitable enclave exit */
- routerinfo_t *r =
+ const node_t *r =
router_find_exact_exit_enclave(socks->address, socks->port);
if (r) {
log_info(LD_APP,
"Redirecting address %s to exit at enclave router %s",
- safe_str_client(socks->address),
- router_describe(r));
+ safe_str_client(socks->address), node_describe(r));
/* use the hex digest, not nickname, in case there are two
routers with this nickname */
conn->chosen_exit_name =
- tor_strdup(hex_str(r->cache_info.identity_digest, DIGEST_LEN));
+ tor_strdup(hex_str(r->identity, DIGEST_LEN));
conn->chosen_exit_optional = 1;
}
}
@@ -1856,12 +1972,12 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
} else {
tor_fragile_assert();
}
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
if ((circ && connection_ap_handshake_attach_chosen_circuit(
conn, circ, cpath) < 0) ||
(!circ &&
connection_ap_handshake_attach_circuit(conn) < 0)) {
- if (!conn->_base.marked_for_close)
+ if (!base_conn->marked_for_close)
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
return -1;
}
@@ -1871,6 +1987,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
rend_cache_entry_t *entry;
int r;
rend_service_authorization_t *client_auth;
+ rend_data_t *rend_data;
tor_assert(!automap);
if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
/* if it's a resolve request, fail it right now, rather than
@@ -1892,16 +2009,17 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
return -1;
}
- conn->rend_data = tor_malloc_zero(sizeof(rend_data_t));
- strlcpy(conn->rend_data->onion_address, socks->address,
- sizeof(conn->rend_data->onion_address));
+ ENTRY_TO_EDGE_CONN(conn)->rend_data = rend_data =
+ tor_malloc_zero(sizeof(rend_data_t));
+ strlcpy(rend_data->onion_address, socks->address,
+ sizeof(rend_data->onion_address));
log_info(LD_REND,"Got a hidden service request for ID '%s'",
- safe_str_client(conn->rend_data->onion_address));
+ safe_str_client(rend_data->onion_address));
/* see if we already have it cached */
- r = rend_cache_lookup_entry(conn->rend_data->onion_address, -1, &entry);
+ r = rend_cache_lookup_entry(rend_data->onion_address, -1, &entry);
if (r<0) {
log_warn(LD_BUG,"Invalid service name '%s'",
- safe_str_client(conn->rend_data->onion_address));
+ safe_str_client(rend_data->onion_address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
}
@@ -1912,24 +2030,24 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
/* Look up if we have client authorization for it. */
client_auth = rend_client_lookup_service_authorization(
- conn->rend_data->onion_address);
+ rend_data->onion_address);
if (client_auth) {
log_info(LD_REND, "Using previously configured client authorization "
"for hidden service request.");
- memcpy(conn->rend_data->descriptor_cookie,
+ memcpy(rend_data->descriptor_cookie,
client_auth->descriptor_cookie, REND_DESC_COOKIE_LEN);
- conn->rend_data->auth_type = client_auth->auth_type;
+ rend_data->auth_type = client_auth->auth_type;
}
if (r==0) {
- conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
+ base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
- safe_str_client(conn->rend_data->onion_address));
- rend_client_refetch_v2_renddesc(conn->rend_data);
+ safe_str_client(rend_data->onion_address));
+ rend_client_refetch_v2_renddesc(rend_data);
} else { /* r > 0 */
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here. Great.");
if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!conn->_base.marked_for_close)
+ if (!base_conn->marked_for_close)
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
return -1;
}
@@ -1951,10 +2069,10 @@ get_pf_socket(void)
#ifdef OPENBSD
/* only works on OpenBSD */
- pf = open("/dev/pf", O_RDONLY);
+ pf = tor_open_cloexec("/dev/pf", O_RDONLY, 0);
#else
/* works on NetBSD and FreeBSD */
- pf = open("/dev/pf", O_RDWR);
+ pf = tor_open_cloexec("/dev/pf", O_RDWR, 0);
#endif
if (pf < 0) {
@@ -1975,7 +2093,7 @@ get_pf_socket(void)
* else return 0.
*/
static int
-connection_ap_get_original_destination(edge_connection_t *conn,
+connection_ap_get_original_destination(entry_connection_t *conn,
socks_request_t *req)
{
#ifdef TRANS_NETFILTER
@@ -1984,9 +2102,9 @@ connection_ap_get_original_destination(edge_connection_t *conn,
socklen_t orig_dst_len = sizeof(orig_dst);
tor_addr_t addr;
- if (getsockopt(conn->_base.s, SOL_IP, SO_ORIGINAL_DST,
+ if (getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IP, SO_ORIGINAL_DST,
(struct sockaddr*)&orig_dst, &orig_dst_len) < 0) {
- int e = tor_socket_errno(conn->_base.s);
+ int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s);
log_warn(LD_NET, "getsockopt() failed: %s", tor_socket_strerror(e));
return -1;
}
@@ -2003,9 +2121,9 @@ connection_ap_get_original_destination(edge_connection_t *conn,
tor_addr_t addr;
int pf = -1;
- if (getsockname(conn->_base.s, (struct sockaddr*)&proxy_addr,
+ if (getsockname(ENTRY_TO_CONN(conn)->s, (struct sockaddr*)&proxy_addr,
&proxy_addr_len) < 0) {
- int e = tor_socket_errno(conn->_base.s);
+ int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s);
log_warn(LD_NET, "getsockname() to determine transocks destination "
"failed: %s", tor_socket_strerror(e));
return -1;
@@ -2017,16 +2135,16 @@ connection_ap_get_original_destination(edge_connection_t *conn,
if (proxy_sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)proxy_sa;
pnl.af = AF_INET;
- pnl.saddr.v4.s_addr = tor_addr_to_ipv4n(&conn->_base.addr);
- pnl.sport = htons(conn->_base.port);
+ pnl.saddr.v4.s_addr = tor_addr_to_ipv4n(&ENTRY_TO_CONN(conn)->addr);
+ pnl.sport = htons(ENTRY_TO_CONN(conn)->port);
pnl.daddr.v4.s_addr = sin->sin_addr.s_addr;
pnl.dport = sin->sin_port;
} else if (proxy_sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)proxy_sa;
pnl.af = AF_INET6;
- memcpy(&pnl.saddr.v6, tor_addr_to_in6(&conn->_base.addr),
+ memcpy(&pnl.saddr.v6, tor_addr_to_in6(&ENTRY_TO_CONN(conn)->addr),
sizeof(struct in6_addr));
- pnl.sport = htons(conn->_base.port);
+ pnl.sport = htons(ENTRY_TO_CONN(conn)->port);
memcpy(&pnl.daddr.v6, &sin6->sin6_addr, sizeof(struct in6_addr));
pnl.dport = sin6->sin6_port;
} else {
@@ -2077,38 +2195,43 @@ connection_ap_get_original_destination(edge_connection_t *conn,
* else return 0.
*/
static int
-connection_ap_handshake_process_socks(edge_connection_t *conn)
+connection_ap_handshake_process_socks(entry_connection_t *conn)
{
socks_request_t *socks;
int sockshere;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
+ int had_reply = 0;
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
tor_assert(conn);
- tor_assert(conn->_base.type == CONN_TYPE_AP);
- tor_assert(conn->_base.state == AP_CONN_STATE_SOCKS_WAIT);
+ tor_assert(base_conn->type == CONN_TYPE_AP);
+ tor_assert(base_conn->state == AP_CONN_STATE_SOCKS_WAIT);
tor_assert(conn->socks_request);
socks = conn->socks_request;
log_debug(LD_APP,"entered.");
- sockshere = fetch_from_buf_socks(conn->_base.inbuf, socks,
- options->TestSocks, options->SafeSocks);
+ IF_HAS_BUFFEREVENT(base_conn, {
+ struct evbuffer *input = bufferevent_get_input(base_conn->bufev);
+ sockshere = fetch_from_evbuffer_socks(input, socks,
+ options->TestSocks, options->SafeSocks);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ sockshere = fetch_from_buf_socks(base_conn->inbuf, socks,
+ options->TestSocks, options->SafeSocks);
+ };
+
+ if (socks->replylen) {
+ had_reply = 1;
+ connection_write_to_buf((const char*)socks->reply, socks->replylen,
+ base_conn);
+ socks->replylen = 0;
+ }
+
if (sockshere == 0) {
- if (socks->replylen) {
- connection_write_to_buf(socks->reply, socks->replylen, TO_CONN(conn));
- /* zero it out so we can do another round of negotiation */
- socks->replylen = 0;
- } else {
- log_debug(LD_APP,"socks handshake not all here yet.");
- }
+ log_debug(LD_APP,"socks handshake not all here yet.");
return 0;
} else if (sockshere == -1) {
- if (socks->replylen) { /* we should send reply back */
- log_debug(LD_APP,"reply is already set for us. Using it.");
- connection_ap_handshake_socks_reply(conn, socks->reply, socks->replylen,
- END_STREAM_REASON_SOCKSPROTOCOL);
-
- } else {
+ if (!had_reply) {
log_warn(LD_APP,"Fetching socks handshake failed. Closing.");
connection_ap_handshake_socks_reply(conn, NULL, 0,
END_STREAM_REASON_SOCKSPROTOCOL);
@@ -2135,12 +2258,11 @@ connection_ap_handshake_process_socks(edge_connection_t *conn)
* for close), else return 0.
*/
int
-connection_ap_process_transparent(edge_connection_t *conn)
+connection_ap_process_transparent(entry_connection_t *conn)
{
socks_request_t *socks;
tor_assert(conn);
- tor_assert(conn->_base.type == CONN_TYPE_AP);
tor_assert(conn->socks_request);
socks = conn->socks_request;
@@ -2176,7 +2298,7 @@ connection_ap_process_transparent(edge_connection_t *conn)
* for close), else return 0.
*/
static int
-connection_ap_process_natd(edge_connection_t *conn)
+connection_ap_process_natd(entry_connection_t *conn)
{
char tmp_buf[36], *tbuf, *daddr;
size_t tlen = 30;
@@ -2184,8 +2306,7 @@ connection_ap_process_natd(edge_connection_t *conn)
socks_request_t *socks;
tor_assert(conn);
- tor_assert(conn->_base.type == CONN_TYPE_AP);
- tor_assert(conn->_base.state == AP_CONN_STATE_NATD_WAIT);
+ tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_NATD_WAIT);
tor_assert(conn->socks_request);
socks = conn->socks_request;
@@ -2193,7 +2314,7 @@ connection_ap_process_natd(edge_connection_t *conn)
/* look for LF-terminated "[DEST ip_addr port]"
* where ip_addr is a dotted-quad and port is in string form */
- err = fetch_from_buf_line(conn->_base.inbuf, tmp_buf, &tlen);
+ err = connection_fetch_from_buf_line(ENTRY_TO_CONN(conn), tmp_buf, &tlen);
if (err == 0)
return 0;
if (err < 0) {
@@ -2237,7 +2358,7 @@ connection_ap_process_natd(edge_connection_t *conn)
control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT;
return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
}
@@ -2267,28 +2388,46 @@ get_unique_stream_id_by_circ(origin_circuit_t *circ)
return test_stream_id;
}
+/** Return true iff <b>conn</b> is linked to a circuit and configured to use
+ * an exit that supports optimistic data. */
+static int
+connection_ap_supports_optimistic_data(const entry_connection_t *conn)
+{
+ const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
+ /* We can only send optimistic data if we're connected to an open
+ general circuit. */
+ if (edge_conn->on_circuit == NULL ||
+ edge_conn->on_circuit->state != CIRCUIT_STATE_OPEN ||
+ edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+ return 0;
+
+ return conn->may_use_optimistic_data;
+}
+
/** Write a relay begin cell, using destaddr and destport from ap_conn's
* socks_request field, and send it down circ.
*
* If ap_conn is broken, mark it for close and return -1. Else return 0.
*/
int
-connection_ap_handshake_send_begin(edge_connection_t *ap_conn)
+connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
{
char payload[CELL_PAYLOAD_SIZE];
int payload_len;
int begin_type;
origin_circuit_t *circ;
- tor_assert(ap_conn->on_circuit);
- circ = TO_ORIGIN_CIRCUIT(ap_conn->on_circuit);
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
+ connection_t *base_conn = TO_CONN(edge_conn);
+ tor_assert(edge_conn->on_circuit);
+ circ = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit);
- tor_assert(ap_conn->_base.type == CONN_TYPE_AP);
- tor_assert(ap_conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(base_conn->type == CONN_TYPE_AP);
+ tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
tor_assert(ap_conn->socks_request);
tor_assert(SOCKS_COMMAND_IS_CONNECT(ap_conn->socks_request->command));
- ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
- if (ap_conn->stream_id==0) {
+ edge_conn->stream_id = get_unique_stream_id_by_circ(circ);
+ if (edge_conn->stream_id==0) {
/* XXXX023 Instead of closing this stream, we should make it get
* retried on another circuit. */
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
@@ -2306,8 +2445,10 @@ connection_ap_handshake_send_begin(edge_connection_t *ap_conn)
ap_conn->socks_request->port);
payload_len = (int)strlen(payload)+1;
- log_debug(LD_APP,
- "Sending relay cell to begin stream %d.", ap_conn->stream_id);
+ log_info(LD_APP,
+ "Sending relay cell %d to begin stream %d.",
+ (int)ap_conn->use_begindir,
+ edge_conn->stream_id);
begin_type = ap_conn->use_begindir ?
RELAY_COMMAND_BEGIN_DIR : RELAY_COMMAND_BEGIN;
@@ -2315,17 +2456,31 @@ connection_ap_handshake_send_begin(edge_connection_t *ap_conn)
tor_assert(circ->build_state->onehop_tunnel == 0);
}
- if (connection_edge_send_command(ap_conn, begin_type,
+ if (connection_edge_send_command(edge_conn, begin_type,
begin_type == RELAY_COMMAND_BEGIN ? payload : NULL,
begin_type == RELAY_COMMAND_BEGIN ? payload_len : 0) < 0)
return -1; /* circuit is closed, don't continue */
- ap_conn->package_window = STREAMWINDOW_START;
- ap_conn->deliver_window = STREAMWINDOW_START;
- ap_conn->_base.state = AP_CONN_STATE_CONNECT_WAIT;
+ edge_conn->package_window = STREAMWINDOW_START;
+ edge_conn->deliver_window = STREAMWINDOW_START;
+ base_conn->state = AP_CONN_STATE_CONNECT_WAIT;
log_info(LD_APP,"Address/port sent, ap socket %d, n_circ_id %d",
- ap_conn->_base.s, circ->_base.n_circ_id);
+ base_conn->s, circ->_base.n_circ_id);
control_event_stream_status(ap_conn, STREAM_EVENT_SENT_CONNECT, 0);
+
+ /* If there's queued-up data, send it now */
+ if ((connection_get_inbuf_len(base_conn) ||
+ ap_conn->sending_optimistic_data) &&
+ connection_ap_supports_optimistic_data(ap_conn)) {
+ log_info(LD_APP, "Sending up to %ld + %ld bytes of queued-up data",
+ (long)connection_get_inbuf_len(base_conn),
+ ap_conn->sending_optimistic_data ?
+ (long)generic_buffer_len(ap_conn->sending_optimistic_data) : 0);
+ if (connection_edge_package_raw_inbuf(edge_conn, 1, NULL) < 0) {
+ connection_mark_for_close(base_conn);
+ }
+ }
+
return 0;
}
@@ -2335,25 +2490,27 @@ connection_ap_handshake_send_begin(edge_connection_t *ap_conn)
* If ap_conn is broken, mark it for close and return -1. Else return 0.
*/
int
-connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
+connection_ap_handshake_send_resolve(entry_connection_t *ap_conn)
{
int payload_len, command;
const char *string_addr;
char inaddr_buf[REVERSE_LOOKUP_NAME_BUF_LEN];
origin_circuit_t *circ;
- tor_assert(ap_conn->on_circuit);
- circ = TO_ORIGIN_CIRCUIT(ap_conn->on_circuit);
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
+ connection_t *base_conn = TO_CONN(edge_conn);
+ tor_assert(edge_conn->on_circuit);
+ circ = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit);
- tor_assert(ap_conn->_base.type == CONN_TYPE_AP);
- tor_assert(ap_conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(base_conn->type == CONN_TYPE_AP);
+ tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
tor_assert(ap_conn->socks_request);
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL);
command = ap_conn->socks_request->command;
tor_assert(SOCKS_COMMAND_IS_RESOLVE(command));
- ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
- if (ap_conn->stream_id==0) {
+ edge_conn->stream_id = get_unique_stream_id_by_circ(circ);
+ if (edge_conn->stream_id==0) {
/* XXXX023 Instead of closing this stream, we should make it get
* retried on another circuit. */
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
@@ -2376,7 +2533,7 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
/* We're doing a reverse lookup. The input could be an IP address, or
* could be an .in-addr.arpa or .ip6.arpa address */
- r = tor_addr_parse_reverse_lookup_name(&addr, a, AF_INET, 1);
+ r = tor_addr_parse_PTR_name(&addr, a, AF_INET, 1);
if (r <= 0) {
log_warn(LD_APP, "Rejecting ill-formed reverse lookup of %s",
safe_str_client(a));
@@ -2384,7 +2541,7 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
return -1;
}
- r = tor_addr_to_reverse_lookup_name(inaddr_buf, sizeof(inaddr_buf), &addr);
+ r = tor_addr_to_PTR_name(inaddr_buf, sizeof(inaddr_buf), &addr);
if (r < 0) {
log_warn(LD_BUG, "Couldn't generate reverse lookup hostname of %s",
safe_str_client(a));
@@ -2398,18 +2555,18 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
}
log_debug(LD_APP,
- "Sending relay cell to begin stream %d.", ap_conn->stream_id);
+ "Sending relay cell to begin stream %d.", edge_conn->stream_id);
- if (connection_edge_send_command(ap_conn,
+ if (connection_edge_send_command(edge_conn,
RELAY_COMMAND_RESOLVE,
string_addr, payload_len) < 0)
return -1; /* circuit is closed, don't continue */
- tor_free(ap_conn->_base.address); /* Maybe already set by dnsserv. */
- ap_conn->_base.address = tor_strdup("(Tor_internal)");
- ap_conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
+ tor_free(base_conn->address); /* Maybe already set by dnsserv. */
+ base_conn->address = tor_strdup("(Tor_internal)");
+ base_conn->state = AP_CONN_STATE_RESOLVE_WAIT;
log_info(LD_APP,"Address sent for resolve, ap socket %d, n_circ_id %d",
- ap_conn->_base.s, circ->_base.n_circ_id);
+ base_conn->s, circ->_base.n_circ_id);
control_event_stream_status(ap_conn, STREAM_EVENT_NEW, 0);
control_event_stream_status(ap_conn, STREAM_EVENT_SENT_RESOLVE, 0);
return 0;
@@ -2420,19 +2577,25 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
* and call connection_ap_handshake_attach_circuit(conn) on it.
*
* Return the other end of the linked connection pair, or -1 if error.
+ * DOCDOC partner.
*/
-edge_connection_t *
-connection_ap_make_link(char *address, uint16_t port,
- const char *digest, int use_begindir, int want_onehop)
+entry_connection_t *
+connection_ap_make_link(connection_t *partner,
+ char *address, uint16_t port,
+ const char *digest,
+ int session_group, int isolation_flags,
+ int use_begindir, int want_onehop)
{
- edge_connection_t *conn;
+ entry_connection_t *conn;
+ connection_t *base_conn;
log_info(LD_APP,"Making internal %s tunnel to %s:%d ...",
want_onehop ? "direct" : "anonymized",
safe_str_client(address), port);
- conn = edge_connection_new(CONN_TYPE_AP, AF_INET);
- conn->_base.linked = 1; /* so that we can add it safely below. */
+ conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
+ base_conn = ENTRY_TO_CONN(conn);
+ base_conn->linked = 1; /* so that we can add it safely below. */
/* populate conn->socks_request */
@@ -2453,22 +2616,30 @@ connection_ap_make_link(char *address, uint16_t port,
digest, DIGEST_LEN);
}
- conn->_base.address = tor_strdup("(Tor_internal)");
- tor_addr_make_unspec(&conn->_base.addr);
- conn->_base.port = 0;
+ /* Populate isolation fields. */
+ conn->socks_request->listener_type = CONN_TYPE_DIR_LISTENER;
+ conn->original_dest_address = tor_strdup(address);
+ conn->session_group = session_group;
+ conn->isolation_flags = isolation_flags;
- if (connection_add(TO_CONN(conn)) < 0) { /* no space, forget it */
- connection_free(TO_CONN(conn));
+ base_conn->address = tor_strdup("(Tor_internal)");
+ tor_addr_make_unspec(&base_conn->addr);
+ base_conn->port = 0;
+
+ connection_link_connections(partner, base_conn);
+
+ if (connection_add(base_conn) < 0) { /* no space, forget it */
+ connection_free(base_conn);
return NULL;
}
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
/* attaching to a dirty circuit is fine */
if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!conn->_base.marked_for_close)
+ if (!base_conn->marked_for_close)
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
return NULL;
}
@@ -2481,7 +2652,7 @@ connection_ap_make_link(char *address, uint16_t port,
* or resolve error. Takes the same arguments as does
* connection_ap_handshake_socks_resolved(). */
static void
-tell_controller_about_resolved_result(edge_connection_t *conn,
+tell_controller_about_resolved_result(entry_connection_t *conn,
int answer_type,
size_t answer_len,
const char *answer,
@@ -2493,13 +2664,11 @@ tell_controller_about_resolved_result(edge_connection_t *conn,
answer_type == RESOLVED_TYPE_HOSTNAME)) {
return; /* we already told the controller. */
} else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len >= 4) {
- struct in_addr in;
- char buf[INET_NTOA_BUF_LEN];
- in.s_addr = get_uint32(answer);
- tor_inet_ntoa(&in, buf, sizeof(buf));
+ char *cp = tor_dup_ip(get_uint32(answer));
control_event_address_mapped(conn->socks_request->address,
- buf, expires, NULL);
- } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len <256) {
+ cp, expires, NULL);
+ tor_free(cp);
+ } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
char *cp = tor_strndup(answer, answer_len);
control_event_address_mapped(conn->socks_request->address,
cp, expires, NULL);
@@ -2522,7 +2691,7 @@ tell_controller_about_resolved_result(edge_connection_t *conn,
/* XXXX023 the use of the ttl and expires fields is nutty. Let's make this
* interface and those that use it less ugly. */
void
-connection_ap_handshake_socks_resolved(edge_connection_t *conn,
+connection_ap_handshake_socks_resolved(entry_connection_t *conn,
int answer_type,
size_t answer_len,
const uint8_t *answer,
@@ -2547,7 +2716,7 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
}
}
- if (conn->is_dns_request) {
+ if (ENTRY_TO_EDGE_CONN(conn)->is_dns_request) {
if (conn->dns_server_request) {
/* We had a request on our DNS port: answer it. */
dnsserv_resolved(conn, answer_type, answer_len, (char*)answer, ttl);
@@ -2627,7 +2796,7 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
* be 0 or REASON_DONE. Send endreason to the controller, if appropriate.
*/
void
-connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
+connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
size_t replylen, int endreason)
{
char buf[256];
@@ -2646,7 +2815,7 @@ connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
return;
}
if (replylen) { /* we already have a reply in mind */
- connection_write_to_buf(reply, replylen, TO_CONN(conn));
+ connection_write_to_buf(reply, replylen, ENTRY_TO_CONN(conn));
conn->socks_request->has_finished = 1;
return;
}
@@ -2654,7 +2823,7 @@ connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
memset(buf,0,SOCKS4_NETWORK_LEN);
buf[1] = (status==SOCKS5_SUCCEEDED ? SOCKS4_GRANTED : SOCKS4_REJECT);
/* leave version, destport, destip zero */
- connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, TO_CONN(conn));
+ connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, ENTRY_TO_CONN(conn));
} else if (conn->socks_request->socks_version == 5) {
buf[0] = 5; /* version 5 */
buf[1] = (char)status;
@@ -2662,7 +2831,7 @@ connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
buf[3] = 1; /* ipv4 addr */
memset(buf+4,0,6); /* Set external addr/port to 0.
The spec doesn't seem to say what to do here. -RD */
- connection_write_to_buf(buf,10,TO_CONN(conn));
+ connection_write_to_buf(buf,10,ENTRY_TO_CONN(conn));
}
/* If socks_version isn't 4 or 5, don't send anything.
* This can happen in the case of AP bridges. */
@@ -2696,7 +2865,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
char *address=NULL;
uint16_t port;
or_circuit_t *or_circ = NULL;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
assert_circuit_ok(circ);
if (!CIRCUIT_IS_ORIGIN(circ))
@@ -2728,9 +2897,9 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
END_STREAM_REASON_TORPROTOCOL, NULL);
return 0;
}
- if (parse_addr_port(LOG_PROTOCOL_WARN,
- (char*)(cell->payload+RELAY_HEADER_SIZE),
- &address,NULL,&port)<0) {
+ if (tor_addr_port_split(LOG_PROTOCOL_WARN,
+ (char*)(cell->payload+RELAY_HEADER_SIZE),
+ &address,&port)<0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Unable to parse addr:port in relay begin cell. Closing.");
relay_send_end_cell_from_edge(rh.stream_id, circ,
@@ -2979,12 +3148,13 @@ connection_exit_connect(edge_connection_t *edge_conn)
}
conn->state = EXIT_CONN_STATE_OPEN;
- if (connection_wants_to_flush(conn)) {
+ if (connection_get_outbuf_len(conn)) {
/* in case there are any queued data cells */
log_warn(LD_BUG,"newly connected conn had data waiting!");
// connection_start_writing(conn);
}
- connection_watch_events(conn, READ_EVENT);
+ IF_HAS_NO_BUFFEREVENT(conn)
+ connection_watch_events(conn, READ_EVENT);
/* also, deliver a 'connected' cell back through the circuit. */
if (connection_edge_is_rendezvous_stream(edge_conn)) {
@@ -3094,12 +3264,11 @@ connection_edge_is_rendezvous_stream(edge_connection_t *conn)
* resolved.)
*/
int
-connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
+connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
tor_assert(conn);
- tor_assert(conn->_base.type == CONN_TYPE_AP);
tor_assert(conn->socks_request);
tor_assert(exit);
@@ -3107,10 +3276,10 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
* make sure the exit node of the existing circuit matches exactly.
*/
if (conn->chosen_exit_name) {
- routerinfo_t *chosen_exit =
- router_get_by_nickname(conn->chosen_exit_name, 1);
- if (!chosen_exit || tor_memneq(chosen_exit->cache_info.identity_digest,
- exit->cache_info.identity_digest, DIGEST_LEN)) {
+ const node_t *chosen_exit =
+ node_get_by_nickname(conn->chosen_exit_name, 1);
+ if (!chosen_exit || tor_memneq(chosen_exit->identity,
+ exit->identity, DIGEST_LEN)) {
/* doesn't match */
// log_debug(LD_APP,"Requested node '%s', considering node '%s'. No.",
// conn->chosen_exit_name, exit->nickname);
@@ -3121,12 +3290,13 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
if (conn->socks_request->command == SOCKS_COMMAND_CONNECT &&
!conn->use_begindir) {
struct in_addr in;
- uint32_t addr = 0;
+ tor_addr_t addr, *addrp = NULL;
addr_policy_result_t r;
- if (tor_inet_aton(conn->socks_request->address, &in))
- addr = ntohl(in.s_addr);
- r = compare_addr_to_addr_policy(addr, conn->socks_request->port,
- exit->exit_policy);
+ if (tor_inet_aton(conn->socks_request->address, &in)) {
+ tor_addr_from_in(&addr, &in);
+ addrp = &addr;
+ }
+ r = compare_tor_addr_to_node_policy(addrp, conn->socks_request->port,exit);
if (r == ADDR_POLICY_REJECTED)
return 0; /* We know the address, and the exit policy rejects it. */
if (r == ADDR_POLICY_PROBABLY_REJECTED && !conn->chosen_exit_name)
@@ -3134,17 +3304,11 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
* addresses with this port. Since the user didn't ask for
* this node, err on the side of caution. */
} else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
- /* Can't support reverse lookups without eventdns. */
- if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR &&
- exit->has_old_dnsworkers)
- return 0;
-
/* Don't send DNS requests to non-exit servers by default. */
- if (!conn->chosen_exit_name && policy_is_reject_star(exit->exit_policy))
+ if (!conn->chosen_exit_name && node_exit_policy_rejects_all(exit))
return 0;
}
- if (options->_ExcludeExitNodesUnion &&
- routerset_contains_router(options->_ExcludeExitNodesUnion, exit)) {
+ if (routerset_contains_node(options->_ExcludeExitNodesUnion, exit)) {
/* Not a suitable exit. Refuse it. */
return 0;
}
@@ -3199,3 +3363,205 @@ parse_extended_hostname(char *address, int allowdotexit)
return BAD_HOSTNAME;
}
+/** Return true iff the (possibly NULL) <b>alen</b>-byte chunk of memory at
+ * <b>a</b> is equal to the (possibly NULL) <b>blen</b>-byte chunk of memory
+ * at <b>b</b>. */
+static int
+memeq_opt(const char *a, size_t alen, const char *b, size_t blen)
+{
+ if (a == NULL) {
+ return (b == NULL);
+ } else if (b == NULL) {
+ return 0;
+ } else if (alen != blen) {
+ return 0;
+ } else {
+ return tor_memeq(a, b, alen);
+ }
+}
+
+/**
+ * Return true iff none of the isolation flags and fields in <b>conn</b>
+ * should prevent it from being attached to <b>circ</b>.
+ */
+int
+connection_edge_compatible_with_circuit(const entry_connection_t *conn,
+ const origin_circuit_t *circ)
+{
+ const uint8_t iso = conn->isolation_flags;
+ const socks_request_t *sr = conn->socks_request;
+
+ /* If circ has never been used for an isolated connection, we can
+ * totally use it for this one. */
+ if (!circ->isolation_values_set)
+ return 1;
+
+ /* If circ has been used for connections having more than one value
+ * for some field f, it will have the corresponding bit set in
+ * isolation_flags_mixed. If isolation_flags_mixed has any bits
+ * in common with iso, then conn must be isolated from at least
+ * one stream that has been attached to circ. */
+ if ((iso & circ->isolation_flags_mixed) != 0) {
+ /* For at least one field where conn is isolated, the circuit
+ * already has mixed streams. */
+ return 0;
+ }
+
+ if (! conn->original_dest_address) {
+ log_warn(LD_BUG, "Reached connection_edge_compatible_with_circuit without "
+ "having set conn->original_dest_address");
+ ((entry_connection_t*)conn)->original_dest_address =
+ tor_strdup(conn->socks_request->address);
+ }
+
+ if ((iso & ISO_STREAM) &&
+ (circ->associated_isolated_stream_global_id !=
+ ENTRY_TO_CONN(conn)->global_identifier))
+ return 0;
+
+ if ((iso & ISO_DESTPORT) && conn->socks_request->port != circ->dest_port)
+ return 0;
+ if ((iso & ISO_DESTADDR) &&
+ strcasecmp(conn->original_dest_address, circ->dest_address))
+ return 0;
+ if ((iso & ISO_SOCKSAUTH) &&
+ (! memeq_opt(sr->username, sr->usernamelen,
+ circ->socks_username, circ->socks_username_len) ||
+ ! memeq_opt(sr->password, sr->passwordlen,
+ circ->socks_password, circ->socks_password_len)))
+ return 0;
+ if ((iso & ISO_CLIENTPROTO) &&
+ (conn->socks_request->listener_type != circ->client_proto_type ||
+ conn->socks_request->socks_version != circ->client_proto_socksver))
+ return 0;
+ if ((iso & ISO_CLIENTADDR) &&
+ !tor_addr_eq(&ENTRY_TO_CONN(conn)->addr, &circ->client_addr))
+ return 0;
+ if ((iso & ISO_SESSIONGRP) && conn->session_group != circ->session_group)
+ return 0;
+ if ((iso & ISO_NYM_EPOCH) && conn->nym_epoch != circ->nym_epoch)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * If <b>dry_run</b> is false, update <b>circ</b>'s isolation flags and fields
+ * to reflect having had <b>conn</b> attached to it, and return 0. Otherwise,
+ * if <b>dry_run</b> is true, then make no changes to <b>circ</b>, and return
+ * a bitfield of isolation flags that we would have to set in
+ * isolation_flags_mixed to add <b>conn</b> to <b>circ</b>, or -1 if
+ * <b>circ</b> has had no streams attached to it.
+ */
+int
+connection_edge_update_circuit_isolation(const entry_connection_t *conn,
+ origin_circuit_t *circ,
+ int dry_run)
+{
+ const socks_request_t *sr = conn->socks_request;
+ if (! conn->original_dest_address) {
+ log_warn(LD_BUG, "Reached connection_update_circuit_isolation without "
+ "having set conn->original_dest_address");
+ ((entry_connection_t*)conn)->original_dest_address =
+ tor_strdup(conn->socks_request->address);
+ }
+
+ if (!circ->isolation_values_set) {
+ if (dry_run)
+ return -1;
+ circ->associated_isolated_stream_global_id =
+ ENTRY_TO_CONN(conn)->global_identifier;
+ circ->dest_port = conn->socks_request->port;
+ circ->dest_address = tor_strdup(conn->original_dest_address);
+ circ->client_proto_type = conn->socks_request->listener_type;
+ circ->client_proto_socksver = conn->socks_request->socks_version;
+ tor_addr_copy(&circ->client_addr, &ENTRY_TO_CONN(conn)->addr);
+ circ->session_group = conn->session_group;
+ circ->nym_epoch = conn->nym_epoch;
+ circ->socks_username = sr->username ?
+ tor_memdup(sr->username, sr->usernamelen) : NULL;
+ circ->socks_password = sr->password ?
+ tor_memdup(sr->password, sr->passwordlen) : NULL;
+ circ->socks_username_len = sr->usernamelen;
+ circ->socks_password_len = sr->passwordlen;
+
+ circ->isolation_values_set = 1;
+ return 0;
+ } else {
+ uint8_t mixed = 0;
+ if (conn->socks_request->port != circ->dest_port)
+ mixed |= ISO_DESTPORT;
+ if (strcasecmp(conn->original_dest_address, circ->dest_address))
+ mixed |= ISO_DESTADDR;
+ if (!memeq_opt(sr->username, sr->usernamelen,
+ circ->socks_username, circ->socks_username_len) ||
+ !memeq_opt(sr->password, sr->passwordlen,
+ circ->socks_password, circ->socks_password_len))
+ mixed |= ISO_SOCKSAUTH;
+ if ((conn->socks_request->listener_type != circ->client_proto_type ||
+ conn->socks_request->socks_version != circ->client_proto_socksver))
+ mixed |= ISO_CLIENTPROTO;
+ if (!tor_addr_eq(&ENTRY_TO_CONN(conn)->addr, &circ->client_addr))
+ mixed |= ISO_CLIENTADDR;
+ if (conn->session_group != circ->session_group)
+ mixed |= ISO_SESSIONGRP;
+ if (conn->nym_epoch != circ->nym_epoch)
+ mixed |= ISO_NYM_EPOCH;
+
+ if (dry_run)
+ return mixed;
+
+ if ((mixed & conn->isolation_flags) != 0) {
+ log_warn(LD_BUG, "Updating a circuit with seemingly incompatible "
+ "isolation flags.");
+ }
+ circ->isolation_flags_mixed |= mixed;
+ return 0;
+ }
+}
+
+/**
+ * Clear the isolation settings on <b>circ</b>.
+ *
+ * This only works on an open circuit that has never had a stream attached to
+ * it, and whose isolation settings are hypothetical. (We set hypothetical
+ * isolation settings on circuits as we're launching them, so that we
+ * know whether they can handle more streams or whether we need to launch
+ * even more circuits. Once the circuit is open, if it turns out that
+ * we no longer have any streams to attach to it, we clear the isolation flags
+ * and data so that other streams can have a chance.)
+ */
+void
+circuit_clear_isolation(origin_circuit_t *circ)
+{
+ if (circ->isolation_any_streams_attached) {
+ log_warn(LD_BUG, "Tried to clear the isolation status of a dirty circuit");
+ return;
+ }
+ if (TO_CIRCUIT(circ)->state != CIRCUIT_STATE_OPEN) {
+ log_warn(LD_BUG, "Tried to clear the isolation status of a non-open "
+ "circuit");
+ return;
+ }
+
+ circ->isolation_values_set = 0;
+ circ->isolation_flags_mixed = 0;
+ circ->associated_isolated_stream_global_id = 0;
+ circ->client_proto_type = 0;
+ circ->client_proto_socksver = 0;
+ circ->dest_port = 0;
+ tor_addr_make_unspec(&circ->client_addr);
+ tor_free(circ->dest_address);
+ circ->session_group = -1;
+ circ->nym_epoch = 0;
+ if (circ->socks_username) {
+ memset(circ->socks_username, 0x11, circ->socks_username_len);
+ tor_free(circ->socks_username);
+ }
+ if (circ->socks_password) {
+ memset(circ->socks_password, 0x05, circ->socks_password_len);
+ tor_free(circ->socks_password);
+ }
+ circ->socks_username_len = circ->socks_password_len = 0;
+}
+
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 8ba2fafd0..830667e60 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -15,7 +15,7 @@
#define connection_mark_unattached_ap(conn, endreason) \
_connection_mark_unattached_ap((conn), (endreason), __LINE__, _SHORT_FILE_)
-void _connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
+void _connection_mark_unattached_ap(entry_connection_t *conn, int endreason,
int line, const char *file);
int connection_edge_reached_eof(edge_connection_t *conn);
int connection_edge_process_inbuf(edge_connection_t *conn,
@@ -27,16 +27,22 @@ int connection_edge_flushed_some(edge_connection_t *conn);
int connection_edge_finished_flushing(edge_connection_t *conn);
int connection_edge_finished_connecting(edge_connection_t *conn);
-int connection_ap_handshake_send_begin(edge_connection_t *ap_conn);
-int connection_ap_handshake_send_resolve(edge_connection_t *ap_conn);
+void connection_ap_about_to_close(entry_connection_t *edge_conn);
+void connection_exit_about_to_close(edge_connection_t *edge_conn);
-edge_connection_t *connection_ap_make_link(char *address, uint16_t port,
+int connection_ap_handshake_send_begin(entry_connection_t *ap_conn);
+int connection_ap_handshake_send_resolve(entry_connection_t *ap_conn);
+
+entry_connection_t *connection_ap_make_link(connection_t *partner,
+ char *address, uint16_t port,
const char *digest,
+ int session_group,
+ int isolation_flags,
int use_begindir, int want_onehop);
-void connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
+void connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
size_t replylen,
int endreason);
-void connection_ap_handshake_socks_resolved(edge_connection_t *conn,
+void connection_ap_handshake_socks_resolved(entry_connection_t *conn,
int answer_type,
size_t answer_len,
const uint8_t *answer,
@@ -47,22 +53,23 @@ int connection_exit_begin_conn(cell_t *cell, circuit_t *circ);
int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ);
void connection_exit_connect(edge_connection_t *conn);
int connection_edge_is_rendezvous_stream(edge_connection_t *conn);
-int connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit);
+int connection_ap_can_use_exit(const entry_connection_t *conn,
+ const node_t *exit);
void connection_ap_expire_beginning(void);
void connection_ap_attach_pending(void);
void connection_ap_fail_onehop(const char *failed_digest,
cpath_build_state_t *build_state);
void circuit_discard_optional_exit_enclaves(extend_info_t *info);
-int connection_ap_detach_retriable(edge_connection_t *conn,
+int connection_ap_detach_retriable(entry_connection_t *conn,
origin_circuit_t *circ,
int reason);
-int connection_ap_process_transparent(edge_connection_t *conn);
+int connection_ap_process_transparent(entry_connection_t *conn);
int address_is_invalid_destination(const char *address, int client);
void addressmap_init(void);
-void addressmap_clear_excluded_trackexithosts(or_options_t *options);
-void addressmap_clear_invalid_automaps(or_options_t *options);
+void addressmap_clear_excluded_trackexithosts(const or_options_t *options);
+void addressmap_clear_invalid_automaps(const or_options_t *options);
void addressmap_clean(time_t now);
void addressmap_clear_configured(void);
void addressmap_clear_transient(void);
@@ -81,10 +88,10 @@ void client_dns_set_addressmap(const char *address, uint32_t val,
const char *addressmap_register_virtual_address(int type, char *new_address);
void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
time_t max_expires, int want_expiry);
-int connection_ap_rewrite_and_attach_if_allowed(edge_connection_t *conn,
+int connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath);
-int connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
+int connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath);
@@ -98,5 +105,12 @@ hostname_type_t parse_extended_hostname(char *address, int allowdotexit);
int get_pf_socket(void);
#endif
+int connection_edge_compatible_with_circuit(const entry_connection_t *conn,
+ const origin_circuit_t *circ);
+int connection_edge_update_circuit_isolation(const entry_connection_t *conn,
+ origin_circuit_t *circ,
+ int dry_run);
+void circuit_clear_isolation(origin_circuit_t *circ);
+
#endif
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 27a34d3d1..4c0960cec 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -13,6 +13,7 @@
#include "or.h"
#include "buffers.h"
#include "circuitbuild.h"
+#include "circuitlist.h"
#include "command.h"
#include "config.h"
#include "connection.h"
@@ -22,21 +23,32 @@
#include "geoip.h"
#include "main.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "reasons.h"
#include "relay.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
+#ifdef USE_BUFFEREVENTS
+#include <event2/bufferevent_ssl.h>
+#endif
+
static int connection_tls_finish_handshake(or_connection_t *conn);
+static int connection_or_launch_v3_or_handshake(or_connection_t *conn);
static int connection_or_process_cells_from_inbuf(or_connection_t *conn);
-static int connection_or_send_versions(or_connection_t *conn);
-static int connection_init_or_handshake_state(or_connection_t *conn,
- int started_here);
static int connection_or_check_valid_tls_handshake(or_connection_t *conn,
int started_here,
char *digest_rcvd_out);
+static void connection_or_tls_renegotiated_cb(tor_tls_t *tls, void *_conn);
+
+#ifdef USE_BUFFEREVENTS
+static void connection_or_handle_event_cb(struct bufferevent *bufev,
+ short event, void *arg);
+#include <event2/buffer.h>/*XXXX REMOVE */
+#endif
+
/**************************************************************/
/** Map from identity digest of connected OR or desired OR to a connection_t
@@ -136,6 +148,142 @@ connection_or_set_identity_digest(or_connection_t *conn, const char *digest)
#endif
}
+/**************************************************************/
+
+/** Map from a string describing what a non-open OR connection was doing when
+ * failed, to an intptr_t describing the count of connections that failed that
+ * way. Note that the count is stored _as_ the pointer.
+ */
+static strmap_t *broken_connection_counts;
+
+/** If true, do not record information in <b>broken_connection_counts</b>. */
+static int disable_broken_connection_counts = 0;
+
+/** Record that an OR connection failed in <b>state</b>. */
+static void
+note_broken_connection(const char *state)
+{
+ void *ptr;
+ intptr_t val;
+ if (disable_broken_connection_counts)
+ return;
+
+ if (!broken_connection_counts)
+ broken_connection_counts = strmap_new();
+
+ ptr = strmap_get(broken_connection_counts, state);
+ val = (intptr_t)ptr;
+ val++;
+ ptr = (void*)val;
+ strmap_set(broken_connection_counts, state, ptr);
+}
+
+/** Forget all recorded states for failed connections. If
+ * <b>stop_recording</b> is true, don't record any more. */
+void
+clear_broken_connection_map(int stop_recording)
+{
+ if (broken_connection_counts)
+ strmap_free(broken_connection_counts, NULL);
+ broken_connection_counts = NULL;
+ if (stop_recording)
+ disable_broken_connection_counts = 1;
+}
+
+/** Write a detailed description the state of <b>orconn</b> into the
+ * <b>buflen</b>-byte buffer at <b>buf</b>. This description includes not
+ * only the OR-conn level state but also the TLS state. It's useful for
+ * diagnosing broken handshakes. */
+static void
+connection_or_get_state_description(or_connection_t *orconn,
+ char *buf, size_t buflen)
+{
+ connection_t *conn = TO_CONN(orconn);
+ const char *conn_state;
+ char tls_state[256];
+
+ tor_assert(conn->type == CONN_TYPE_OR);
+
+ conn_state = conn_state_to_string(conn->type, conn->state);
+ tor_tls_get_state_description(orconn->tls, tls_state, sizeof(tls_state));
+
+ tor_snprintf(buf, buflen, "%s with SSL state %s", conn_state, tls_state);
+}
+
+/** Record the current state of <b>orconn</b> as the state of a broken
+ * connection. */
+static void
+connection_or_note_state_when_broken(or_connection_t *orconn)
+{
+ char buf[256];
+ if (disable_broken_connection_counts)
+ return;
+ connection_or_get_state_description(orconn, buf, sizeof(buf));
+ log_info(LD_HANDSHAKE,"Connection died in state '%s'", buf);
+ note_broken_connection(buf);
+}
+
+/** Helper type used to sort connection states and find the most frequent. */
+typedef struct broken_state_count_t {
+ intptr_t count;
+ const char *state;
+} broken_state_count_t;
+
+/** Helper function used to sort broken_state_count_t by frequency. */
+static int
+broken_state_count_compare(const void **a_ptr, const void **b_ptr)
+{
+ const broken_state_count_t *a = *a_ptr, *b = *b_ptr;
+ if (b->count < a->count)
+ return -1;
+ else if (b->count == a->count)
+ return 0;
+ else
+ return 1;
+}
+
+/** Upper limit on the number of different states to report for connection
+ * failure. */
+#define MAX_REASONS_TO_REPORT 10
+
+/** Report a list of the top states for failed OR connections at log level
+ * <b>severity</b>, in log domain <b>domain</b>. */
+void
+connection_or_report_broken_states(int severity, int domain)
+{
+ int total = 0;
+ smartlist_t *items;
+
+ if (!broken_connection_counts || disable_broken_connection_counts)
+ return;
+
+ items = smartlist_create();
+ STRMAP_FOREACH(broken_connection_counts, state, void *, countptr) {
+ broken_state_count_t *c = tor_malloc(sizeof(broken_state_count_t));
+ c->count = (intptr_t)countptr;
+ total += (int)c->count;
+ c->state = state;
+ smartlist_add(items, c);
+ } STRMAP_FOREACH_END;
+
+ smartlist_sort(items, broken_state_count_compare);
+
+ log(severity, domain, "%d connections have failed%s", total,
+ smartlist_len(items) > MAX_REASONS_TO_REPORT ? ". Top reasons:" : ":");
+
+ SMARTLIST_FOREACH_BEGIN(items, const broken_state_count_t *, c) {
+ if (c_sl_idx > MAX_REASONS_TO_REPORT)
+ break;
+ log(severity, domain,
+ " %d connections died in state %s", (int)c->count, c->state);
+ } SMARTLIST_FOREACH_END(c);
+
+ SMARTLIST_FOREACH(items, broken_state_count_t *, c, tor_free(c));
+ smartlist_free(items);
+}
+
+/**************************************************************/
+
/** Pack the cell_t host-order structure <b>src</b> into network-order
* in the buffer <b>dest</b>. See tor-spec.txt for details about the
* wire format.
@@ -178,7 +326,8 @@ var_cell_pack_header(const var_cell_t *cell, char *hdr_out)
var_cell_t *
var_cell_new(uint16_t payload_len)
{
- var_cell_t *cell = tor_malloc(sizeof(var_cell_t)+payload_len-1);
+ size_t size = STRUCT_OFFSET(var_cell_t, payload) + payload_len;
+ var_cell_t *cell = tor_malloc(size);
cell->payload_len = payload_len;
cell->command = 0;
cell->circ_id = 0;
@@ -227,8 +376,17 @@ connection_or_process_inbuf(or_connection_t *conn)
}
return ret;
+ case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
+#ifdef USE_BUFFEREVENTS
+ if (tor_tls_server_got_renegotiate(conn->tls))
+ connection_or_tls_renegotiated_cb(conn->tls, conn);
+ if (conn->_base.marked_for_close)
+ return 0;
+ /* fall through. */
+#endif
case OR_CONN_STATE_OPEN:
- case OR_CONN_STATE_OR_HANDSHAKING:
+ case OR_CONN_STATE_OR_HANDSHAKING_V2:
+ case OR_CONN_STATE_OR_HANDSHAKING_V3:
return connection_or_process_cells_from_inbuf(conn);
default:
return 0; /* don't do anything */
@@ -248,7 +406,7 @@ connection_or_process_inbuf(or_connection_t *conn)
int
connection_or_flushed_some(or_connection_t *conn)
{
- size_t datalen = buf_datalen(conn->_base.outbuf);
+ size_t datalen = connection_get_outbuf_len(TO_CONN(conn));
/* If we're under the low water mark, add cells until we're just over the
* high water mark. */
if (datalen < OR_CONN_LOWWATER) {
@@ -280,8 +438,8 @@ connection_or_finished_flushing(or_connection_t *conn)
switch (conn->_base.state) {
case OR_CONN_STATE_PROXY_HANDSHAKING:
case OR_CONN_STATE_OPEN:
- case OR_CONN_STATE_OR_HANDSHAKING:
- connection_stop_writing(TO_CONN(conn));
+ case OR_CONN_STATE_OR_HANDSHAKING_V2:
+ case OR_CONN_STATE_OR_HANDSHAKING_V3:
break;
default:
log_err(LD_BUG,"Called in unexpected state %d.", conn->_base.state);
@@ -296,7 +454,7 @@ connection_or_finished_flushing(or_connection_t *conn)
int
connection_or_finished_connecting(or_connection_t *or_conn)
{
- int proxy_type;
+ const int proxy_type = or_conn->proxy_type;
connection_t *conn;
tor_assert(or_conn);
conn = TO_CONN(or_conn);
@@ -306,15 +464,6 @@ connection_or_finished_connecting(or_connection_t *or_conn)
conn->address,conn->port);
control_event_bootstrap(BOOTSTRAP_STATUS_HANDSHAKE, 0);
- proxy_type = PROXY_NONE;
-
- if (get_options()->HTTPSProxy)
- proxy_type = PROXY_CONNECT;
- else if (get_options()->Socks4Proxy)
- proxy_type = PROXY_SOCKS4;
- else if (get_options()->Socks5Proxy)
- proxy_type = PROXY_SOCKS5;
-
if (proxy_type != PROXY_NONE) {
/* start proxy handshake */
if (connection_proxy_connect(conn, proxy_type) < 0) {
@@ -335,6 +484,51 @@ connection_or_finished_connecting(or_connection_t *or_conn)
return 0;
}
+/* Called when we're about to finally unlink and free an OR connection:
+ * perform necessary accounting and cleanup */
+void
+connection_or_about_to_close(or_connection_t *or_conn)
+{
+ time_t now = time(NULL);
+ connection_t *conn = TO_CONN(or_conn);
+
+ /* Remember why we're closing this connection. */
+ if (conn->state != OR_CONN_STATE_OPEN) {
+ /* Inform any pending (not attached) circs that they should
+ * give up. */
+ circuit_n_conn_done(TO_OR_CONN(conn), 0);
+ /* now mark things down as needed */
+ if (connection_or_nonopen_was_started_here(or_conn)) {
+ const or_options_t *options = get_options();
+ connection_or_note_state_when_broken(or_conn);
+ rep_hist_note_connect_failed(or_conn->identity_digest, now);
+ entry_guard_register_connect_status(or_conn->identity_digest,0,
+ !options->HTTPSProxy, now);
+ if (conn->state >= OR_CONN_STATE_TLS_HANDSHAKING) {
+ int reason = tls_error_to_orconn_end_reason(or_conn->tls_error);
+ control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED,
+ reason);
+ if (!authdir_mode_tests_reachability(options))
+ control_event_bootstrap_problem(
+ orconn_end_reason_to_control_string(reason), reason);
+ }
+ }
+ } else if (conn->hold_open_until_flushed) {
+ /* We only set hold_open_until_flushed when we're intentionally
+ * closing a connection. */
+ rep_hist_note_disconnect(or_conn->identity_digest, now);
+ control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
+ tls_error_to_orconn_end_reason(or_conn->tls_error));
+ } else if (!tor_digest_is_zero(or_conn->identity_digest)) {
+ rep_hist_note_connection_died(or_conn->identity_digest, now);
+ control_event_or_conn_status(or_conn, OR_CONN_EVENT_CLOSED,
+ tls_error_to_orconn_end_reason(or_conn->tls_error));
+ }
+ /* Now close all the attached circuits on it. */
+ circuit_unlink_all_from_or_conn(TO_OR_CONN(conn),
+ END_CIRC_REASON_OR_CONN_CLOSED);
+}
+
/** Return 1 if identity digest <b>id_digest</b> is known to be a
* currently or recently running relay. Otherwise return 0. */
int
@@ -342,7 +536,7 @@ connection_or_digest_is_known_relay(const char *id_digest)
{
if (router_get_consensus_status_by_id(id_digest))
return 1; /* It's in the consensus: "yes" */
- if (router_get_by_digest(id_digest))
+ if (router_get_by_id_digest(id_digest))
return 1; /* Not in the consensus, but we have a descriptor for
* it. Probably it was in a recent consensus. "Yes". */
return 0;
@@ -359,7 +553,7 @@ connection_or_digest_is_known_relay(const char *id_digest)
*/
static void
connection_or_update_token_buckets_helper(or_connection_t *conn, int reset,
- or_options_t *options)
+ const or_options_t *options)
{
int rate, burst; /* per-connection rate limiting params */
if (connection_or_digest_is_known_relay(conn->identity_digest)) {
@@ -382,6 +576,27 @@ connection_or_update_token_buckets_helper(or_connection_t *conn, int reset,
conn->bandwidthrate = rate;
conn->bandwidthburst = burst;
+#ifdef USE_BUFFEREVENTS
+ {
+ const struct timeval *tick = tor_libevent_get_one_tick_timeout();
+ struct ev_token_bucket_cfg *cfg, *old_cfg;
+ int64_t rate64 = (((int64_t)rate) * options->TokenBucketRefillInterval)
+ / 1000;
+ /* This can't overflow, since TokenBucketRefillInterval <= 1000,
+ * and rate started out less than INT_MAX. */
+ int rate_per_tick = (int) rate64;
+
+ cfg = ev_token_bucket_cfg_new(rate_per_tick, burst, rate_per_tick,
+ burst, tick);
+ old_cfg = conn->bucket_cfg;
+ if (conn->_base.bufev)
+ tor_set_bufferevent_rate_limit(conn->_base.bufev, cfg);
+ if (old_cfg)
+ ev_token_bucket_cfg_free(old_cfg);
+ conn->bucket_cfg = cfg;
+ (void) reset; /* No way to do this with libevent yet. */
+ }
+#else
if (reset) { /* set up the token buckets to be full */
conn->read_bucket = conn->write_bucket = burst;
return;
@@ -392,13 +607,15 @@ connection_or_update_token_buckets_helper(or_connection_t *conn, int reset,
conn->read_bucket = burst;
if (conn->write_bucket > burst)
conn->write_bucket = burst;
+#endif
}
/** Either our set of relays or our per-conn rate limits have changed.
* Go through all the OR connections and update their token buckets to make
* sure they don't exceed their maximum values. */
void
-connection_or_update_token_buckets(smartlist_t *conns, or_options_t *options)
+connection_or_update_token_buckets(smartlist_t *conns,
+ const or_options_t *options)
{
SMARTLIST_FOREACH(conns, connection_t *, conn,
{
@@ -410,13 +627,13 @@ connection_or_update_token_buckets(smartlist_t *conns, or_options_t *options)
/** If we don't necessarily know the router we're connecting to, but we
* have an addr/port/id_digest, then fill in as much as we can. Start
* by checking to see if this describes a router we know. */
-static void
+void
connection_or_init_conn_from_address(or_connection_t *conn,
const tor_addr_t *addr, uint16_t port,
const char *id_digest,
int started_here)
{
- routerinfo_t *r = router_get_by_digest(id_digest);
+ const node_t *r = node_get_by_id(id_digest);
connection_or_set_identity_digest(conn, id_digest);
connection_or_update_token_buckets_helper(conn, 1, get_options());
@@ -424,8 +641,10 @@ connection_or_init_conn_from_address(or_connection_t *conn,
tor_addr_copy(&conn->_base.addr, addr);
tor_addr_copy(&conn->real_addr, addr);
if (r) {
+ tor_addr_t node_addr;
+ node_get_addr(r, &node_addr);
/* XXXX proposal 118 will make this more complex. */
- if (tor_addr_eq_ipv4h(&conn->_base.addr, r->addr))
+ if (tor_addr_eq(&conn->_base.addr, &node_addr))
conn->is_canonical = 1;
if (!started_here) {
/* Override the addr/port, so our log messages will make sense.
@@ -438,12 +657,12 @@ connection_or_init_conn_from_address(or_connection_t *conn,
* right IP address and port 56244, that wouldn't be as helpful. now we
* log the "right" port too, so we know if it's moria1 or moria2.
*/
- tor_addr_from_ipv4h(&conn->_base.addr, r->addr);
- conn->_base.port = r->or_port;
+ tor_addr_copy(&conn->_base.addr, &node_addr);
+ conn->_base.port = node_get_orport(r);
}
- conn->nickname = tor_strdup(r->nickname);
+ conn->nickname = tor_strdup(node_get_nickname(r));
tor_free(conn->_base.address);
- conn->_base.address = tor_strdup(r->address);
+ conn->_base.address = tor_dup_addr(&node_addr);
} else {
const char *n;
/* If we're an authoritative directory server, we may know a
@@ -792,11 +1011,15 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
const char *id_digest)
{
or_connection_t *conn;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int socket_error = 0;
- int using_proxy = 0;
tor_addr_t addr;
+ int r;
+ tor_addr_t proxy_addr;
+ uint16_t proxy_port;
+ int proxy_type;
+
tor_assert(_addr);
tor_assert(id_digest);
tor_addr_copy(&addr, _addr);
@@ -815,19 +1038,20 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
conn->is_outgoing = 1;
- /* use a proxy server if available */
- if (options->HTTPSProxy) {
- using_proxy = 1;
- tor_addr_copy(&addr, &options->HTTPSProxyAddr);
- port = options->HTTPSProxyPort;
- } else if (options->Socks4Proxy) {
- using_proxy = 1;
- tor_addr_copy(&addr, &options->Socks4ProxyAddr);
- port = options->Socks4ProxyPort;
- } else if (options->Socks5Proxy) {
- using_proxy = 1;
- tor_addr_copy(&addr, &options->Socks5ProxyAddr);
- port = options->Socks5ProxyPort;
+ /* If we are using a proxy server, find it and use it. */
+ r = get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, TO_CONN(conn));
+ if (r == 0) {
+ conn->proxy_type = proxy_type;
+ if (proxy_type != PROXY_NONE) {
+ tor_addr_copy(&addr, &proxy_addr);
+ port = proxy_port;
+ conn->_base.proxy_state = PROXY_INFANT;
+ }
+ } else {
+ log_warn(LD_GENERAL, "Tried to connect through proxy, but proxy address "
+ "could not be found.");
+ connection_free(TO_CONN(conn));
+ return NULL;
}
switch (connection_connect(TO_CONN(conn), conn->_base.address,
@@ -835,7 +1059,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
case -1:
/* If the connection failed immediately, and we're using
* a proxy, our proxy is down. Don't blame the Tor server. */
- if (!using_proxy)
+ if (conn->_base.proxy_state == PROXY_INFANT)
entry_guard_register_connect_status(conn->identity_digest,
0, 1, time(NULL));
connection_or_connect_failed(conn,
@@ -870,6 +1094,7 @@ int
connection_tls_start_handshake(or_connection_t *conn, int receiving)
{
conn->_base.state = OR_CONN_STATE_TLS_HANDSHAKING;
+ tor_assert(!conn->tls);
conn->tls = tor_tls_new(conn->_base.s, receiving);
tor_tls_set_logged_address(conn->tls, // XXX client and relay?
escaped_safe_str(conn->_base.address));
@@ -877,12 +1102,38 @@ connection_tls_start_handshake(or_connection_t *conn, int receiving)
log_warn(LD_BUG,"tor_tls_new failed. Closing.");
return -1;
}
+#ifdef USE_BUFFEREVENTS
+ if (connection_type_uses_bufferevent(TO_CONN(conn))) {
+ const int filtering = get_options()->_UseFilteringSSLBufferevents;
+ struct bufferevent *b =
+ tor_tls_init_bufferevent(conn->tls, conn->_base.bufev, conn->_base.s,
+ receiving, filtering);
+ if (!b) {
+ log_warn(LD_BUG,"tor_tls_init_bufferevent failed. Closing.");
+ return -1;
+ }
+ conn->_base.bufev = b;
+ if (conn->bucket_cfg)
+ tor_set_bufferevent_rate_limit(conn->_base.bufev, conn->bucket_cfg);
+ connection_enable_rate_limiting(TO_CONN(conn));
+
+ connection_configure_bufferevent_callbacks(TO_CONN(conn));
+ bufferevent_setcb(b,
+ connection_handle_read_cb,
+ connection_handle_write_cb,
+ connection_or_handle_event_cb,/* overriding this one*/
+ TO_CONN(conn));
+ }
+#endif
connection_start_reading(TO_CONN(conn));
log_debug(LD_HANDSHAKE,"starting TLS handshake on fd %d", conn->_base.s);
note_crypto_pk_op(receiving ? TLS_HANDSHAKE_S : TLS_HANDSHAKE_C);
- if (connection_tls_continue_handshake(conn) < 0) {
- return -1;
+ IF_HAS_BUFFEREVENT(TO_CONN(conn), {
+ /* ???? */;
+ }) ELSE_IF_NO_BUFFEREVENT {
+ if (connection_tls_continue_handshake(conn) < 0)
+ return -1;
}
return 0;
}
@@ -936,16 +1187,22 @@ connection_tls_continue_handshake(or_connection_t *conn)
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
- log_debug(LD_OR, "Done with initial SSL handshake (client-side). "
- "Requesting renegotiation.");
- conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
- goto again;
+ if (tor_tls_received_v3_certificate(conn->tls)) {
+ log_info(LD_OR, "Client got a v3 cert! Moving on to v3 "
+ "handshake.");
+ return connection_or_launch_v3_or_handshake(conn);
+ } else {
+ log_debug(LD_OR, "Done with initial SSL handshake (client-side)."
+ " Requesting renegotiation.");
+ conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
+ goto again;
+ }
}
// log_notice(LD_OR,"Done. state was %d.", conn->_base.state);
} else {
- /* improved handshake, but not a client. */
+ /* v2/v3 handshake, but not a client. */
log_debug(LD_OR, "Done with initial SSL handshake (server-side). "
- "Expecting renegotiation.");
+ "Expecting renegotiation or VERSIONS cell");
tor_tls_set_renegotiate_callback(conn->tls,
connection_or_tls_renegotiated_cb,
conn);
@@ -970,6 +1227,85 @@ connection_tls_continue_handshake(or_connection_t *conn)
return 0;
}
+#ifdef USE_BUFFEREVENTS
+static void
+connection_or_handle_event_cb(struct bufferevent *bufev, short event,
+ void *arg)
+{
+ struct or_connection_t *conn = TO_OR_CONN(arg);
+
+ /* XXXX cut-and-paste code; should become a function. */
+ if (event & BEV_EVENT_CONNECTED) {
+ if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
+ if (tor_tls_finish_handshake(conn->tls) < 0) {
+ log_warn(LD_OR, "Problem finishing handshake");
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ }
+
+ if (! tor_tls_used_v1_handshake(conn->tls)) {
+ if (!tor_tls_is_server(conn->tls)) {
+ if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
+ if (tor_tls_received_v3_certificate(conn->tls)) {
+ log_info(LD_OR, "Client got a v3 cert!");
+ if (connection_or_launch_v3_or_handshake(conn) < 0)
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ } else {
+ conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
+ tor_tls_unblock_renegotiation(conn->tls);
+ if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) {
+ log_warn(LD_OR, "Start_renegotiating went badly.");
+ connection_mark_for_close(TO_CONN(conn));
+ }
+ tor_tls_unblock_renegotiation(conn->tls);
+ return; /* ???? */
+ }
+ }
+ } else if (tor_tls_get_num_server_handshakes(conn->tls) == 1) {
+ /* v2 or v3 handshake, as a server. Only got one handshake, so
+ * wait for the next one. */
+ tor_tls_set_renegotiate_callback(conn->tls,
+ connection_or_tls_renegotiated_cb,
+ conn);
+ conn->_base.state = OR_CONN_STATE_TLS_SERVER_RENEGOTIATING;
+ /* return 0; */
+ return; /* ???? */
+ } else {
+ const int handshakes = tor_tls_get_num_server_handshakes(conn->tls);
+ tor_assert(handshakes >= 2);
+ if (handshakes == 2) {
+ /* v2 handshake, as a server. Two handshakes happened already,
+ * so we treat renegotiation as done.
+ */
+ connection_or_tls_renegotiated_cb(conn->tls, conn);
+ } else {
+ log_warn(LD_OR, "More than two handshakes done on connection. "
+ "Closing.");
+ connection_mark_for_close(TO_CONN(conn));
+ }
+ return;
+ }
+ }
+ connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
+ if (connection_tls_finish_handshake(conn) < 0)
+ connection_mark_for_close(TO_CONN(conn)); /* ???? */
+ return;
+ }
+
+ if (event & BEV_EVENT_ERROR) {
+ unsigned long err;
+ while ((err = bufferevent_get_openssl_error(bufev))) {
+ tor_tls_log_one_error(conn->tls, err, LOG_WARN, LD_OR,
+ "handshaking (with bufferevent)");
+ }
+ }
+
+ connection_handle_event_cb(bufev, event, arg);
+}
+#endif
+
/** Return 1 if we initiated this connection, or 0 if it started
* out as an incoming connection.
*/
@@ -984,6 +1320,29 @@ connection_or_nonopen_was_started_here(or_connection_t *conn)
return !tor_tls_is_server(conn->tls);
}
+/** Set the circid_type field of <b>conn</b> (which determines which part of
+ * the circuit ID space we're willing to use) based on comparing our ID to
+ * <b>identity_rcvd</b> */
+void
+connection_or_set_circid_type(or_connection_t *conn,
+ crypto_pk_env_t *identity_rcvd)
+{
+ const int started_here = connection_or_nonopen_was_started_here(conn);
+ crypto_pk_env_t *our_identity =
+ started_here ? get_tlsclient_identity_key() :
+ get_server_identity_key();
+
+ if (identity_rcvd) {
+ if (crypto_pk_cmp_keys(our_identity, identity_rcvd)<0) {
+ conn->circ_id_type = CIRC_ID_TYPE_LOWER;
+ } else {
+ conn->circ_id_type = CIRC_ID_TYPE_HIGHER;
+ }
+ } else {
+ conn->circ_id_type = CIRC_ID_TYPE_NEITHER;
+ }
+}
+
/** <b>Conn</b> just completed its handshake. Return 0 if all is well, and
* return -1 if he is lying, broken, or otherwise something is wrong.
*
@@ -1015,16 +1374,13 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
char *digest_rcvd_out)
{
crypto_pk_env_t *identity_rcvd=NULL;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN;
const char *safe_address =
started_here ? conn->_base.address :
safe_str_client(conn->_base.address);
const char *conn_type = started_here ? "outgoing" : "incoming";
- crypto_pk_env_t *our_identity =
- started_here ? get_tlsclient_identity_key() :
- get_server_identity_key();
- int has_cert = 0, has_identity=0;
+ int has_cert = 0;
check_no_tls_errors();
has_cert = tor_tls_peer_has_cert(conn->tls);
@@ -1059,21 +1415,46 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
}
if (identity_rcvd) {
- has_identity = 1;
crypto_pk_get_digest(identity_rcvd, digest_rcvd_out);
- if (crypto_pk_cmp_keys(our_identity, identity_rcvd)<0) {
- conn->circ_id_type = CIRC_ID_TYPE_LOWER;
- } else {
- conn->circ_id_type = CIRC_ID_TYPE_HIGHER;
- }
- crypto_free_pk_env(identity_rcvd);
} else {
memset(digest_rcvd_out, 0, DIGEST_LEN);
- conn->circ_id_type = CIRC_ID_TYPE_NEITHER;
}
- if (started_here && tor_digest_is_zero(conn->identity_digest)) {
- connection_or_set_identity_digest(conn, digest_rcvd_out);
+ connection_or_set_circid_type(conn, identity_rcvd);
+ crypto_free_pk_env(identity_rcvd);
+
+ if (started_here)
+ return connection_or_client_learned_peer_id(conn,
+ (const uint8_t*)digest_rcvd_out);
+
+ return 0;
+}
+
+/** Called when we (as a connection initiator) have definitively,
+ * authenticatedly, learned that ID of the Tor instance on the other
+ * side of <b>conn</b> is <b>peer_id</b>. For v1 and v2 handshakes,
+ * this is right after we get a certificate chain in a TLS handshake
+ * or renegotiation. For v3 handshakes, this is right after we get a
+ * certificate chain in a CERT cell.
+ *
+ * If we want any particular ID before, record the one we got.
+ *
+ * If we wanted an ID, but we didn't get it, log a warning and return -1.
+ *
+ * If we're testing reachability, remember what we learned.
+ *
+ * Return 0 on success, -1 on failure.
+ */
+int
+connection_or_client_learned_peer_id(or_connection_t *conn,
+ const uint8_t *peer_id)
+{
+ int as_expected = 1;
+ const or_options_t *options = get_options();
+ int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN;
+
+ if (tor_digest_is_zero(conn->identity_digest)) {
+ connection_or_set_identity_digest(conn, (const char*)peer_id);
tor_free(conn->nickname);
conn->nickname = tor_malloc(HEX_DIGEST_LEN+2);
conn->nickname[0] = '$';
@@ -1085,43 +1466,39 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
/* if it's a bridge and we didn't know its identity fingerprint, now
* we do -- remember it for future attempts. */
learned_router_identity(&conn->_base.addr, conn->_base.port,
- digest_rcvd_out);
+ (const char*)peer_id);
}
- if (started_here) {
- int as_advertised = 1;
- tor_assert(has_cert);
- tor_assert(has_identity);
- if (tor_memneq(digest_rcvd_out, conn->identity_digest, DIGEST_LEN)) {
- /* I was aiming for a particular digest. I didn't get it! */
- char seen[HEX_DIGEST_LEN+1];
- char expected[HEX_DIGEST_LEN+1];
- base16_encode(seen, sizeof(seen), digest_rcvd_out, DIGEST_LEN);
- base16_encode(expected, sizeof(expected), conn->identity_digest,
- DIGEST_LEN);
- log_fn(severity, LD_HANDSHAKE,
- "Tried connecting to router at %s:%d, but identity key was not "
- "as expected: wanted %s but got %s.",
- conn->_base.address, conn->_base.port, expected, seen);
- entry_guard_register_connect_status(conn->identity_digest, 0, 1,
- time(NULL));
- control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
- END_OR_CONN_REASON_OR_IDENTITY);
- if (!authdir_mode_tests_reachability(options))
- control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY);
- as_advertised = 0;
- }
- if (authdir_mode_tests_reachability(options)) {
- dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
- digest_rcvd_out, as_advertised);
- }
- if (!as_advertised)
- return -1;
+ if (tor_memneq(peer_id, conn->identity_digest, DIGEST_LEN)) {
+ /* I was aiming for a particular digest. I didn't get it! */
+ char seen[HEX_DIGEST_LEN+1];
+ char expected[HEX_DIGEST_LEN+1];
+ base16_encode(seen, sizeof(seen), (const char*)peer_id, DIGEST_LEN);
+ base16_encode(expected, sizeof(expected), conn->identity_digest,
+ DIGEST_LEN);
+ log_fn(severity, LD_HANDSHAKE,
+ "Tried connecting to router at %s:%d, but identity key was not "
+ "as expected: wanted %s but got %s.",
+ conn->_base.address, conn->_base.port, expected, seen);
+ entry_guard_register_connect_status(conn->identity_digest, 0, 1,
+ time(NULL));
+ control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
+ END_OR_CONN_REASON_OR_IDENTITY);
+ if (!authdir_mode_tests_reachability(options))
+ control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY);
+ as_expected = 0;
+ }
+ if (authdir_mode_tests_reachability(options)) {
+ dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
+ (const char*)peer_id, as_expected);
}
+ if (!as_expected)
+ return -1;
+
return 0;
}
-/** The tls handshake is finished.
+/** The v1/v2 TLS handshake is finished.
*
* Make sure we are happy with the person we just handshaked with.
*
@@ -1131,6 +1508,8 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
* If all is successful, call circuit_n_conn_done() to handle events
* that have been pending on the <tls handshake completion. Also set the
* directory to be dirty (only matters if I'm an authdirserver).
+ *
+ * If this is a v2 TLS handshake, send a versions cell.
*/
static int
connection_tls_finish_handshake(or_connection_t *conn)
@@ -1138,7 +1517,9 @@ connection_tls_finish_handshake(or_connection_t *conn)
char digest_rcvd[DIGEST_LEN];
int started_here = connection_or_nonopen_was_started_here(conn);
- log_debug(LD_HANDSHAKE,"tls handshake with %s done. verifying.",
+ log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done. verifying.",
+ started_here?"outgoing":"incoming",
+ conn,
safe_str_client(conn->_base.address));
directory_set_dirty();
@@ -1158,25 +1539,48 @@ connection_tls_finish_handshake(or_connection_t *conn)
tor_tls_block_renegotiation(conn->tls);
return connection_or_set_state_open(conn);
} else {
- conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING;
+ conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V2;
if (connection_init_or_handshake_state(conn, started_here) < 0)
return -1;
if (!started_here) {
connection_or_init_conn_from_address(conn, &conn->_base.addr,
conn->_base.port, digest_rcvd, 0);
}
- return connection_or_send_versions(conn);
+ return connection_or_send_versions(conn, 0);
}
}
+/**
+ * Called as client when initial TLS handshake is done, and we notice
+ * that we got a v3-handshake signalling certificate from the server.
+ * Set up structures, do bookkeeping, and send the versions cell.
+ * Return 0 on success and -1 on failure.
+ */
+static int
+connection_or_launch_v3_or_handshake(or_connection_t *conn)
+{
+ tor_assert(connection_or_nonopen_was_started_here(conn));
+ tor_assert(tor_tls_received_v3_certificate(conn->tls));
+
+ circuit_build_times_network_is_live(&circ_times);
+
+ conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
+ if (connection_init_or_handshake_state(conn, 1) < 0)
+ return -1;
+
+ return connection_or_send_versions(conn, 1);
+}
+
/** Allocate a new connection handshake state for the connection
* <b>conn</b>. Return 0 on success, -1 on failure. */
-static int
+int
connection_init_or_handshake_state(or_connection_t *conn, int started_here)
{
or_handshake_state_t *s;
s = conn->handshake_state = tor_malloc_zero(sizeof(or_handshake_state_t));
s->started_here = started_here ? 1 : 0;
+ s->digest_sent_data = 1;
+ s->digest_received_data = 1;
return 0;
}
@@ -1186,10 +1590,89 @@ or_handshake_state_free(or_handshake_state_t *state)
{
if (!state)
return;
+ crypto_free_digest_env(state->digest_sent);
+ crypto_free_digest_env(state->digest_received);
+ tor_cert_free(state->auth_cert);
+ tor_cert_free(state->id_cert);
memset(state, 0xBE, sizeof(or_handshake_state_t));
tor_free(state);
}
+/**
+ * Remember that <b>cell</b> has been transmitted (if <b>incoming</b> is
+ * false) or received (if <b>incoming is true) during a V3 handshake using
+ * <b>state</b>.
+ *
+ * (We don't record the cell, but we keep a digest of everything sent or
+ * received during the v3 handshake, and the client signs it in an
+ * authenticate cell.)
+ */
+void
+or_handshake_state_record_cell(or_handshake_state_t *state,
+ const cell_t *cell,
+ int incoming)
+{
+ crypto_digest_env_t *d, **dptr;
+ packed_cell_t packed;
+ if (incoming) {
+ if (!state->digest_received_data)
+ return;
+ } else {
+ if (!state->digest_sent_data)
+ return;
+ }
+ if (!incoming) {
+ log_warn(LD_BUG, "We shouldn't be sending any non-variable-length cells "
+ "while making a handshake digest. But we think we are sending "
+ "one with type %d.", (int)cell->command);
+ }
+ dptr = incoming ? &state->digest_received : &state->digest_sent;
+ if (! *dptr)
+ *dptr = crypto_new_digest256_env(DIGEST_SHA256);
+
+ d = *dptr;
+ /* Re-packing like this is a little inefficient, but we don't have to do
+ this very often at all. */
+ cell_pack(&packed, cell);
+ crypto_digest_add_bytes(d, packed.body, sizeof(packed.body));
+ memset(&packed, 0, sizeof(packed));
+}
+
+/** Remember that a variable-length <b>cell</b> has been transmitted (if
+ * <b>incoming</b> is false) or received (if <b>incoming is true) during a V3
+ * handshake using <b>state</b>.
+ *
+ * (We don't record the cell, but we keep a digest of everything sent or
+ * received during the v3 handshake, and the client signs it in an
+ * authenticate cell.)
+ */
+void
+or_handshake_state_record_var_cell(or_handshake_state_t *state,
+ const var_cell_t *cell,
+ int incoming)
+{
+ crypto_digest_env_t *d, **dptr;
+ char buf[VAR_CELL_HEADER_SIZE];
+ if (incoming) {
+ if (!state->digest_received_data)
+ return;
+ } else {
+ if (!state->digest_sent_data)
+ return;
+ }
+ dptr = incoming ? &state->digest_received : &state->digest_sent;
+ if (! *dptr)
+ *dptr = crypto_new_digest256_env(DIGEST_SHA256);
+
+ d = *dptr;
+
+ var_cell_pack_header(cell, buf);
+ crypto_digest_add_bytes(d, buf, sizeof(buf));
+ crypto_digest_add_bytes(d, (const char *)cell->payload, cell->payload_len);
+
+ memset(buf, 0, sizeof(buf));
+}
+
/** Set <b>conn</b>'s state to OR_CONN_STATE_OPEN, and tell other subsystems
* as appropriate. Called when we are done with all TLS and OR handshaking.
*/
@@ -1219,7 +1702,7 @@ connection_or_set_state_open(or_connection_t *conn)
router_set_status(conn->identity_digest, 1);
} else {
/* only report it to the geoip module if it's not a known router */
- if (!router_get_by_digest(conn->identity_digest)) {
+ if (!router_get_by_id_digest(conn->identity_digest)) {
if (tor_addr_family(&TO_CONN(conn)->addr) == AF_INET) {
/*XXXX IP6 support ipv6 geoip.*/
uint32_t a = tor_addr_to_ipv4h(&TO_CONN(conn)->addr);
@@ -1230,8 +1713,12 @@ connection_or_set_state_open(or_connection_t *conn)
or_handshake_state_free(conn->handshake_state);
conn->handshake_state = NULL;
+ IF_HAS_BUFFEREVENT(TO_CONN(conn), {
+ connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ connection_start_reading(TO_CONN(conn));
+ }
- connection_start_reading(TO_CONN(conn));
circuit_n_conn_done(conn, 1); /* send the pending creates, if any. */
return 0;
@@ -1253,6 +1740,9 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
connection_write_to_buf(networkcell.body, CELL_NETWORK_SIZE, TO_CONN(conn));
+ if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
+ or_handshake_state_record_cell(conn->handshake_state, cell, 0);
+
if (cell->command != CELL_PADDING)
conn->timestamp_last_added_nonpadding = approx_time();
}
@@ -1272,16 +1762,24 @@ connection_or_write_var_cell_to_buf(const var_cell_t *cell,
connection_write_to_buf(hdr, sizeof(hdr), TO_CONN(conn));
connection_write_to_buf((char*)cell->payload,
cell->payload_len, TO_CONN(conn));
+ if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
+ or_handshake_state_record_var_cell(conn->handshake_state, cell, 0);
if (cell->command != CELL_PADDING)
conn->timestamp_last_added_nonpadding = approx_time();
}
-/** See whether there's a variable-length cell waiting on <b>conn</b>'s
+/** See whether there's a variable-length cell waiting on <b>or_conn</b>'s
* inbuf. Return values as for fetch_var_cell_from_buf(). */
static int
-connection_fetch_var_cell_from_buf(or_connection_t *conn, var_cell_t **out)
+connection_fetch_var_cell_from_buf(or_connection_t *or_conn, var_cell_t **out)
{
- return fetch_var_cell_from_buf(conn->_base.inbuf, out, conn->link_proto);
+ connection_t *conn = TO_CONN(or_conn);
+ IF_HAS_BUFFEREVENT(conn, {
+ struct evbuffer *input = bufferevent_get_input(conn->bufev);
+ return fetch_var_cell_from_evbuffer(input, out, or_conn->link_proto);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return fetch_var_cell_from_buf(conn->inbuf, out, or_conn->link_proto);
+ }
}
/** Process cells from <b>conn</b>'s inbuf.
@@ -1299,7 +1797,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn)
while (1) {
log_debug(LD_OR,
"%d: starting, inbuf_datalen %d (%d pending in tls object).",
- conn->_base.s,(int)buf_datalen(conn->_base.inbuf),
+ conn->_base.s,(int)connection_get_inbuf_len(TO_CONN(conn)),
tor_tls_get_pending_bytes(conn->tls));
if (connection_fetch_var_cell_from_buf(conn, &var_cell)) {
if (!var_cell)
@@ -1310,8 +1808,8 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn)
} else {
char buf[CELL_NETWORK_SIZE];
cell_t cell;
- if (buf_datalen(conn->_base.inbuf) < CELL_NETWORK_SIZE) /* whole response
- available? */
+ if (connection_get_inbuf_len(TO_CONN(conn))
+ < CELL_NETWORK_SIZE) /* whole response available? */
return 0; /* not yet */
circuit_build_times_network_is_live(&circ_times);
@@ -1350,7 +1848,7 @@ connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, int reason)
}
/** Array of recognized link protocol versions. */
-static const uint16_t or_protocol_versions[] = { 1, 2 };
+static const uint16_t or_protocol_versions[] = { 1, 2, 3 };
/** Number of versions in <b>or_protocol_versions</b>. */
static const int n_or_protocol_versions =
(int)( sizeof(or_protocol_versions)/sizeof(uint16_t) );
@@ -1369,20 +1867,33 @@ is_or_protocol_version_known(uint16_t v)
}
/** Send a VERSIONS cell on <b>conn</b>, telling the other host about the
- * link protocol versions that this Tor can support. */
-static int
-connection_or_send_versions(or_connection_t *conn)
+ * link protocol versions that this Tor can support.
+ *
+ * If <b>v3_plus</b>, this is part of a V3 protocol handshake, so only
+ * allow protocol version v3 or later. If not <b>v3_plus</b>, this is
+ * not part of a v3 protocol handshake, so don't allow protocol v3 or
+ * later.
+ **/
+int
+connection_or_send_versions(or_connection_t *conn, int v3_plus)
{
var_cell_t *cell;
int i;
+ int n_versions = 0;
+ const int min_version = v3_plus ? 3 : 0;
+ const int max_version = v3_plus ? UINT16_MAX : 2;
tor_assert(conn->handshake_state &&
!conn->handshake_state->sent_versions_at);
cell = var_cell_new(n_or_protocol_versions * 2);
cell->command = CELL_VERSIONS;
for (i = 0; i < n_or_protocol_versions; ++i) {
uint16_t v = or_protocol_versions[i];
- set_uint16(cell->payload+(2*i), htons(v));
+ if (v < min_version || v > max_version)
+ continue;
+ set_uint16(cell->payload+(2*n_versions), htons(v));
+ ++n_versions;
}
+ cell->payload_len = n_versions * 2;
connection_or_write_var_cell_to_buf(cell, conn);
conn->handshake_state->sent_versions_at = time(NULL);
@@ -1398,10 +1909,12 @@ connection_or_send_netinfo(or_connection_t *conn)
{
cell_t cell;
time_t now = time(NULL);
- routerinfo_t *me;
+ const routerinfo_t *me;
int len;
uint8_t *out;
+ tor_assert(conn->handshake_state);
+
memset(&cell, 0, sizeof(cell_t));
cell.command = CELL_NETINFO;
@@ -1428,8 +1941,285 @@ connection_or_send_netinfo(or_connection_t *conn)
*out = 0;
}
+ conn->handshake_state->digest_sent_data = 0;
connection_or_write_cell_to_buf(&cell, conn);
return 0;
}
+/** Send a CERT cell on the connection <b>conn</b>. Return 0 on success, -1
+ * on failure. */
+int
+connection_or_send_cert_cell(or_connection_t *conn)
+{
+ const tor_cert_t *link_cert = NULL, *id_cert = NULL;
+ const uint8_t *link_encoded = NULL, *id_encoded = NULL;
+ size_t link_len, id_len;
+ var_cell_t *cell;
+ size_t cell_len;
+ ssize_t pos;
+ int server_mode;
+
+ tor_assert(conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
+
+ if (! conn->handshake_state)
+ return -1;
+ server_mode = ! conn->handshake_state->started_here;
+ if (tor_tls_get_my_certs(server_mode, &link_cert, &id_cert) < 0)
+ return -1;
+ tor_cert_get_der(link_cert, &link_encoded, &link_len);
+ tor_cert_get_der(id_cert, &id_encoded, &id_len);
+
+ cell_len = 1 /* 1 byte: num certs in cell */ +
+ 2 * ( 1 + 2 ) /* For each cert: 1 byte for type, 2 for length */ +
+ link_len + id_len;
+ cell = var_cell_new(cell_len);
+ cell->command = CELL_CERT;
+ cell->payload[0] = 2;
+ pos = 1;
+
+ if (server_mode)
+ cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */
+ else
+ cell->payload[pos] = OR_CERT_TYPE_AUTH_1024; /* client authentication */
+ set_uint16(&cell->payload[pos+1], htons(link_len));
+ memcpy(&cell->payload[pos+3], link_encoded, link_len);
+ pos += 3 + link_len;
+
+ cell->payload[pos] = OR_CERT_TYPE_ID_1024; /* ID cert */
+ set_uint16(&cell->payload[pos+1], htons(id_len));
+ memcpy(&cell->payload[pos+3], id_encoded, id_len);
+ pos += 3 + id_len;
+
+ tor_assert(pos == (int)cell_len); /* Otherwise we just smashed the heap */
+
+ connection_or_write_var_cell_to_buf(cell, conn);
+ var_cell_free(cell);
+
+ return 0;
+}
+
+/** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0
+ * on success, -1 on failure. */
+int
+connection_or_send_auth_challenge_cell(or_connection_t *conn)
+{
+ var_cell_t *cell;
+ uint8_t *cp;
+ uint8_t challenge[OR_AUTH_CHALLENGE_LEN];
+ tor_assert(conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
+
+ if (! conn->handshake_state)
+ return -1;
+
+ if (crypto_rand((char*)challenge, OR_AUTH_CHALLENGE_LEN) < 0)
+ return -1;
+ cell = var_cell_new(OR_AUTH_CHALLENGE_LEN + 4);
+ cell->command = CELL_AUTH_CHALLENGE;
+ memcpy(cell->payload, challenge, OR_AUTH_CHALLENGE_LEN);
+ cp = cell->payload + OR_AUTH_CHALLENGE_LEN;
+ set_uint16(cp, htons(1)); /* We recognize one authentication type. */
+ set_uint16(cp+2, htons(AUTHTYPE_RSA_SHA256_TLSSECRET));
+
+ connection_or_write_var_cell_to_buf(cell, conn);
+ var_cell_free(cell);
+ memset(challenge, 0, sizeof(challenge));
+
+ return 0;
+}
+
+/** Compute the main body of an AUTHENTICATE cell that a client can use
+ * to authenticate itself on a v3 handshake for <b>conn</b>. Write it to the
+ * <b>outlen</b>-byte buffer at <b>out</b>.
+ *
+ * If <b>server</b> is true, only calculate the first
+ * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's
+ * determined by the rest of the handshake, and which match the provided value
+ * exactly.
+ *
+ * If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the
+ * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything
+ * that should be signed), but don't actually sign it.
+ *
+ * If <b>server</b> is false and <b>signing_key</b> is provided, calculate the
+ * entire authenticator, signed with <b>signing_key</b>.
+ * DOCDOC return value
+ */
+int
+connection_or_compute_authenticate_cell_body(or_connection_t *conn,
+ uint8_t *out, size_t outlen,
+ crypto_pk_env_t *signing_key,
+ int server)
+{
+ uint8_t *ptr;
+
+ /* assert state is reasonable XXXX */
+
+ if (outlen < V3_AUTH_FIXED_PART_LEN ||
+ (!server && outlen < V3_AUTH_BODY_LEN))
+ return -1;
+
+ ptr = out;
+
+ /* Type: 8 bytes. */
+ memcpy(ptr, "AUTH0001", 8);
+ ptr += 8;
+
+ {
+ const tor_cert_t *id_cert=NULL, *link_cert=NULL;
+ const digests_t *my_digests, *their_digests;
+ const uint8_t *my_id, *their_id, *client_id, *server_id;
+ if (tor_tls_get_my_certs(0, &link_cert, &id_cert))
+ return -1;
+ my_digests = tor_cert_get_id_digests(id_cert);
+ their_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert);
+ tor_assert(my_digests);
+ tor_assert(their_digests);
+ my_id = (uint8_t*)my_digests->d[DIGEST_SHA256];
+ their_id = (uint8_t*)their_digests->d[DIGEST_SHA256];
+
+ client_id = server ? their_id : my_id;
+ server_id = server ? my_id : their_id;
+
+ /* Client ID digest: 32 octets. */
+ memcpy(ptr, client_id, 32);
+ ptr += 32;
+
+ /* Server ID digest: 32 octets. */
+ memcpy(ptr, server_id, 32);
+ ptr += 32;
+ }
+
+ {
+ crypto_digest_env_t *server_d, *client_d;
+ if (server) {
+ server_d = conn->handshake_state->digest_sent;
+ client_d = conn->handshake_state->digest_received;
+ } else {
+ client_d = conn->handshake_state->digest_sent;
+ server_d = conn->handshake_state->digest_received;
+ }
+
+ /* Server log digest : 32 octets */
+ crypto_digest_get_digest(server_d, (char*)ptr, 32);
+ ptr += 32;
+
+ /* Client log digest : 32 octets */
+ crypto_digest_get_digest(client_d, (char*)ptr, 32);
+ ptr += 32;
+ }
+
+ {
+ /* Digest of cert used on TLS link : 32 octets. */
+ const tor_cert_t *cert = NULL;
+ tor_cert_t *freecert = NULL;
+ if (server) {
+ tor_tls_get_my_certs(1, &cert, NULL);
+ } else {
+ freecert = tor_tls_get_peer_cert(conn->tls);
+ cert = freecert;
+ }
+ if (!cert)
+ return -1;
+ memcpy(ptr, tor_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
+
+ if (freecert)
+ tor_cert_free(freecert);
+ ptr += 32;
+ }
+
+ /* HMAC of clientrandom and serverrandom using master key : 32 octets */
+ tor_tls_get_tlssecrets(conn->tls, ptr);
+ ptr += 32;
+
+ tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN);
+
+ if (server)
+ return V3_AUTH_FIXED_PART_LEN; // ptr-out
+
+ /* Time: 8 octets. */
+ {
+ uint64_t now = time(NULL);
+ if ((time_t)now < 0)
+ return -1;
+ set_uint32(ptr, htonl((uint32_t)(now>>32)));
+ set_uint32(ptr+4, htonl((uint32_t)now));
+ ptr += 8;
+ }
+
+ /* Nonce: 16 octets. */
+ crypto_rand((char*)ptr, 16);
+ ptr += 16;
+
+ tor_assert(ptr - out == V3_AUTH_BODY_LEN);
+
+ if (!signing_key)
+ return V3_AUTH_BODY_LEN; // ptr - out
+
+ {
+ int siglen;
+ char d[32];
+ crypto_digest256(d, (char*)out, ptr-out, DIGEST_SHA256);
+ siglen = crypto_pk_private_sign(signing_key,
+ (char*)ptr, outlen - (ptr-out),
+ d, 32);
+ if (siglen < 0)
+ return -1;
+
+ ptr += siglen;
+ tor_assert(ptr <= out+outlen);
+ return (int)(ptr - out);
+ }
+}
+
+/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on
+ * success, -1 on failure */
+int
+connection_or_send_authenticate_cell(or_connection_t *conn, int authtype)
+{
+ var_cell_t *cell;
+ crypto_pk_env_t *pk = tor_tls_get_my_client_auth_key();
+ int authlen;
+ size_t cell_maxlen;
+ /* XXXX make sure we're actually supposed to send this! */
+
+ if (!pk) {
+ log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key");
+ return -1;
+ }
+ if (authtype != AUTHTYPE_RSA_SHA256_TLSSECRET) {
+ log_warn(LD_BUG, "Tried to send authenticate cell with unknown "
+ "authentication type %d", authtype);
+ return -1;
+ }
+
+ cell_maxlen = 4 + /* overhead */
+ V3_AUTH_BODY_LEN + /* Authentication body */
+ crypto_pk_keysize(pk) + /* Max signature length */
+ 16 /* add a few extra bytes just in case. */;
+
+ cell = var_cell_new(cell_maxlen);
+ cell->command = CELL_AUTHENTICATE;
+ set_uint16(cell->payload, htons(AUTHTYPE_RSA_SHA256_TLSSECRET));
+ /* skip over length ; we don't know that yet. */
+
+ authlen = connection_or_compute_authenticate_cell_body(conn,
+ cell->payload+4,
+ cell_maxlen-4,
+ pk,
+ 0 /* not server */);
+ if (authlen < 0) {
+ log_warn(LD_BUG, "Unable to compute authenticate cell!");
+ var_cell_free(cell);
+ return -1;
+ }
+ tor_assert(authlen + 4 <= cell->payload_len);
+ set_uint16(cell->payload+2, htons(authlen));
+ cell->payload_len = authlen + 4;
+
+ connection_or_write_var_cell_to_buf(cell, conn);
+ var_cell_free(cell);
+
+ return 0;
+}
+
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index 70ef96a33..df009ab39 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -14,6 +14,7 @@
void connection_or_remove_from_identity_map(or_connection_t *conn);
void connection_or_clear_identity_map(void);
+void clear_broken_connection_map(int disable);
or_connection_t *connection_or_get_for_extend(const char *digest,
const tor_addr_t *target_addr,
const char **msg_out,
@@ -25,19 +26,40 @@ int connection_or_process_inbuf(or_connection_t *conn);
int connection_or_flushed_some(or_connection_t *conn);
int connection_or_finished_flushing(or_connection_t *conn);
int connection_or_finished_connecting(or_connection_t *conn);
+void connection_or_about_to_close(or_connection_t *conn);
int connection_or_digest_is_known_relay(const char *id_digest);
void connection_or_update_token_buckets(smartlist_t *conns,
- or_options_t *options);
+ const or_options_t *options);
void connection_or_connect_failed(or_connection_t *conn,
int reason, const char *msg);
or_connection_t *connection_or_connect(const tor_addr_t *addr, uint16_t port,
const char *id_digest);
+void connection_or_report_broken_states(int severity, int domain);
+
int connection_tls_start_handshake(or_connection_t *conn, int receiving);
int connection_tls_continue_handshake(or_connection_t *conn);
+int connection_init_or_handshake_state(or_connection_t *conn,
+ int started_here);
+void connection_or_init_conn_from_address(or_connection_t *conn,
+ const tor_addr_t *addr,
+ uint16_t port,
+ const char *id_digest,
+ int started_here);
+int connection_or_client_learned_peer_id(or_connection_t *conn,
+ const uint8_t *peer_id);
+void connection_or_set_circid_type(or_connection_t *conn,
+ crypto_pk_env_t *identity_rcvd);
void or_handshake_state_free(or_handshake_state_t *state);
+void or_handshake_state_record_cell(or_handshake_state_t *state,
+ const cell_t *cell,
+ int incoming);
+void or_handshake_state_record_var_cell(or_handshake_state_t *state,
+ const var_cell_t *cell,
+ int incoming);
+
int connection_or_set_state_open(or_connection_t *conn);
void connection_or_write_cell_to_buf(const cell_t *cell,
or_connection_t *conn);
@@ -45,7 +67,16 @@ void connection_or_write_var_cell_to_buf(const var_cell_t *cell,
or_connection_t *conn);
int connection_or_send_destroy(circid_t circ_id, or_connection_t *conn,
int reason);
+int connection_or_send_versions(or_connection_t *conn, int v3_plus);
int connection_or_send_netinfo(or_connection_t *conn);
+int connection_or_send_cert_cell(or_connection_t *conn);
+int connection_or_send_auth_challenge_cell(or_connection_t *conn);
+int connection_or_compute_authenticate_cell_body(or_connection_t *conn,
+ uint8_t *out, size_t outlen,
+ crypto_pk_env_t *signing_key,
+ int server);
+int connection_or_send_authenticate_cell(or_connection_t *conn, int type);
+
int is_or_protocol_version_known(uint16_t version);
void cell_pack(packed_cell_t *dest, const cell_t *src);
diff --git a/src/or/control.c b/src/or/control.c
index 1e411ec9c..1fdbac281 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -18,6 +18,7 @@
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
+#include "connection_or.h"
#include "control.h"
#include "directory.h"
#include "dirserv.h"
@@ -26,12 +27,18 @@
#include "hibernate.h"
#include "main.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#ifndef MS_WINDOWS
+#include <pwd.h>
+#include <sys/resource.h>
+#endif
+
#include "procmon.h"
/** Yield true iff <b>s</b> is the state of a control_connection_t that has
@@ -66,7 +73,9 @@
#define EVENT_CLIENTS_SEEN 0x0015
#define EVENT_NEWCONSENSUS 0x0016
#define EVENT_BUILDTIMEOUT_SET 0x0017
-#define _EVENT_MAX 0x0017
+#define EVENT_SIGNAL 0x0018
+#define EVENT_CONF_CHANGED 0x0019
+#define _EVENT_MAX 0x0019
/* If _EVENT_MAX ever hits 0x0020, we need to make the mask wider. */
/** Bitfield: The bit 1&lt;&lt;e is set if <b>any</b> open control
@@ -168,7 +177,7 @@ static int handle_control_resolve(control_connection_t *conn, uint32_t len,
static int handle_control_usefeature(control_connection_t *conn,
uint32_t len,
const char *body);
-static int write_stream_target_to_buf(edge_connection_t *conn, char *buf,
+static int write_stream_target_to_buf(entry_connection_t *conn, char *buf,
size_t len);
static void orconn_target_get_name(char *buf, size_t len,
or_connection_t *conn);
@@ -494,8 +503,8 @@ connection_printf_to_buf(control_connection_t *conn, const char *format, ...)
va_end(ap);
if (len < 0) {
- log_warn(LD_BUG, "Unable to format string for controller.");
- return;
+ log_err(LD_BUG, "Unable to format string for controller.");
+ tor_assert(0);
}
connection_write_to_buf(buf, (size_t)len, TO_CONN(conn));
@@ -509,7 +518,7 @@ control_ports_write_to_file(void)
{
smartlist_t *lines;
char *joined = NULL;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (!options->ControlPortWriteToFile)
return;
@@ -593,7 +602,7 @@ send_control_event_string(uint16_t event, event_format_t which,
else if (event == EVENT_STATUS_SERVER)
is_err = !strcmpstart(msg, "STATUS_SERVER ERR ");
if (is_err)
- connection_handle_write(TO_CONN(control_conn), 1);
+ connection_flush(TO_CONN(control_conn));
}
}
} SMARTLIST_FOREACH_END(conn);
@@ -647,7 +656,7 @@ get_circ(const char *id)
}
/** Given a text stream <b>id</b>, return the corresponding AP connection. */
-static edge_connection_t *
+static entry_connection_t *
get_stream(const char *id)
{
uint64_t n_id;
@@ -659,7 +668,7 @@ get_stream(const char *id)
conn = connection_get_by_global_id(n_id);
if (!conn || conn->type != CONN_TYPE_AP || conn->marked_for_close)
return NULL;
- return TO_EDGE_CONN(conn);
+ return TO_ENTRY_CONN(conn);
}
/** Helper for setconf and resetconf. Acts like setconf, except
@@ -798,7 +807,7 @@ handle_control_getconf(control_connection_t *conn, uint32_t body_len,
smartlist_t *unrecognized = smartlist_create();
char *msg = NULL;
size_t msg_len;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int i, len;
(void) body_len; /* body is NUL-terminated; so we can ignore len. */
@@ -910,13 +919,45 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len,
return 0;
}
+struct control_event_t {
+ uint16_t event_code;
+ const char *event_name;
+};
+static const struct control_event_t control_event_table[] = {
+ { EVENT_CIRCUIT_STATUS, "CIRC" },
+ { EVENT_STREAM_STATUS, "STREAM" },
+ { EVENT_OR_CONN_STATUS, "ORCONN" },
+ { EVENT_BANDWIDTH_USED, "BW" },
+ { EVENT_DEBUG_MSG, "DEBUG" },
+ { EVENT_INFO_MSG, "INFO" },
+ { EVENT_NOTICE_MSG, "NOTICE" },
+ { EVENT_WARN_MSG, "WARN" },
+ { EVENT_ERR_MSG, "ERR" },
+ { EVENT_NEW_DESC, "NEWDESC" },
+ { EVENT_ADDRMAP, "ADDRMAP" },
+ { EVENT_AUTHDIR_NEWDESCS, "AUTHDIR_NEWDESCS" },
+ { EVENT_DESCCHANGED, "DESCCHANGED" },
+ { EVENT_NS, "NS" },
+ { EVENT_STATUS_GENERAL, "STATUS_GENERAL" },
+ { EVENT_STATUS_CLIENT, "STATUS_CLIENT" },
+ { EVENT_STATUS_SERVER, "STATUS_SERVER" },
+ { EVENT_GUARD, "GUARD" },
+ { EVENT_STREAM_BANDWIDTH_USED, "STREAM_BW" },
+ { EVENT_CLIENTS_SEEN, "CLIENTS_SEEN" },
+ { EVENT_NEWCONSENSUS, "NEWCONSENSUS" },
+ { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" },
+ { EVENT_SIGNAL, "SIGNAL" },
+ { EVENT_CONF_CHANGED, "CONF_CHANGED"},
+ { 0, NULL },
+};
+
/** Called when we get a SETEVENTS message: update conn->event_mask,
* and reply with DONE or ERROR. */
static int
handle_control_setevents(control_connection_t *conn, uint32_t len,
const char *body)
{
- uint16_t event_code;
+ int event_code = -1;
uint32_t event_mask = 0;
smartlist_t *events = smartlist_create();
@@ -928,56 +969,22 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
{
if (!strcasecmp(ev, "EXTENDED")) {
continue;
- } else if (!strcasecmp(ev, "CIRC"))
- event_code = EVENT_CIRCUIT_STATUS;
- else if (!strcasecmp(ev, "STREAM"))
- event_code = EVENT_STREAM_STATUS;
- else if (!strcasecmp(ev, "ORCONN"))
- event_code = EVENT_OR_CONN_STATUS;
- else if (!strcasecmp(ev, "BW"))
- event_code = EVENT_BANDWIDTH_USED;
- else if (!strcasecmp(ev, "DEBUG"))
- event_code = EVENT_DEBUG_MSG;
- else if (!strcasecmp(ev, "INFO"))
- event_code = EVENT_INFO_MSG;
- else if (!strcasecmp(ev, "NOTICE"))
- event_code = EVENT_NOTICE_MSG;
- else if (!strcasecmp(ev, "WARN"))
- event_code = EVENT_WARN_MSG;
- else if (!strcasecmp(ev, "ERR"))
- event_code = EVENT_ERR_MSG;
- else if (!strcasecmp(ev, "NEWDESC"))
- event_code = EVENT_NEW_DESC;
- else if (!strcasecmp(ev, "ADDRMAP"))
- event_code = EVENT_ADDRMAP;
- else if (!strcasecmp(ev, "AUTHDIR_NEWDESCS"))
- event_code = EVENT_AUTHDIR_NEWDESCS;
- else if (!strcasecmp(ev, "DESCCHANGED"))
- event_code = EVENT_DESCCHANGED;
- else if (!strcasecmp(ev, "NS"))
- event_code = EVENT_NS;
- else if (!strcasecmp(ev, "STATUS_GENERAL"))
- event_code = EVENT_STATUS_GENERAL;
- else if (!strcasecmp(ev, "STATUS_CLIENT"))
- event_code = EVENT_STATUS_CLIENT;
- else if (!strcasecmp(ev, "STATUS_SERVER"))
- event_code = EVENT_STATUS_SERVER;
- else if (!strcasecmp(ev, "GUARD"))
- event_code = EVENT_GUARD;
- else if (!strcasecmp(ev, "STREAM_BW"))
- event_code = EVENT_STREAM_BANDWIDTH_USED;
- else if (!strcasecmp(ev, "CLIENTS_SEEN"))
- event_code = EVENT_CLIENTS_SEEN;
- else if (!strcasecmp(ev, "NEWCONSENSUS"))
- event_code = EVENT_NEWCONSENSUS;
- else if (!strcasecmp(ev, "BUILDTIMEOUT_SET"))
- event_code = EVENT_BUILDTIMEOUT_SET;
- else {
- connection_printf_to_buf(conn, "552 Unrecognized event \"%s\"\r\n",
- ev);
- SMARTLIST_FOREACH(events, char *, e, tor_free(e));
- smartlist_free(events);
- return 0;
+ } else {
+ int i;
+ for (i = 0; control_event_table[i].event_name != NULL; ++i) {
+ if (!strcasecmp(ev, control_event_table[i].event_name)) {
+ event_code = control_event_table[i].event_code;
+ break;
+ }
+ }
+
+ if (event_code == -1) {
+ connection_printf_to_buf(conn, "552 Unrecognized event \"%s\"\r\n",
+ ev);
+ SMARTLIST_FOREACH(events, char *, e, tor_free(e));
+ smartlist_free(events);
+ return 0;
+ }
}
event_mask |= (1 << event_code);
}
@@ -1039,7 +1046,7 @@ handle_control_authenticate(control_connection_t *conn, uint32_t len,
const char *body)
{
int used_quoted_string = 0;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
const char *errstr = NULL;
char *password;
size_t password_len;
@@ -1248,7 +1255,7 @@ handle_control_signal(control_connection_t *conn, uint32_t len,
send_control_done(conn);
/* Flush the "done" first if the signal might make us shut down. */
if (sig == SIGTERM || sig == SIGINT)
- connection_handle_write(TO_CONN(conn), 1);
+ connection_flush(TO_CONN(conn));
process_signal(sig);
@@ -1377,11 +1384,16 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
} else if (!strcmp(question, "info/names")) {
*answer = list_getinfo_options();
} else if (!strcmp(question, "events/names")) {
- *answer = tor_strdup("CIRC STREAM ORCONN BW DEBUG INFO NOTICE WARN ERR "
- "NEWDESC ADDRMAP AUTHDIR_NEWDESCS DESCCHANGED "
- "NS STATUS_GENERAL STATUS_CLIENT STATUS_SERVER "
- "GUARD STREAM_BW CLIENTS_SEEN NEWCONSENSUS "
- "BUILDTIMEOUT_SET");
+ int i;
+ smartlist_t *event_names = smartlist_create();
+
+ for (i = 0; control_event_table[i].event_name != NULL; ++i) {
+ smartlist_add(event_names, (char *)control_event_table[i].event_name);
+ }
+
+ *answer = smartlist_join_strings(event_names, " ", 0, NULL);
+
+ smartlist_free(event_names);
} else if (!strcmp(question, "features/names")) {
*answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
} else if (!strcmp(question, "address")) {
@@ -1391,6 +1403,61 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
return -1;
}
*answer = tor_dup_ip(addr);
+ } else if (!strcmp(question, "traffic/read")) {
+ tor_asprintf(answer, U64_FORMAT, U64_PRINTF_ARG(get_bytes_read()));
+ } else if (!strcmp(question, "traffic/written")) {
+ tor_asprintf(answer, U64_FORMAT, U64_PRINTF_ARG(get_bytes_written()));
+ } else if (!strcmp(question, "process/pid")) {
+ int myPid = -1;
+
+ #ifdef MS_WINDOWS
+ myPid = _getpid();
+ #else
+ myPid = getpid();
+ #endif
+
+ tor_asprintf(answer, "%d", myPid);
+ } else if (!strcmp(question, "process/uid")) {
+ #ifdef MS_WINDOWS
+ *answer = tor_strdup("-1");
+ #else
+ int myUid = geteuid();
+ tor_asprintf(answer, "%d", myUid);
+ #endif
+ } else if (!strcmp(question, "process/user")) {
+ #ifdef MS_WINDOWS
+ *answer = tor_strdup("");
+ #else
+ int myUid = geteuid();
+ struct passwd *myPwEntry = getpwuid(myUid);
+
+ if (myPwEntry) {
+ *answer = tor_strdup(myPwEntry->pw_name);
+ } else {
+ *answer = tor_strdup("");
+ }
+ #endif
+ } else if (!strcmp(question, "process/descriptor-limit")) {
+ /** platform specifc limits are from the set_max_file_descriptors function
+ * of src/common/compat.c */
+ /* XXXX023 This is duplicated code from compat.c; it should turn into a
+ * function. */
+ #ifdef HAVE_GETRLIMIT
+ struct rlimit descriptorLimit;
+
+ if (getrlimit(RLIMIT_NOFILE, &descriptorLimit) == 0) {
+ tor_asprintf(answer, U64_FORMAT,
+ U64_PRINTF_ARG(descriptorLimit.rlim_max));
+ } else {
+ *answer = tor_strdup("-1");
+ }
+ #elif defined(CYGWIN) || defined(__CYGWIN__)
+ *answer = tor_strdup("3200");
+ #elif defined(MS_WINDOWS)
+ *answer = tor_strdup("15000");
+ #else
+ *answer = tor_strdup("15000");
+ #endif
} else if (!strcmp(question, "dir-usage")) {
*answer = directory_dump_request_log();
} else if (!strcmp(question, "fingerprint")) {
@@ -1416,8 +1483,9 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
* NOTE: <b>ri_body</b> is as returned by signed_descriptor_get_body: it might
* not be NUL-terminated. */
static char *
-munge_extrainfo_into_routerinfo(const char *ri_body, signed_descriptor_t *ri,
- signed_descriptor_t *ei)
+munge_extrainfo_into_routerinfo(const char *ri_body,
+ const signed_descriptor_t *ri,
+ const signed_descriptor_t *ei)
{
char *out = NULL, *outp;
int i;
@@ -1519,16 +1587,17 @@ getinfo_helper_dir(control_connection_t *control_conn,
const char *question, char **answer,
const char **errmsg)
{
+ const routerinfo_t *ri;
(void) control_conn;
if (!strcmpstart(question, "desc/id/")) {
- routerinfo_t *ri = router_get_by_hexdigest(question+strlen("desc/id/"));
+ ri = router_get_by_hexdigest(question+strlen("desc/id/"));
if (ri) {
const char *body = signed_descriptor_get_body(&ri->cache_info);
if (body)
*answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
}
} else if (!strcmpstart(question, "desc/name/")) {
- routerinfo_t *ri = router_get_by_nickname(question+strlen("desc/name/"),1);
+ ri = router_get_by_nickname(question+strlen("desc/name/"),1);
if (ri) {
const char *body = signed_descriptor_get_body(&ri->cache_info);
if (body)
@@ -1538,7 +1607,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
routerlist_t *routerlist = router_get_routerlist();
smartlist_t *sl = smartlist_create();
if (routerlist && routerlist->routers) {
- SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
+ SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, ri,
{
const char *body = signed_descriptor_get_body(&ri->cache_info);
if (body)
@@ -1554,7 +1623,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
routerlist_t *routerlist = router_get_routerlist();
smartlist_t *sl = smartlist_create();
if (routerlist && routerlist->routers) {
- SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
+ SMARTLIST_FOREACH(routerlist->routers, const routerinfo_t *, ri,
{
const char *body = signed_descriptor_get_body(&ri->cache_info);
signed_descriptor_t *ei = extrainfo_get_by_descriptor_digest(
@@ -1572,8 +1641,8 @@ getinfo_helper_dir(control_connection_t *control_conn,
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
} else if (!strcmpstart(question, "desc-annotations/id/")) {
- routerinfo_t *ri = router_get_by_hexdigest(question+
- strlen("desc-annotations/id/"));
+ ri = router_get_by_hexdigest(question+
+ strlen("desc-annotations/id/"));
if (ri) {
const char *annotations =
signed_descriptor_get_annotations(&ri->cache_info);
@@ -1733,7 +1802,7 @@ getinfo_helper_events(control_connection_t *control_conn,
char buf[256];
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
const char *state;
- edge_connection_t *conn;
+ entry_connection_t *conn;
char *s;
size_t slen;
circuit_t *circ;
@@ -1743,8 +1812,8 @@ getinfo_helper_events(control_connection_t *control_conn,
base_conn->state == AP_CONN_STATE_SOCKS_WAIT ||
base_conn->state == AP_CONN_STATE_NATD_WAIT)
continue;
- conn = TO_EDGE_CONN(base_conn);
- switch (conn->_base.state)
+ conn = TO_ENTRY_CONN(base_conn);
+ switch (base_conn->state)
{
case AP_CONN_STATE_CONTROLLER_WAIT:
case AP_CONN_STATE_CIRCUIT_WAIT:
@@ -1763,17 +1832,17 @@ getinfo_helper_events(control_connection_t *control_conn,
state = "SUCCEEDED"; break;
default:
log_warn(LD_BUG, "Asked for stream in unknown state %d",
- conn->_base.state);
+ base_conn->state);
continue;
}
- circ = circuit_get_by_edge_conn(conn);
+ circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn));
if (circ && CIRCUIT_IS_ORIGIN(circ))
origin_circ = TO_ORIGIN_CIRCUIT(circ);
write_stream_target_to_buf(conn, buf, sizeof(buf));
slen = strlen(buf)+strlen(state)+32;
s = tor_malloc(slen+1);
tor_snprintf(s, slen, "%lu %s %lu %s",
- (unsigned long) conn->_base.global_identifier,state,
+ (unsigned long) base_conn->global_identifier,state,
origin_circ?
(unsigned long)origin_circ->global_identifier : 0ul,
buf);
@@ -1886,7 +1955,7 @@ getinfo_helper_events(control_connection_t *control_conn,
} else if (!strcmp(question, "status/version/num-versioning") ||
!strcmp(question, "status/version/num-concurring")) {
char s[33];
- tor_snprintf(s, sizeof(s), "%d", get_n_authorities(V3_AUTHORITY));
+ tor_snprintf(s, sizeof(s), "%d", get_n_authorities(V3_DIRINFO));
*answer = tor_strdup(s);
log_warn(LD_GENERAL, "%s is deprecated; it no longer gives useful "
"information", question);
@@ -2004,6 +2073,14 @@ static const getinfo_item_t getinfo_items[] = {
"Number of versioning authorities agreeing on the status of the "
"current version"),
ITEM("address", misc, "IP address of this Tor host, if we can guess it."),
+ ITEM("traffic/read", misc,"Bytes read since the process was started."),
+ ITEM("traffic/written", misc,
+ "Bytes written since the process was started."),
+ ITEM("process/pid", misc, "Process id belonging to the main tor process."),
+ ITEM("process/uid", misc, "User id running the tor process."),
+ ITEM("process/user", misc,
+ "Username under which the tor process is running."),
+ ITEM("process/descriptor-limit", misc, "File descriptor limit."),
ITEM("dir-usage", misc, "Breakdown of bytes transferred over DirPort."),
PREFIX("desc-annotations/id/", dir, "Router annotations by hexdigest."),
PREFIX("dir/server/", dir,"Router descriptors as retrieved from a DirPort."),
@@ -2213,7 +2290,7 @@ static int
handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
const char *body)
{
- smartlist_t *router_nicknames=NULL, *routers=NULL;
+ smartlist_t *router_nicknames=NULL, *nodes=NULL;
origin_circuit_t *circ = NULL;
int zero_circ;
uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL;
@@ -2244,8 +2321,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
if ((smartlist_len(args) == 1) ||
(smartlist_len(args) >= 2 && is_keyval_pair(smartlist_get(args, 1)))) {
// "EXTENDCIRCUIT 0" || EXTENDCIRCUIT 0 foo=bar"
- circ = circuit_launch_by_router(intended_purpose, NULL,
- CIRCLAUNCH_NEED_CAPACITY);
+ circ = circuit_launch(intended_purpose, CIRCLAUNCH_NEED_CAPACITY);
if (!circ) {
connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn);
} else {
@@ -2273,17 +2349,21 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
smartlist_free(args);
- routers = smartlist_create();
+ nodes = smartlist_create();
SMARTLIST_FOREACH(router_nicknames, const char *, n,
{
- routerinfo_t *r = router_get_by_nickname(n, 1);
- if (!r) {
+ const node_t *node = node_get_by_nickname(n, 1);
+ if (!node) {
connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n);
goto done;
}
- smartlist_add(routers, r);
+ if (!node_has_descriptor(node)) {
+ connection_printf_to_buf(conn, "552 descriptor for \"%s\"\r\n", n);
+ goto done;
+ }
+ smartlist_add(nodes, (void*)node);
});
- if (!smartlist_len(routers)) {
+ if (!smartlist_len(nodes)) {
connection_write_str_to_buf("512 No router names provided\r\n", conn);
goto done;
}
@@ -2294,9 +2374,10 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
}
/* now circ refers to something that is ready to be extended */
- SMARTLIST_FOREACH(routers, routerinfo_t *, r,
+ SMARTLIST_FOREACH(nodes, const node_t *, node,
{
- extend_info_t *info = extend_info_from_router(r);
+ extend_info_t *info = extend_info_from_node(node);
+ tor_assert(info); /* True, since node_has_descriptor(node) == true */
circuit_append_new_exit(circ, info);
extend_info_free(info);
});
@@ -2330,7 +2411,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
done:
SMARTLIST_FOREACH(router_nicknames, char *, n, tor_free(n));
smartlist_free(router_nicknames);
- smartlist_free(routers);
+ smartlist_free(nodes);
return 0;
}
@@ -2381,7 +2462,7 @@ static int
handle_control_attachstream(control_connection_t *conn, uint32_t len,
const char *body)
{
- edge_connection_t *ap_conn = NULL;
+ entry_connection_t *ap_conn = NULL;
origin_circuit_t *circ = NULL;
int zero_circ;
smartlist_t *args;
@@ -2417,9 +2498,9 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
if (!ap_conn || (!zero_circ && !circ) || !hop_line_ok)
return 0;
- if (ap_conn->_base.state != AP_CONN_STATE_CONTROLLER_WAIT &&
- ap_conn->_base.state != AP_CONN_STATE_CONNECT_WAIT &&
- ap_conn->_base.state != AP_CONN_STATE_RESOLVE_WAIT) {
+ if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT &&
+ ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONNECT_WAIT &&
+ ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_RESOLVE_WAIT) {
connection_write_str_to_buf(
"555 Connection is not managed by controller.\r\n",
conn);
@@ -2427,15 +2508,16 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
}
/* Do we need to detach it first? */
- if (ap_conn->_base.state != AP_CONN_STATE_CONTROLLER_WAIT) {
- circuit_t *tmpcirc = circuit_get_by_edge_conn(ap_conn);
- connection_edge_end(ap_conn, END_STREAM_REASON_TIMEOUT);
+ if (ENTRY_TO_CONN(ap_conn)->state != AP_CONN_STATE_CONTROLLER_WAIT) {
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
+ circuit_t *tmpcirc = circuit_get_by_edge_conn(edge_conn);
+ connection_edge_end(edge_conn, END_STREAM_REASON_TIMEOUT);
/* Un-mark it as ending, since we're going to reuse it. */
- ap_conn->edge_has_sent_end = 0;
- ap_conn->end_reason = 0;
+ edge_conn->edge_has_sent_end = 0;
+ edge_conn->end_reason = 0;
if (tmpcirc)
- circuit_detach_stream(tmpcirc,ap_conn);
- ap_conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
+ circuit_detach_stream(tmpcirc, edge_conn);
+ TO_CONN(edge_conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
}
if (circ && (circ->_base.state != CIRCUIT_STATE_OPEN)) {
@@ -2446,16 +2528,17 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
}
/* Is this a single hop circuit? */
if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) {
- routerinfo_t *r = NULL;
- char* exit_digest;
+ const node_t *node = NULL;
+ char *exit_digest;
if (circ->build_state &&
circ->build_state->chosen_exit &&
!tor_digest_is_zero(circ->build_state->chosen_exit->identity_digest)) {
exit_digest = circ->build_state->chosen_exit->identity_digest;
- r = router_get_by_digest(exit_digest);
+ node = node_get_by_id(exit_digest);
}
/* Do both the client and relay allow one-hop exit circuits? */
- if (!r || !r->allow_single_hop_exits ||
+ if (!node ||
+ !node_allows_single_hop_exits(node) ||
!get_options()->AllowSingleHopCircuits) {
connection_write_str_to_buf(
"551 Can't attach stream to this one-hop circuit.\r\n", conn);
@@ -2556,7 +2639,7 @@ static int
handle_control_redirectstream(control_connection_t *conn, uint32_t len,
const char *body)
{
- edge_connection_t *ap_conn = NULL;
+ entry_connection_t *ap_conn = NULL;
char *new_addr = NULL;
uint16_t new_port = 0;
smartlist_t *args;
@@ -2604,7 +2687,7 @@ static int
handle_control_closestream(control_connection_t *conn, uint32_t len,
const char *body)
{
- edge_connection_t *ap_conn=NULL;
+ entry_connection_t *ap_conn=NULL;
uint8_t reason=0;
smartlist_t *args;
int ok;
@@ -2749,7 +2832,7 @@ handle_control_protocolinfo(control_connection_t *conn, uint32_t len,
connection_mark_for_close(TO_CONN(conn));
goto done;
} else {
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int cookies = options->CookieAuthentication;
char *cfile = get_cookie_file();
char *esc_cfile = esc_for_log(cfile);
@@ -2827,8 +2910,6 @@ int
connection_control_finished_flushing(control_connection_t *conn)
{
tor_assert(conn);
-
- connection_stop_writing(TO_CONN(conn));
return 0;
}
@@ -2900,6 +2981,17 @@ is_valid_initial_command(control_connection_t *conn, const char *cmd)
* interfaces is broken. */
#define MAX_COMMAND_LINE_LENGTH (1024*1024)
+static int
+peek_connection_has_control0_command(connection_t *conn)
+{
+ IF_HAS_BUFFEREVENT(conn, {
+ struct evbuffer *input = bufferevent_get_input(conn->bufev);
+ return peek_evbuffer_has_control0_command(input);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ return peek_buf_has_control0_command(conn->inbuf);
+ }
+}
+
/** Called when data has arrived on a v1 control connection: Try to fetch
* commands from conn->inbuf, and execute them.
*/
@@ -2922,7 +3014,7 @@ connection_control_process_inbuf(control_connection_t *conn)
}
if (conn->_base.state == CONTROL_CONN_STATE_NEEDAUTH &&
- peek_buf_has_control0_command(conn->_base.inbuf)) {
+ peek_connection_has_control0_command(TO_CONN(conn))) {
/* Detect v0 commands and send a "no more v0" message. */
size_t body_len;
char buf[128];
@@ -2934,8 +3026,8 @@ connection_control_process_inbuf(control_connection_t *conn)
body_len = 2+strlen(buf+6)+2; /* code, msg, nul. */
set_uint16(buf+0, htons(body_len));
connection_write_to_buf(buf, 4+body_len, TO_CONN(conn));
- connection_mark_for_close(TO_CONN(conn));
- conn->_base.hold_open_until_flushed = 1;
+
+ connection_mark_and_flush(TO_CONN(conn));
return 0;
}
@@ -2946,7 +3038,7 @@ connection_control_process_inbuf(control_connection_t *conn)
/* First, fetch a line. */
do {
data_len = conn->incoming_cmd_len - conn->incoming_cmd_cur_len;
- r = fetch_from_buf_line(conn->_base.inbuf,
+ r = connection_fetch_from_buf_line(TO_CONN(conn),
conn->incoming_cmd+conn->incoming_cmd_cur_len,
&data_len);
if (r == 0)
@@ -2956,8 +3048,7 @@ connection_control_process_inbuf(control_connection_t *conn)
if (data_len + conn->incoming_cmd_cur_len > MAX_COMMAND_LINE_LENGTH) {
connection_write_str_to_buf("500 Line too long.\r\n", conn);
connection_stop_reading(TO_CONN(conn));
- connection_mark_for_close(TO_CONN(conn));
- conn->_base.hold_open_until_flushed = 1;
+ connection_mark_and_flush(TO_CONN(conn));
}
while (conn->incoming_cmd_len < data_len+conn->incoming_cmd_cur_len)
conn->incoming_cmd_len *= 2;
@@ -3017,8 +3108,7 @@ connection_control_process_inbuf(control_connection_t *conn)
/* Otherwise, Quit is always valid. */
if (!strcasecmp(conn->incoming_cmd, "QUIT")) {
connection_write_str_to_buf("250 closing connection\r\n", conn);
- connection_mark_for_close(TO_CONN(conn));
- conn->_base.hold_open_until_flushed = 1;
+ connection_mark_and_flush(TO_CONN(conn));
return 0;
}
@@ -3177,7 +3267,7 @@ control_event_circuit_status(origin_circuit_t *circ, circuit_status_event_t tp,
* <b>conn</b>, and write it to <b>buf</b>. Return 0 on success, -1 on
* failure. */
static int
-write_stream_target_to_buf(edge_connection_t *conn, char *buf, size_t len)
+write_stream_target_to_buf(entry_connection_t *conn, char *buf, size_t len)
{
char buf2[256];
if (conn->chosen_exit_name)
@@ -3188,8 +3278,8 @@ write_stream_target_to_buf(edge_connection_t *conn, char *buf, size_t len)
if (tor_snprintf(buf, len, "%s%s%s:%d",
conn->socks_request->address,
conn->chosen_exit_name ? buf2 : "",
- !conn->chosen_exit_name &&
- connection_edge_is_rendezvous_stream(conn) ? ".onion" : "",
+ !conn->chosen_exit_name && connection_edge_is_rendezvous_stream(
+ ENTRY_TO_EDGE_CONN(conn)) ? ".onion" : "",
conn->socks_request->port)<0)
return -1;
return 0;
@@ -3198,7 +3288,7 @@ write_stream_target_to_buf(edge_connection_t *conn, char *buf, size_t len)
/** Something has happened to the stream associated with AP connection
* <b>conn</b>: tell any interested control connections. */
int
-control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
+control_event_stream_status(entry_connection_t *conn, stream_status_event_t tp,
int reason_code)
{
char reason_buf[64];
@@ -3270,7 +3360,7 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
if (tp == STREAM_EVENT_NEW) {
tor_snprintf(addrport_buf,sizeof(addrport_buf), " SOURCE_ADDR=%s:%d",
- TO_CONN(conn)->address, TO_CONN(conn)->port );
+ ENTRY_TO_CONN(conn)->address, ENTRY_TO_CONN(conn)->port);
} else {
addrport_buf[0] = '\0';
}
@@ -3278,12 +3368,12 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
if (tp == STREAM_EVENT_NEW_RESOLVE) {
purpose = " PURPOSE=DNS_REQUEST";
} else if (tp == STREAM_EVENT_NEW) {
- if (conn->is_dns_request ||
+ if (ENTRY_TO_EDGE_CONN(conn)->is_dns_request ||
(conn->socks_request &&
SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)))
purpose = " PURPOSE=DNS_REQUEST";
else if (conn->use_begindir) {
- connection_t *linked = TO_CONN(conn)->linked_conn;
+ connection_t *linked = ENTRY_TO_CONN(conn)->linked_conn;
int linked_dir_purpose = -1;
if (linked && linked->type == CONN_TYPE_DIR)
linked_dir_purpose = linked->purpose;
@@ -3295,12 +3385,13 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
purpose = " PURPOSE=USER";
}
- circ = circuit_get_by_edge_conn(conn);
+ circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn));
if (circ && CIRCUIT_IS_ORIGIN(circ))
origin_circ = TO_ORIGIN_CIRCUIT(circ);
send_control_event(EVENT_STREAM_STATUS, ALL_FORMATS,
"650 STREAM "U64_FORMAT" %s %lu %s%s%s%s\r\n",
- U64_PRINTF_ARG(conn->_base.global_identifier), status,
+ U64_PRINTF_ARG(ENTRY_TO_CONN(conn)->global_identifier),
+ status,
origin_circ?
(unsigned long)origin_circ->global_identifier : 0ul,
buf, reason_buf, addrport_buf, purpose);
@@ -3316,10 +3407,10 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
static void
orconn_target_get_name(char *name, size_t len, or_connection_t *conn)
{
- routerinfo_t *ri = router_get_by_digest(conn->identity_digest);
- if (ri) {
+ const node_t *node = node_get_by_id(conn->identity_digest);
+ if (node) {
tor_assert(len > MAX_VERBOSE_NICKNAME_LEN);
- router_get_verbose_nickname(name, ri);
+ node_get_verbose_nickname(node, name);
} else if (! tor_digest_is_zero(conn->identity_digest)) {
name[0] = '$';
base16_encode(name+1, len-1, conn->identity_digest,
@@ -3636,7 +3727,7 @@ control_event_networkstatus_changed_helper(smartlist_t *statuses,
smartlist_add(strs, tor_strdup("650+"));
smartlist_add(strs, tor_strdup(event_string));
smartlist_add(strs, tor_strdup("\r\n"));
- SMARTLIST_FOREACH(statuses, routerstatus_t *, rs,
+ SMARTLIST_FOREACH(statuses, const routerstatus_t *, rs,
{
s = networkstatus_getinfo_helper_single(rs);
if (!s) continue;
@@ -3724,10 +3815,46 @@ control_event_buildtimeout_set(const circuit_build_times_t *cbt,
return 0;
}
+/** Called when a signal has been processed from signal_callback */
+int
+control_event_signal(uintptr_t signal)
+{
+ const char *signal_string = NULL;
+
+ if (!control_event_is_interesting(EVENT_SIGNAL))
+ return 0;
+
+ switch (signal) {
+ case SIGHUP:
+ signal_string = "RELOAD";
+ break;
+ case SIGUSR1:
+ signal_string = "DUMP";
+ break;
+ case SIGUSR2:
+ signal_string = "DEBUG";
+ break;
+ case SIGNEWNYM:
+ signal_string = "NEWNYM";
+ break;
+ case SIGCLEARDNSCACHE:
+ signal_string = "CLEARDNSCACHE";
+ break;
+ default:
+ log_warn(LD_BUG, "Unrecognized signal %lu in control_event_signal",
+ (unsigned long)signal);
+ return -1;
+ }
+
+ send_control_event(EVENT_SIGNAL, ALL_FORMATS, "650 SIGNAL %s\r\n",
+ signal_string);
+ return 0;
+}
+
/** Called when a single local_routerstatus_t has changed: Sends an NS event
* to any controller that cares. */
int
-control_event_networkstatus_changed_single(routerstatus_t *rs)
+control_event_networkstatus_changed_single(const routerstatus_t *rs)
{
smartlist_t *statuses;
int r;
@@ -3736,7 +3863,7 @@ control_event_networkstatus_changed_single(routerstatus_t *rs)
return 0;
statuses = smartlist_create();
- smartlist_add(statuses, rs);
+ smartlist_add(statuses, (void*)rs);
r = control_event_networkstatus_changed(statuses);
smartlist_free(statuses);
return r;
@@ -3861,9 +3988,9 @@ control_event_guard(const char *nickname, const char *digest,
{
char buf[MAX_VERBOSE_NICKNAME_LEN+1];
- routerinfo_t *ri = router_get_by_digest(digest);
- if (ri) {
- router_get_verbose_nickname(buf, ri);
+ const node_t *node = node_get_by_id(digest);
+ if (node) {
+ node_get_verbose_nickname(node, buf);
} else {
tor_snprintf(buf, sizeof(buf), "$%s~%s", hbuf, nickname);
}
@@ -3873,12 +4000,45 @@ control_event_guard(const char *nickname, const char *digest,
return 0;
}
+/** Called when a configuration option changes. This is generally triggered
+ * by SETCONF requests and RELOAD/SIGHUP signals. The <b>elements</b> is
+ * a smartlist_t containing (key, value, ...) pairs in sequence.
+ * <b>value</b> can be NULL. */
+int
+control_event_conf_changed(smartlist_t *elements)
+{
+ int i;
+ char *result;
+ smartlist_t *lines;
+ if (!EVENT_IS_INTERESTING(EVENT_CONF_CHANGED) ||
+ smartlist_len(elements) == 0) {
+ return 0;
+ }
+ lines = smartlist_create();
+ for (i = 0; i < smartlist_len(elements); i += 2) {
+ char *k = smartlist_get(elements, i);
+ char *v = smartlist_get(elements, i+1);
+ if (v == NULL) {
+ smartlist_asprintf_add(lines, "650-%s", k);
+ } else {
+ smartlist_asprintf_add(lines, "650-%s=%s", k, v);
+ }
+ }
+ result = smartlist_join_strings(lines, "\r\n", 0, NULL);
+ send_control_event(EVENT_CONF_CHANGED, 0,
+ "650-CONF_CHANGED\r\n%s\r\n650 OK\r\n", result);
+ tor_free(result);
+ SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
+ smartlist_free(lines);
+ return 0;
+}
+
/** Helper: Return a newly allocated string containing a path to the
* file where we store our authentication cookie. */
static char *
get_cookie_file(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (options->CookieAuthFile && strlen(options->CookieAuthFile)) {
return tor_strdup(options->CookieAuthFile);
} else {
@@ -4146,6 +4306,7 @@ control_event_bootstrap_problem(const char *warn, int reason)
const char *tag, *summary;
char buf[BOOTSTRAP_MSG_LEN];
const char *recommendation = "ignore";
+ int severity;
/* bootstrap_percent must not be in "undefined" state here. */
tor_assert(status >= 0);
@@ -4170,12 +4331,17 @@ control_event_bootstrap_problem(const char *warn, int reason)
status--; /* find a recognized status string based on current progress */
status = bootstrap_percent; /* set status back to the actual number */
- log_fn(!strcmp(recommendation, "warn") ? LOG_WARN : LOG_INFO,
+ severity = !strcmp(recommendation, "warn") ? LOG_WARN : LOG_INFO;
+
+ log_fn(severity,
LD_CONTROL, "Problem bootstrapping. Stuck at %d%%: %s. (%s; %s; "
"count %d; recommendation %s)",
status, summary, warn,
orconn_end_reason_to_control_string(reason),
bootstrap_problems, recommendation);
+
+ connection_or_report_broken_states(severity, LD_HANDSHAKE);
+
tor_snprintf(buf, sizeof(buf),
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\" WARNING=\"%s\" REASON=%s "
"COUNT=%d RECOMMENDATION=%s",
diff --git a/src/or/control.h b/src/or/control.h
index ddea4cd54..0d9acd26e 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -37,7 +37,7 @@ int control_event_is_interesting(int event);
int control_event_circuit_status(origin_circuit_t *circ,
circuit_status_event_t e, int reason);
-int control_event_stream_status(edge_connection_t *conn,
+int control_event_stream_status(entry_connection_t *conn,
stream_status_event_t e,
int reason);
int control_event_or_conn_status(or_connection_t *conn,
@@ -57,7 +57,7 @@ int control_event_my_descriptor_changed(void);
int control_event_networkstatus_changed(smartlist_t *statuses);
int control_event_newconsensus(const networkstatus_t *consensus);
-int control_event_networkstatus_changed_single(routerstatus_t *rs);
+int control_event_networkstatus_changed_single(const routerstatus_t *rs);
int control_event_general_status(int severity, const char *format, ...)
CHECK_PRINTF(2,3);
int control_event_client_status(int severity, const char *format, ...)
@@ -66,8 +66,10 @@ int control_event_server_status(int severity, const char *format, ...)
CHECK_PRINTF(2,3);
int control_event_guard(const char *nickname, const char *digest,
const char *status);
+int control_event_conf_changed(smartlist_t *elements);
int control_event_buildtimeout_set(const circuit_build_times_t *cbt,
buildtimeout_set_event_t type);
+int control_event_signal(uintptr_t signal);
int init_cookie_authentication(int enabled);
smartlist_t *decode_hashed_passwords(config_line_t *passwords);
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index c5e4863f7..bf8964c29 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -62,7 +62,6 @@ connection_cpu_finished_flushing(connection_t *conn)
{
tor_assert(conn);
tor_assert(conn->type == CONN_TYPE_CPUWORKER);
- connection_stop_writing(conn);
return 0;
}
@@ -141,13 +140,13 @@ connection_cpu_process_inbuf(connection_t *conn)
tor_assert(conn);
tor_assert(conn->type == CONN_TYPE_CPUWORKER);
- if (!buf_datalen(conn->inbuf))
+ if (!connection_get_inbuf_len(conn))
return 0;
if (conn->state == CPUWORKER_STATE_BUSY_ONION) {
- if (buf_datalen(conn->inbuf) < LEN_ONION_RESPONSE) /* answer available? */
+ if (connection_get_inbuf_len(conn) < LEN_ONION_RESPONSE)
return 0; /* not yet */
- tor_assert(buf_datalen(conn->inbuf) == LEN_ONION_RESPONSE);
+ tor_assert(connection_get_inbuf_len(conn) == LEN_ONION_RESPONSE);
connection_fetch_from_buf(&success,1,conn);
connection_fetch_from_buf(buf,LEN_ONION_RESPONSE-1,conn);
@@ -367,7 +366,7 @@ spawn_cpuworker(void)
static void
spawn_enough_cpuworkers(void)
{
- int num_cpuworkers_needed = get_options()->NumCPUs;
+ int num_cpuworkers_needed = get_num_cpus(get_options());
if (num_cpuworkers_needed < MIN_CPUWORKERS)
num_cpuworkers_needed = MIN_CPUWORKERS;
diff --git a/src/or/directory.c b/src/or/directory.c
index e3cc70f91..776b7a25f 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -15,7 +15,9 @@
#include "dirvote.h"
#include "geoip.h"
#include "main.h"
+#include "microdesc.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "policies.h"
#include "rendclient.h"
#include "rendcommon.h"
@@ -78,6 +80,8 @@ static void dir_routerdesc_download_failed(smartlist_t *failed,
int router_purpose,
int was_extrainfo,
int was_descriptor_digests);
+static void dir_microdesc_download_failed(smartlist_t *failed,
+ int status_code);
static void note_client_request(int purpose, int compressed, size_t bytes);
static int client_likes_consensus(networkstatus_t *v, const char *want_url);
@@ -137,26 +141,28 @@ purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose)
dir_purpose == DIR_PURPOSE_FETCH_CONSENSUS ||
dir_purpose == DIR_PURPOSE_FETCH_CERTIFICATE ||
dir_purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
- dir_purpose == DIR_PURPOSE_FETCH_EXTRAINFO)
+ dir_purpose == DIR_PURPOSE_FETCH_EXTRAINFO ||
+ dir_purpose == DIR_PURPOSE_FETCH_MICRODESC)
return 0;
return 1;
}
-/** Return a newly allocated string describing <b>auth</b>. */
-char *
-authority_type_to_string(authority_type_t auth)
+/** Return a newly allocated string describing <b>auth</b>. Only describes
+ * authority features. */
+static char *
+authdir_type_to_string(dirinfo_type_t auth)
{
char *result;
smartlist_t *lst = smartlist_create();
- if (auth & V1_AUTHORITY)
+ if (auth & V1_DIRINFO)
smartlist_add(lst, (void*)"V1");
- if (auth & V2_AUTHORITY)
+ if (auth & V2_DIRINFO)
smartlist_add(lst, (void*)"V2");
- if (auth & V3_AUTHORITY)
+ if (auth & V3_DIRINFO)
smartlist_add(lst, (void*)"V3");
- if (auth & BRIDGE_AUTHORITY)
+ if (auth & BRIDGE_DIRINFO)
smartlist_add(lst, (void*)"Bridge");
- if (auth & HIDSERV_AUTHORITY)
+ if (auth & HIDSERV_DIRINFO)
smartlist_add(lst, (void*)"Hidden service");
if (smartlist_len(lst)) {
result = smartlist_join_strings(lst, ", ", 0, NULL);
@@ -201,6 +207,8 @@ dir_conn_purpose_to_string(int purpose)
return "hidden-service v2 descriptor fetch";
case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
return "hidden-service v2 descriptor upload";
+ case DIR_PURPOSE_FETCH_MICRODESC:
+ return "microdescriptor fetch";
}
log_warn(LD_BUG, "Called with unknown purpose %d", purpose);
@@ -213,17 +221,19 @@ dir_conn_purpose_to_string(int purpose)
int
router_supports_extrainfo(const char *identity_digest, int is_authority)
{
- routerinfo_t *ri = router_get_by_digest(identity_digest);
+ const node_t *node = node_get_by_id(identity_digest);
- if (ri) {
- if (ri->caches_extra_info)
+ if (node && node->ri) {
+ if (node->ri->caches_extra_info)
return 1;
- if (is_authority && ri->platform &&
- tor_version_as_new_as(ri->platform, "Tor 0.2.0.0-alpha-dev (r10070)"))
+ if (is_authority && node->ri->platform &&
+ tor_version_as_new_as(node->ri->platform,
+ "Tor 0.2.0.0-alpha-dev (r10070)"))
return 1;
}
if (is_authority) {
- routerstatus_t *rs = router_get_consensus_status_by_id(identity_digest);
+ const routerstatus_t *rs =
+ router_get_consensus_status_by_id(identity_digest);
if (rs && rs->version_supports_extrainfo_upload)
return 1;
}
@@ -242,7 +252,7 @@ int
directories_have_accepted_server_descriptor(void)
{
smartlist_t *servers = router_get_trusted_dir_servers();
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
SMARTLIST_FOREACH(servers, trusted_dir_server_t *, d, {
if ((d->type & options->_PublishServerDescriptor) &&
d->has_accepted_serverdesc) {
@@ -271,11 +281,11 @@ directories_have_accepted_server_descriptor(void)
*/
void
directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
- authority_type_t type,
+ dirinfo_type_t type,
const char *payload,
size_t payload_len, size_t extrainfo_len)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int post_via_tor;
smartlist_t *dirservers = router_get_trusted_dir_servers();
int found = 0;
@@ -296,8 +306,8 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
if (exclude_self && router_digest_is_me(ds->digest))
continue;
- if (options->ExcludeNodes && options->StrictNodes &&
- routerset_contains_routerstatus(options->ExcludeNodes, rs)) {
+ if (options->StrictNodes &&
+ routerset_contains_routerstatus(options->ExcludeNodes, rs, -1)) {
log_warn(LD_DIR, "Wanted to contact authority '%s' for %s, but "
"it's in our ExcludedNodes list and StrictNodes is set. "
"Skipping.",
@@ -324,7 +334,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
NULL, payload, upload_len, 0);
} SMARTLIST_FOREACH_END(ds);
if (!found) {
- char *s = authority_type_to_string(type);
+ char *s = authdir_type_to_string(type);
log_warn(LD_DIR, "Publishing server descriptor to directory authorities "
"of type '%s', but no authorities of that type listed!", s);
tor_free(s);
@@ -341,39 +351,44 @@ void
directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
const char *resource, int pds_flags)
{
- routerstatus_t *rs = NULL;
- or_options_t *options = get_options();
+ const routerstatus_t *rs = NULL;
+ const or_options_t *options = get_options();
int prefer_authority = directory_fetches_from_authorities(options);
int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose);
- authority_type_t type;
+ dirinfo_type_t type;
time_t if_modified_since = 0;
/* FFFF we could break this switch into its own function, and call
* it elsewhere in directory.c. -RD */
switch (dir_purpose) {
case DIR_PURPOSE_FETCH_EXTRAINFO:
- type = EXTRAINFO_CACHE |
- (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY :
- V3_AUTHORITY);
+ type = EXTRAINFO_DIRINFO |
+ (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_DIRINFO :
+ V3_DIRINFO);
break;
case DIR_PURPOSE_FETCH_V2_NETWORKSTATUS:
- type = V2_AUTHORITY;
+ type = V2_DIRINFO;
prefer_authority = 1; /* Only v2 authorities have these anyway. */
break;
case DIR_PURPOSE_FETCH_SERVERDESC:
- type = (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_AUTHORITY :
- V3_AUTHORITY);
+ type = (router_purpose == ROUTER_PURPOSE_BRIDGE ? BRIDGE_DIRINFO :
+ V3_DIRINFO);
break;
case DIR_PURPOSE_FETCH_RENDDESC:
- type = HIDSERV_AUTHORITY;
+ type = HIDSERV_DIRINFO;
break;
case DIR_PURPOSE_FETCH_STATUS_VOTE:
case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES:
- type = V3_AUTHORITY;
+ case DIR_PURPOSE_FETCH_CERTIFICATE:
+ type = V3_DIRINFO;
break;
case DIR_PURPOSE_FETCH_CONSENSUS:
- case DIR_PURPOSE_FETCH_CERTIFICATE:
- type = V3_AUTHORITY;
+ type = V3_DIRINFO;
+ if (resource && !strcmp(resource,"microdesc"))
+ type |= MICRODESC_DIRINFO;
+ break;
+ case DIR_PURPOSE_FETCH_MICRODESC:
+ type = MICRODESC_DIRINFO;
break;
default:
log_warn(LD_BUG, "Unexpected purpose %d", (int)dir_purpose);
@@ -381,25 +396,42 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
}
if (dir_purpose == DIR_PURPOSE_FETCH_CONSENSUS) {
- networkstatus_t *v = networkstatus_get_latest_consensus();
- if (v)
- if_modified_since = v->valid_after + 180;
+ int flav = FLAV_NS;
+ networkstatus_t *v;
+ if (resource)
+ flav = networkstatus_parse_flavor_name(resource);
+
+ if (flav != -1) {
+ /* IF we have a parsed consensus of this type, we can do an
+ * if-modified-time based on it. */
+ v = networkstatus_get_latest_consensus_by_flavor(flav);
+ if (v)
+ if_modified_since = v->valid_after + 180;
+ } else {
+ /* Otherwise it might be a consensus we don't parse, but which we
+ * do cache. Look at the cached copy, perhaps. */
+ cached_dir_t *cd = dirserv_get_consensus(resource ? resource : "ns");
+ if (cd)
+ if_modified_since = cd->published + 180;
+ }
}
- if (!options->FetchServerDescriptors && type != HIDSERV_AUTHORITY)
+ if (!options->FetchServerDescriptors && type != HIDSERV_DIRINFO)
return;
if (!get_via_tor) {
- if (options->UseBridges && type != BRIDGE_AUTHORITY) {
+ if (options->UseBridges && type != BRIDGE_DIRINFO) {
/* want to ask a running bridge for which we have a descriptor. */
/* XXX023 we assume that all of our bridges can answer any
* possible directory question. This won't be true forever. -RD */
/* It certainly is not true with conditional consensus downloading,
* so, for now, never assume the server supports that. */
- routerinfo_t *ri = choose_random_entry(NULL);
- if (ri) {
+ const node_t *node = choose_random_entry(NULL);
+ if (node && node->ri) {
+ /* every bridge has a routerinfo. */
tor_addr_t addr;
- tor_addr_from_ipv4h(&addr, ri->addr);
+ routerinfo_t *ri = node->ri;
+ node_get_addr(node, &addr);
directory_initiate_command(ri->address, &addr,
ri->or_port, 0,
0, /* don't use conditional consensus url */
@@ -412,10 +444,11 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
"nodes are available yet.");
return;
} else {
- if (prefer_authority || type == BRIDGE_AUTHORITY) {
+ if (prefer_authority || type == BRIDGE_DIRINFO) {
/* only ask authdirservers, and don't ask myself */
rs = router_pick_trusteddirserver(type, pds_flags);
- if (rs == NULL && (pds_flags & PDS_NO_EXISTING_SERVERDESC_FETCH)) {
+ if (rs == NULL && (pds_flags & (PDS_NO_EXISTING_SERVERDESC_FETCH|
+ PDS_NO_EXISTING_MICRODESC_FETCH))) {
/* We don't want to fetch from any authorities that we're currently
* fetching server descriptors from, and we got no match. Did we
* get no match because all the authorities have connections
@@ -423,7 +456,8 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
* return,) or because all the authorities are down or on fire or
* unreachable or something (in which case we should go on with
* our fallback code)? */
- pds_flags &= ~PDS_NO_EXISTING_SERVERDESC_FETCH;
+ pds_flags &= ~(PDS_NO_EXISTING_SERVERDESC_FETCH|
+ PDS_NO_EXISTING_MICRODESC_FETCH);
rs = router_pick_trusteddirserver(type, pds_flags);
if (rs) {
log_debug(LD_DIR, "Deferring serverdesc fetch: all authorities "
@@ -432,7 +466,7 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
}
}
}
- if (!rs && type != BRIDGE_AUTHORITY) {
+ if (!rs && type != BRIDGE_DIRINFO) {
/* anybody with a non-zero dirport will do */
rs = router_pick_directory_server(type, pds_flags);
if (!rs) {
@@ -449,7 +483,7 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
if (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC) {
/* only ask hidserv authorities, any of them will do */
pds_flags |= PDS_IGNORE_FASCISTFIREWALL|PDS_ALLOW_SELF;
- rs = router_pick_trusteddirserver(HIDSERV_AUTHORITY, pds_flags);
+ rs = router_pick_trusteddirserver(HIDSERV_DIRINFO, pds_flags);
} else {
/* anybody with a non-zero dirport will do. Disregard firewalls. */
pds_flags |= PDS_IGNORE_FASCISTFIREWALL;
@@ -495,7 +529,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose,
routerstatus_t *rs;
if (router_digest_is_me(ds->digest))
continue;
- if (!(ds->type & V3_AUTHORITY))
+ if (!(ds->type & V3_DIRINFO))
continue;
rs = &ds->fake_status;
directory_initiate_command_routerstatus(rs, dir_purpose, router_purpose,
@@ -506,7 +540,7 @@ directory_get_from_all_authorities(uint8_t dir_purpose,
/** Same as directory_initiate_command_routerstatus(), but accepts
* rendezvous data to fetch a hidden service descriptor. */
void
-directory_initiate_command_routerstatus_rend(routerstatus_t *status,
+directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
uint8_t dir_purpose,
uint8_t router_purpose,
int anonymized_connection,
@@ -516,21 +550,22 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status,
time_t if_modified_since,
const rend_data_t *rend_query)
{
- or_options_t *options = get_options();
- routerinfo_t *router;
+ const or_options_t *options = get_options();
+ const node_t *node;
char address_buf[INET_NTOA_BUF_LEN+1];
struct in_addr in;
const char *address;
tor_addr_t addr;
- router = router_get_by_digest(status->identity_digest);
+ node = node_get_by_id(status->identity_digest);
- if (!router && anonymized_connection) {
- log_info(LD_DIR, "Not sending anonymized request to directory %s; we "
+ if (!node && anonymized_connection) {
+ log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we "
"don't have its router descriptor.",
routerstatus_describe(status));
return;
- } else if (router) {
- address = router->address;
+ } else if (node) {
+ node_get_address_string(node, address_buf, sizeof(address_buf));
+ address = address_buf;
} else {
in.s_addr = htonl(status->addr);
tor_inet_ntoa(&in, address_buf, sizeof(address_buf));
@@ -539,7 +574,7 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status,
tor_addr_from_ipv4h(&addr, status->addr);
if (options->ExcludeNodes && options->StrictNodes &&
- routerset_contains_routerstatus(options->ExcludeNodes, status)) {
+ routerset_contains_routerstatus(options->ExcludeNodes, status, -1)) {
log_warn(LD_DIR, "Wanted to contact directory mirror %s for %s, but "
"it's in our ExcludedNodes list and StrictNodes is set. "
"Skipping. This choice might make your Tor not work.",
@@ -574,7 +609,7 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status,
* want to fetch.
*/
void
-directory_initiate_command_routerstatus(routerstatus_t *status,
+directory_initiate_command_routerstatus(const routerstatus_t *status,
uint8_t dir_purpose,
uint8_t router_purpose,
int anonymized_connection,
@@ -598,7 +633,7 @@ directory_conn_is_self_reachability_test(dir_connection_t *conn)
{
if (conn->requested_resource &&
!strcmpstart(conn->requested_resource,"authority")) {
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
if (me &&
router_digest_is_me(conn->identity_digest) &&
tor_addr_eq_ipv4h(&conn->_base.addr, me->addr) && /*XXXX prop 118*/
@@ -612,7 +647,7 @@ directory_conn_is_self_reachability_test(dir_connection_t *conn)
* server due to a network error: Mark the router as down and try again if
* possible.
*/
-void
+static void
connection_dir_request_failed(dir_connection_t *conn)
{
if (directory_conn_is_self_reachability_test(conn)) {
@@ -626,15 +661,18 @@ connection_dir_request_failed(dir_connection_t *conn)
connection_dir_download_v2_networkstatus_failed(conn, -1);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) {
- log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
+ log_info(LD_DIR, "Giving up on serverdesc/extrainfo fetch from "
+ "directory server at '%s'; retrying",
conn->_base.address);
if (conn->router_purpose == ROUTER_PURPOSE_BRIDGE)
connection_dir_bridge_routerdesc_failed(conn);
connection_dir_download_routerdesc_failed(conn);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_CONSENSUS) {
- networkstatus_consensus_download_failed(0);
+ if (conn->requested_resource)
+ networkstatus_consensus_download_failed(0, conn->requested_resource);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_CERTIFICATE) {
- log_info(LD_DIR, "Giving up on directory server at '%s'; retrying",
+ log_info(LD_DIR, "Giving up on certificate fetch from directory server "
+ "at '%s'; retrying",
conn->_base.address);
connection_dir_download_cert_failed(conn, 0);
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES) {
@@ -643,6 +681,10 @@ connection_dir_request_failed(dir_connection_t *conn)
} else if (conn->_base.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) {
log_info(LD_DIR, "Giving up downloading votes from '%s'",
conn->_base.address);
+ } else if (conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC) {
+ log_info(LD_DIR, "Giving up on downloading microdescriptors from "
+ " directory server at '%s'; will retry", conn->_base.address);
+ connection_dir_download_routerdesc_failed(conn);
}
}
@@ -713,7 +755,8 @@ connection_dir_download_routerdesc_failed(dir_connection_t *conn)
/* No need to relaunch descriptor downloads here: we already do it
* every 10 or 60 seconds (FOO_DESCRIPTOR_RETRY_INTERVAL) in main.c. */
tor_assert(conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
- conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO);
+ conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO ||
+ conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC);
(void) conn;
}
@@ -776,7 +819,7 @@ connection_dir_download_cert_failed(dir_connection_t *conn, int status)
* 3) Else yes.
*/
static int
-directory_command_should_use_begindir(or_options_t *options,
+directory_command_should_use_begindir(const or_options_t *options,
const tor_addr_t *addr,
int or_port, uint8_t router_purpose,
int anonymized_connection)
@@ -816,6 +859,20 @@ directory_initiate_command(const char *address, const tor_addr_t *_addr,
if_modified_since, NULL);
}
+/** Return non-zero iff a directory connection with purpose
+ * <b>dir_purpose</b> reveals sensitive information about a Tor
+ * instance's client activities. (Such connections must be performed
+ * through normal three-hop Tor circuits.) */
+static int
+is_sensitive_dir_purpose(uint8_t dir_purpose)
+{
+ return ((dir_purpose == DIR_PURPOSE_FETCH_RENDDESC) ||
+ (dir_purpose == DIR_PURPOSE_HAS_FETCHED_RENDDESC) ||
+ (dir_purpose == DIR_PURPOSE_UPLOAD_RENDDESC) ||
+ (dir_purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) ||
+ (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC_V2));
+}
+
/** Same as directory_initiate_command(), but accepts rendezvous data to
* fetch a hidden service descriptor. */
static void
@@ -831,7 +888,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
const rend_data_t *rend_query)
{
dir_connection_t *conn;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int socket_error = 0;
int use_begindir = supports_begindir &&
directory_command_should_use_begindir(options, _addr,
@@ -850,6 +907,9 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
+ tor_assert(!(is_sensitive_dir_purpose(dir_purpose) &&
+ !anonymized_connection));
+
/* ensure that we don't make direct connections when a SOCKS server is
* configured. */
if (!anonymized_connection && !use_begindir && !options->HTTPProxy &&
@@ -911,7 +971,11 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
error indicates broken link in windowsland. */
}
} else { /* we want to connect via a tor connection */
- edge_connection_t *linked_conn;
+ entry_connection_t *linked_conn;
+ /* Anonymized tunneled connections can never share a circuit.
+ * One-hop directory connections can share circuits with each other
+ * but nothing else. */
+ int iso_flags = anonymized_connection ? ISO_STREAM : ISO_SESSIONGRP;
/* If it's an anonymized connection, remember the fact that we
* wanted it for later: maybe we'll want it again soon. */
@@ -925,14 +989,16 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
* hook up both sides
*/
linked_conn =
- connection_ap_make_link(conn->_base.address, conn->_base.port,
- digest, use_begindir, conn->dirconn_direct);
+ connection_ap_make_link(TO_CONN(conn),
+ conn->_base.address, conn->_base.port,
+ digest,
+ SESSION_GROUP_DIRCONN, iso_flags,
+ use_begindir, conn->dirconn_direct);
if (!linked_conn) {
log_warn(LD_NET,"Making tunnel to dirserver failed.");
connection_mark_for_close(TO_CONN(conn));
return;
}
- connection_link_connections(TO_CONN(conn), TO_CONN(linked_conn));
if (connection_add(TO_CONN(conn)) < 0) {
log_warn(LD_NET,"Unable to add connection for link to dirserver.");
@@ -945,8 +1011,13 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
payload, payload_len,
supports_conditional_consensus,
if_modified_since);
+
connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
- connection_start_reading(TO_CONN(linked_conn));
+ IF_HAS_BUFFEREVENT(ENTRY_TO_CONN(linked_conn), {
+ connection_watch_events(ENTRY_TO_CONN(linked_conn),
+ READ_EVENT|WRITE_EVENT);
+ }) ELSE_IF_NO_BUFFEREVENT
+ connection_start_reading(ENTRY_TO_CONN(linked_conn));
}
}
@@ -984,12 +1055,22 @@ _compare_strs(const void **a, const void **b)
* This url depends on whether or not the server we go to
* is sufficiently new to support conditional consensus downloading,
* i.e. GET .../consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b>
+ *
+ * If 'resource' is provided, it is the name of a consensus flavor to request.
*/
static char *
-directory_get_consensus_url(int supports_conditional_consensus)
+directory_get_consensus_url(int supports_conditional_consensus,
+ const char *resource)
{
- char *url;
- size_t len;
+ char *url = NULL;
+ const char *hyphen, *flavor;
+ if (resource==NULL || strcmp(resource, "ns")==0) {
+ flavor = ""; /* Request ns consensuses as "", so older servers will work*/
+ hyphen = "";
+ } else {
+ flavor = resource;
+ hyphen = "-";
+ }
if (supports_conditional_consensus) {
char *authority_id_list;
@@ -999,7 +1080,7 @@ directory_get_consensus_url(int supports_conditional_consensus)
trusted_dir_server_t *, ds,
{
char *hex;
- if (!(ds->type & V3_AUTHORITY))
+ if (!(ds->type & V3_DIRINFO))
continue;
hex = tor_malloc(2*CONDITIONAL_CONSENSUS_FPR_LEN+1);
@@ -1011,16 +1092,15 @@ directory_get_consensus_url(int supports_conditional_consensus)
authority_id_list = smartlist_join_strings(authority_digests,
"+", 0, NULL);
- len = strlen(authority_id_list)+64;
- url = tor_malloc(len);
- tor_snprintf(url, len, "/tor/status-vote/current/consensus/%s.z",
- authority_id_list);
+ tor_asprintf(&url, "/tor/status-vote/current/consensus%s%s/%s.z",
+ hyphen, flavor, authority_id_list);
SMARTLIST_FOREACH(authority_digests, char *, cp, tor_free(cp));
smartlist_free(authority_digests);
tor_free(authority_id_list);
} else {
- url = tor_strdup("/tor/status-vote/current/consensus.z");
+ tor_asprintf(&url, "/tor/status-vote/current/consensus%s%s.z",
+ hyphen, flavor);
}
return url;
}
@@ -1036,11 +1116,11 @@ directory_send_command(dir_connection_t *conn,
time_t if_modified_since)
{
char proxystring[256];
- char proxyauthstring[256];
char hoststring[128];
- char imsstring[RFC1123_TIME_LEN+32];
+ smartlist_t *headers = smartlist_create();
char *url;
char request[8192];
+ char *header;
const char *httpcommand = NULL;
size_t len;
@@ -1060,12 +1140,11 @@ directory_send_command(dir_connection_t *conn,
}
/* Format if-modified-since */
- if (!if_modified_since) {
- imsstring[0] = '\0';
- } else {
+ if (if_modified_since) {
char b[RFC1123_TIME_LEN+1];
format_rfc1123_time(b, if_modified_since);
- tor_snprintf(imsstring, sizeof(imsstring), "\r\nIf-Modified-Since: %s", b);
+ tor_asprintf(&header, "If-Modified-Since: %s\r\n", b);
+ smartlist_add(headers, header);
}
/* come up with some proxy lines, if we're using one. */
@@ -1080,16 +1159,14 @@ directory_send_command(dir_connection_t *conn,
log_warn(LD_BUG, "Encoding http authenticator failed");
}
if (base64_authenticator) {
- tor_snprintf(proxyauthstring, sizeof(proxyauthstring),
- "\r\nProxy-Authorization: Basic %s",
+ tor_asprintf(&header,
+ "Proxy-Authorization: Basic %s\r\n",
base64_authenticator);
tor_free(base64_authenticator);
- } else {
- proxyauthstring[0] = 0;
+ smartlist_add(headers, header);
}
} else {
proxystring[0] = 0;
- proxyauthstring[0] = 0;
}
switch (purpose) {
@@ -1101,10 +1178,11 @@ directory_send_command(dir_connection_t *conn,
tor_snprintf(url, len, "/tor/status/%s", resource);
break;
case DIR_PURPOSE_FETCH_CONSENSUS:
- tor_assert(!resource);
+ /* resource is optional. If present, it's a flavor name */
tor_assert(!payload);
httpcommand = "GET";
- url = directory_get_consensus_url(supports_conditional_consensus);
+ url = directory_get_consensus_url(supports_conditional_consensus,
+ resource);
log_info(LD_DIR, "Downloading consensus from %s using %s",
hoststring, url);
break;
@@ -1144,12 +1222,23 @@ directory_send_command(dir_connection_t *conn,
url = tor_malloc(len);
tor_snprintf(url, len, "/tor/extra/%s", resource);
break;
- case DIR_PURPOSE_UPLOAD_DIR:
+ case DIR_PURPOSE_FETCH_MICRODESC:
+ tor_assert(resource);
+ httpcommand = "GET";
+ tor_asprintf(&url, "/tor/micro/%s", resource);
+ break;
+ case DIR_PURPOSE_UPLOAD_DIR: {
+ const char *why = router_get_descriptor_gen_reason();
tor_assert(!resource);
tor_assert(payload);
httpcommand = "POST";
url = tor_strdup("/tor/");
+ if (why) {
+ tor_asprintf(&header, "X-Desc-Gen-Reason: %s\r\n", why);
+ smartlist_add(headers, header);
+ }
break;
+ }
case DIR_PURPOSE_UPLOAD_VOTE:
tor_assert(!resource);
tor_assert(payload);
@@ -1200,26 +1289,26 @@ directory_send_command(dir_connection_t *conn,
connection_write_to_buf(url, strlen(url), TO_CONN(conn));
tor_free(url);
- if (!strcmp(httpcommand, "GET") && !payload) {
- tor_snprintf(request, sizeof(request),
- " HTTP/1.0\r\nHost: %s%s%s\r\n\r\n",
- hoststring,
- imsstring,
- proxyauthstring);
- } else {
- tor_snprintf(request, sizeof(request),
- " HTTP/1.0\r\nContent-Length: %lu\r\nHost: %s%s%s\r\n\r\n",
- payload ? (unsigned long)payload_len : 0,
- hoststring,
- imsstring,
- proxyauthstring);
+ if (!strcmp(httpcommand, "POST") || payload) {
+ tor_asprintf(&header, "Content-Length: %lu\r\n",
+ payload ? (unsigned long)payload_len : 0);
+ smartlist_add(headers, header);
}
+
+ header = smartlist_join_strings(headers, "", 0, NULL);
+ tor_snprintf(request, sizeof(request), " HTTP/1.0\r\nHost: %s\r\n%s\r\n",
+ hoststring, header);
+ tor_free(header);
+
connection_write_to_buf(request, strlen(request), TO_CONN(conn));
if (payload) {
/* then send the payload afterwards too */
connection_write_to_buf(payload, payload_len, TO_CONN(conn));
}
+
+ SMARTLIST_FOREACH(headers, char *, h, tor_free(h));
+ smartlist_free(headers);
}
/** Parse an HTTP request string <b>headers</b> of the form
@@ -1419,6 +1508,9 @@ body_is_plausible(const char *body, size_t len, int purpose)
return 1; /* empty bodies don't need decompression */
if (len < 32)
return 0;
+ if (purpose == DIR_PURPOSE_FETCH_MICRODESC) {
+ return (!strcmpstart(body,"onion-key"));
+ }
if (purpose != DIR_PURPOSE_FETCH_RENDDESC) {
if (!strcmpstart(body,"router") ||
!strcmpstart(body,"signed-directory") ||
@@ -1495,11 +1587,12 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
int plausible;
int skewed=0;
int allow_partial = (conn->_base.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
- conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO);
+ conn->_base.purpose == DIR_PURPOSE_FETCH_EXTRAINFO ||
+ conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC);
int was_compressed=0;
time_t now = time(NULL);
- switch (fetch_from_buf_http(conn->_base.inbuf,
+ switch (connection_fetch_from_buf_http(TO_CONN(conn),
&headers, MAX_HEADERS_SIZE,
&body, &body_len, MAX_DIR_DL_SIZE,
allow_partial)) {
@@ -1574,13 +1667,14 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (status_code == 503) {
routerstatus_t *rs;
trusted_dir_server_t *ds;
+ const char *id_digest = conn->identity_digest;
log_info(LD_DIR,"Received http status code %d (%s) from server "
"'%s:%d'. I'll try again soon.",
status_code, escaped(reason), conn->_base.address,
conn->_base.port);
- if ((rs = router_get_consensus_status_by_id(conn->identity_digest)))
+ if ((rs = router_get_mutable_consensus_status_by_id(id_digest)))
rs->last_dir_503_at = now;
- if ((ds = router_get_trusteddirserver_by_digest(conn->identity_digest)))
+ if ((ds = router_get_trusteddirserver_by_digest(id_digest)))
ds->fake_status.last_dir_503_at = now;
tor_free(body); tor_free(headers); tor_free(reason);
@@ -1719,6 +1813,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (conn->_base.purpose == DIR_PURPOSE_FETCH_CONSENSUS) {
int r;
+ const char *flavname = conn->requested_resource;
if (status_code != 200) {
int severity = (status_code == 304) ? LOG_INFO : LOG_WARN;
log(severity, LD_DIR,
@@ -1727,22 +1822,24 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
status_code, escaped(reason), conn->_base.address,
conn->_base.port);
tor_free(body); tor_free(headers); tor_free(reason);
- networkstatus_consensus_download_failed(status_code);
+ networkstatus_consensus_download_failed(status_code, flavname);
return -1;
}
log_info(LD_DIR,"Received consensus directory (size %d) from server "
"'%s:%d'", (int)body_len, conn->_base.address, conn->_base.port);
- if ((r=networkstatus_set_current_consensus(body, "ns", 0))<0) {
+ if ((r=networkstatus_set_current_consensus(body, flavname, 0))<0) {
log_fn(r<-1?LOG_WARN:LOG_INFO, LD_DIR,
- "Unable to load consensus directory downloaded from "
+ "Unable to load %s consensus directory downloaded from "
"server '%s:%d'. I'll try again soon.",
- conn->_base.address, conn->_base.port);
+ flavname, conn->_base.address, conn->_base.port);
tor_free(body); tor_free(headers); tor_free(reason);
- networkstatus_consensus_download_failed(0);
+ networkstatus_consensus_download_failed(0, flavname);
return -1;
}
/* launches router downloads as needed */
routers_update_all_from_networkstatus(now, 3);
+ update_microdescs_from_networkstatus(now);
+ update_microdesc_downloads(now);
directory_info_has_arrived(now, 0);
log_info(LD_DIR, "Successfully loaded consensus.");
}
@@ -1892,6 +1989,43 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
if (directory_conn_is_self_reachability_test(conn))
router_dirport_found_reachable();
}
+ if (conn->_base.purpose == DIR_PURPOSE_FETCH_MICRODESC) {
+ smartlist_t *which = NULL;
+ log_info(LD_DIR,"Received answer to microdescriptor request (status %d, "
+ "size %d) from server '%s:%d'",
+ status_code, (int)body_len, conn->_base.address,
+ conn->_base.port);
+ tor_assert(conn->requested_resource &&
+ !strcmpstart(conn->requested_resource, "d/"));
+ which = smartlist_create();
+ dir_split_resource_into_fingerprints(conn->requested_resource+2,
+ which, NULL,
+ DSR_DIGEST256|DSR_BASE64);
+ if (status_code != 200) {
+ log_info(LD_DIR, "Received status code %d (%s) from server "
+ "'%s:%d' while fetching \"/tor/micro/%s\". I'll try again "
+ "soon.",
+ status_code, escaped(reason), conn->_base.address,
+ (int)conn->_base.port, conn->requested_resource);
+ dir_microdesc_download_failed(which, status_code);
+ SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
+ smartlist_free(which);
+ tor_free(body); tor_free(headers); tor_free(reason);
+ return 0;
+ } else {
+ smartlist_t *mds;
+ mds = microdescs_add_to_cache(get_microdesc_cache(),
+ body, body+body_len, SAVED_NOWHERE, 0,
+ now, which);
+ if (smartlist_len(which)) {
+ /* Mark remaining ones as failed. */
+ dir_microdesc_download_failed(which, status_code);
+ }
+ SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
+ smartlist_free(which);
+ smartlist_free(mds);
+ }
+ }
if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_DIR) {
switch (status_code) {
@@ -2166,7 +2300,7 @@ connection_dir_process_inbuf(dir_connection_t *conn)
return 0;
}
- if (buf_datalen(conn->_base.inbuf) > MAX_DIRECTORY_OBJECT_SIZE) {
+ if (connection_get_inbuf_len(TO_CONN(conn)) > MAX_DIRECTORY_OBJECT_SIZE) {
log_warn(LD_HTTP, "Too much data received from directory connection: "
"denial of service attempt, or you need to upgrade?");
connection_mark_for_close(TO_CONN(conn));
@@ -2178,6 +2312,28 @@ connection_dir_process_inbuf(dir_connection_t *conn)
return 0;
}
+/** Called when we're about to finally unlink and free a directory connection:
+ * perform necessary accounting and cleanup */
+void
+connection_dir_about_to_close(dir_connection_t *dir_conn)
+{
+ connection_t *conn = TO_CONN(dir_conn);
+
+ if (conn->state < DIR_CONN_STATE_CLIENT_FINISHED) {
+ /* It's a directory connection and connecting or fetching
+ * failed: forget about this router, and maybe try again. */
+ connection_dir_request_failed(dir_conn);
+ }
+ /* If we were trying to fetch a v2 rend desc and did not succeed,
+ * retry as needed. (If a fetch is successful, the connection state
+ * is changed to DIR_PURPOSE_HAS_FETCHED_RENDDESC to mark that
+ * refetching is unnecessary.) */
+ if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 &&
+ dir_conn->rend_data &&
+ strlen(dir_conn->rend_data->onion_address) == REND_SERVICE_ID_LEN_BASE32)
+ rend_client_refetch_v2_renddesc(dir_conn->rend_data);
+}
+
/** Create an http response for the client <b>conn</b> out of
* <b>status</b> and <b>reason_phrase</b>. Write it to <b>conn</b>.
*/
@@ -2469,18 +2625,18 @@ client_likes_consensus(networkstatus_t *v, const char *want_url)
* Always return 0. */
static int
directory_handle_command_get(dir_connection_t *conn, const char *headers,
- const char *body, size_t body_len)
+ const char *req_body, size_t req_body_len)
{
size_t dlen;
char *url, *url_mem, *header;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
time_t if_modified_since = 0;
int compressed;
size_t url_len;
/* We ignore the body of a GET request. */
- (void)body;
- (void)body_len;
+ (void)req_body;
+ (void)req_body_len;
log_debug(LD_DIRSERV,"Received GET command.");
@@ -3189,7 +3345,7 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers,
const char *body, size_t body_len)
{
char *url = NULL;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
log_debug(LD_DIRSERV,"Received POST command.");
@@ -3333,7 +3489,7 @@ directory_handle_command(dir_connection_t *conn)
tor_assert(conn);
tor_assert(conn->_base.type == CONN_TYPE_DIR);
- switch (fetch_from_buf_http(conn->_base.inbuf,
+ switch (connection_fetch_from_buf_http(TO_CONN(conn),
&headers, MAX_HEADERS_SIZE,
&body, &body_len, MAX_DIR_UL_SIZE, 0)) {
case -1: /* overflow */
@@ -3386,14 +3542,27 @@ connection_dir_finished_flushing(dir_connection_t *conn)
DIRREQ_DIRECT,
DIRREQ_FLUSHING_DIR_CONN_FINISHED);
switch (conn->_base.state) {
+ case DIR_CONN_STATE_CONNECTING:
case DIR_CONN_STATE_CLIENT_SENDING:
log_debug(LD_DIR,"client finished sending command.");
conn->_base.state = DIR_CONN_STATE_CLIENT_READING;
- connection_stop_writing(TO_CONN(conn));
return 0;
case DIR_CONN_STATE_SERVER_WRITING:
- log_debug(LD_DIRSERV,"Finished writing server response. Closing.");
- connection_mark_for_close(TO_CONN(conn));
+ if (conn->dir_spool_src != DIR_SPOOL_NONE) {
+#ifdef USE_BUFFEREVENTS
+ /* This can happen with paired bufferevents, since a paired connection
+ * can flush immediately when you write to it, making the subsequent
+ * check in connection_handle_write_cb() decide that the connection
+ * is flushed. */
+ log_debug(LD_DIRSERV, "Emptied a dirserv buffer, but still spooling.");
+#else
+ log_warn(LD_BUG, "Emptied a dirserv buffer, but it's still spooling!");
+ connection_mark_for_close(TO_CONN(conn));
+#endif
+ } else {
+ log_debug(LD_DIRSERV, "Finished writing server response. Closing.");
+ connection_mark_for_close(TO_CONN(conn));
+ }
return 0;
default:
log_warn(LD_BUG,"called in unexpected state %d.",
@@ -3619,6 +3788,36 @@ dir_routerdesc_download_failed(smartlist_t *failed, int status_code,
* every 10 or 60 seconds (FOO_DESCRIPTOR_RETRY_INTERVAL) in main.c. */
}
+/* DOCDOC NM */
+static void
+dir_microdesc_download_failed(smartlist_t *failed,
+ int status_code)
+{
+ networkstatus_t *consensus
+ = networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC);
+ routerstatus_t *rs;
+ download_status_t *dls;
+ time_t now = time(NULL);
+ int server = directory_fetches_from_authorities(get_options());
+
+ if (! consensus)
+ return;
+ SMARTLIST_FOREACH_BEGIN(failed, const char *, d) {
+ rs = router_get_mutable_consensus_status_by_descriptor_digest(consensus,d);
+ if (!rs)
+ continue;
+ dls = &rs->dl_status;
+ if (dls->n_download_failures >= MAX_MICRODESC_DOWNLOAD_FAILURES)
+ continue;
+ {
+ char buf[BASE64_DIGEST256_LEN+1];
+ digest256_to_base64(buf, d);
+ download_status_increment_failure(dls, status_code, buf,
+ server, now);
+ }
+ } SMARTLIST_FOREACH_END(d);
+}
+
/** Helper. Compare two fp_pair_t objects, and return negative, 0, or
* positive as appropriate. */
static int
diff --git a/src/or/directory.h b/src/or/directory.h
index 94dfb1764..8c63bb5df 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -13,9 +13,8 @@
#define _TOR_DIRECTORY_H
int directories_have_accepted_server_descriptor(void);
-char *authority_type_to_string(authority_type_t auth);
void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
- authority_type_t type, const char *payload,
+ dirinfo_type_t type, const char *payload,
size_t payload_len, size_t extrainfo_len);
void directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
const char *resource,
@@ -23,7 +22,7 @@ void directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose,
void directory_get_from_all_authorities(uint8_t dir_purpose,
uint8_t router_purpose,
const char *resource);
-void directory_initiate_command_routerstatus(routerstatus_t *status,
+void directory_initiate_command_routerstatus(const routerstatus_t *status,
uint8_t dir_purpose,
uint8_t router_purpose,
int anonymized_connection,
@@ -31,7 +30,7 @@ void directory_initiate_command_routerstatus(routerstatus_t *status,
const char *payload,
size_t payload_len,
time_t if_modified_since);
-void directory_initiate_command_routerstatus_rend(routerstatus_t *status,
+void directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
uint8_t dir_purpose,
uint8_t router_purpose,
int anonymized_connection,
@@ -49,7 +48,7 @@ int connection_dir_reached_eof(dir_connection_t *conn);
int connection_dir_process_inbuf(dir_connection_t *conn);
int connection_dir_finished_flushing(dir_connection_t *conn);
int connection_dir_finished_connecting(dir_connection_t *conn);
-void connection_dir_request_failed(dir_connection_t *conn);
+void connection_dir_about_to_close(dir_connection_t *dir_conn);
void directory_initiate_command(const char *address, const tor_addr_t *addr,
uint16_t or_port, uint16_t dir_port,
int supports_conditional_consensus,
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index c427fe2ef..288fca99b 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -16,6 +16,7 @@
#include "hibernate.h"
#include "microdesc.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "policies.h"
#include "rephist.h"
#include "router.h"
@@ -67,8 +68,6 @@ static char *format_versions_list(config_line_t *ln);
struct authdir_config_t;
static int add_fingerprint_to_dir(const char *nickname, const char *fp,
struct authdir_config_t *list);
-static uint32_t dirserv_router_get_status(const routerinfo_t *router,
- const char **msg);
static uint32_t
dirserv_get_status_impl(const char *fp, const char *nickname,
const char *address,
@@ -76,7 +75,8 @@ dirserv_get_status_impl(const char *fp, const char *nickname,
const char *platform, const char *contact,
const char **msg, int should_log);
static void clear_cached_dir(cached_dir_t *d);
-static signed_descriptor_t *get_signed_descriptor_by_fp(const char *fp,
+static const signed_descriptor_t *get_signed_descriptor_by_fp(
+ const char *fp,
int extrainfo,
time_t publish_cutoff);
static int dirserv_add_extrainfo(extrainfo_t *ei, const char **msg);
@@ -212,7 +212,7 @@ dirserv_load_fingerprint_file(void)
authdir_config_t *fingerprint_list_new;
int result;
config_line_t *front=NULL, *list;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
fname = get_datadir_fname("approved-routers");
log_info(LD_GENERAL,
@@ -305,7 +305,7 @@ dirserv_load_fingerprint_file(void)
*
* If the status is 'FP_REJECT' and <b>msg</b> is provided, set
* *<b>msg</b> to an explanation of why. */
-static uint32_t
+uint32_t
dirserv_router_get_status(const routerinfo_t *router, const char **msg)
{
char d[DIGEST_LEN];
@@ -327,7 +327,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg)
/** Return true if there is no point in downloading the router described by
* <b>rs</b> because this directory would reject it. */
int
-dirserv_would_reject_router(routerstatus_t *rs)
+dirserv_would_reject_router(const routerstatus_t *rs)
{
uint32_t res;
@@ -362,7 +362,7 @@ dirserv_get_name_status(const char *id_digest, const char *nickname)
return 0;
}
-/** Helper: As dirserv_get_router_status, but takes the router fingerprint
+/** Helper: As dirserv_router_get_status, but takes the router fingerprint
* (hex, no spaces), nickname, address (used for logging only), IP address, OR
* port, platform (logging only) and contact info (logging only) as arguments.
*
@@ -377,7 +377,7 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
const char **msg, int should_log)
{
int reject_unlisted = get_options()->AuthDirRejectUnlisted;
- uint32_t result = 0;
+ uint32_t result;
router_status_t *status_by_digest;
if (!fingerprint_list)
@@ -542,7 +542,7 @@ dirserv_router_has_valid_address(routerinfo_t *ri)
*/
int
authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
- int complain)
+ int complain, int *valid_out)
{
/* Okay. Now check whether the fingerprint is recognized. */
uint32_t status = dirserv_router_get_status(ri, msg);
@@ -586,15 +586,24 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
*msg = "Rejected: Address is not an IP, or IP is a private address.";
return -1;
}
- /* Okay, looks like we're willing to accept this one. */
- ri->is_named = (status & FP_NAMED) ? 1 : 0;
- ri->is_valid = (status & FP_INVALID) ? 0 : 1;
- ri->is_bad_directory = (status & FP_BADDIR) ? 1 : 0;
- ri->is_bad_exit = (status & FP_BADEXIT) ? 1 : 0;
+
+ *valid_out = ! (status & FP_INVALID);
return 0;
}
+/** Update the relevant flags of <b>node</b> based on our opinion as a
+ * directory authority in <b>authstatus</b>, as returned by
+ * dirserv_router_get_status or equivalent. */
+void
+dirserv_set_node_flags_from_authoritative_status(node_t *node,
+ uint32_t authstatus)
+{
+ node->is_valid = (authstatus & FP_INVALID) ? 0 : 1;
+ node->is_bad_directory = (authstatus & FP_BADDIR) ? 1 : 0;
+ node->is_bad_exit = (authstatus & FP_BADEXIT) ? 1 : 0;
+}
+
/** True iff <b>a</b> is more severe than <b>b</b>. */
static int
WRA_MORE_SEVERE(was_router_added_t a, was_router_added_t b)
@@ -719,7 +728,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
* from this server. (We do this here and not in router_add_to_routerlist
* because we want to be able to accept the newest router descriptor that
* another authority has, so we all converge on the same one.) */
- ri_old = router_get_by_digest(ri->cache_info.identity_digest);
+ ri_old = router_get_mutable_by_digest(ri->cache_info.identity_digest);
if (ri_old && ri_old->cache_info.published_on < ri->cache_info.published_on
&& router_differences_are_cosmetic(ri_old, ri)
&& !router_is_me(ri)) {
@@ -763,8 +772,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
routerlist_descriptors_added(changed, 0);
smartlist_free(changed);
if (!*msg) {
- *msg = ri->is_valid ? "Descriptor for valid server accepted" :
- "Descriptor for invalid server accepted";
+ *msg = "Descriptor accepted";
}
log_info(LD_DIRSERV,
"Added descriptor from '%s' (source: %s): %s.",
@@ -779,12 +787,12 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
static was_router_added_t
dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
{
- routerinfo_t *ri;
+ const routerinfo_t *ri;
int r;
tor_assert(msg);
*msg = NULL;
- ri = router_get_by_digest(ei->cache_info.identity_digest);
+ ri = router_get_by_id_digest(ei->cache_info.identity_digest);
if (!ri) {
*msg = "No corresponding router descriptor for extra-info descriptor";
extrainfo_free(ei);
@@ -819,56 +827,67 @@ dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
static void
directory_remove_invalid(void)
{
- int i;
int changed = 0;
routerlist_t *rl = router_get_routerlist();
+ smartlist_t *nodes = smartlist_create();
+ smartlist_add_all(nodes, nodelist_get_list());
- routerlist_assert_ok(rl);
-
- for (i = 0; i < smartlist_len(rl->routers); ++i) {
+ SMARTLIST_FOREACH_BEGIN(nodes, node_t *, node) {
const char *msg;
- routerinfo_t *ent = smartlist_get(rl->routers, i);
+ routerinfo_t *ent = node->ri;
char description[NODE_DESC_BUF_LEN];
- uint32_t r = dirserv_router_get_status(ent, &msg);
+ uint32_t r;
+ if (!ent)
+ continue;
+ r = dirserv_router_get_status(ent, &msg);
router_get_description(description, ent);
if (r & FP_REJECT) {
log_info(LD_DIRSERV, "Router %s is now rejected: %s",
description, msg?msg:"");
routerlist_remove(rl, ent, 0, time(NULL));
- i--;
changed = 1;
continue;
}
- if (bool_neq((r & FP_NAMED), ent->is_named)) {
+#if 0
+ if (bool_neq((r & FP_NAMED), ent->auth_says_is_named)) {
log_info(LD_DIRSERV,
"Router %s is now %snamed.", description,
(r&FP_NAMED)?"":"un");
ent->is_named = (r&FP_NAMED)?1:0;
changed = 1;
}
- if (bool_neq((r & FP_INVALID), !ent->is_valid)) {
+ if (bool_neq((r & FP_UNNAMED), ent->auth_says_is_unnamed)) {
+ log_info(LD_DIRSERV,
+ "Router '%s' is now %snamed. (FP_UNNAMED)", description,
+ (r&FP_NAMED)?"":"un");
+ ent->is_named = (r&FP_NUNAMED)?0:1;
+ changed = 1;
+ }
+#endif
+ if (bool_neq((r & FP_INVALID), !node->is_valid)) {
log_info(LD_DIRSERV, "Router '%s' is now %svalid.", description,
(r&FP_INVALID) ? "in" : "");
- ent->is_valid = (r&FP_INVALID)?0:1;
+ node->is_valid = (r&FP_INVALID)?0:1;
changed = 1;
}
- if (bool_neq((r & FP_BADDIR), ent->is_bad_directory)) {
+ if (bool_neq((r & FP_BADDIR), node->is_bad_directory)) {
log_info(LD_DIRSERV, "Router '%s' is now a %s directory", description,
(r & FP_BADDIR) ? "bad" : "good");
- ent->is_bad_directory = (r&FP_BADDIR) ? 1: 0;
+ node->is_bad_directory = (r&FP_BADDIR) ? 1: 0;
changed = 1;
}
- if (bool_neq((r & FP_BADEXIT), ent->is_bad_exit)) {
+ if (bool_neq((r & FP_BADEXIT), node->is_bad_exit)) {
log_info(LD_DIRSERV, "Router '%s' is now a %s exit", description,
(r & FP_BADEXIT) ? "bad" : "good");
- ent->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
+ node->is_bad_exit = (r&FP_BADEXIT) ? 1: 0;
changed = 1;
}
- }
+ } SMARTLIST_FOREACH_END(node);
if (changed)
directory_set_dirty();
routerlist_assert_ok(rl);
+ smartlist_free(nodes);
}
/** Mark the directory as <b>dirty</b> -- when we're next asked for a
@@ -907,10 +926,11 @@ directory_set_dirty(void)
* as running iff <b>is_live</b> is true.
*/
static char *
-list_single_server_status(routerinfo_t *desc, int is_live)
+list_single_server_status(const routerinfo_t *desc, int is_live)
{
char buf[MAX_NICKNAME_LEN+HEX_DIGEST_LEN+4]; /* !nickname=$hexdigest\0 */
char *cp;
+ const node_t *node;
tor_assert(desc);
@@ -918,7 +938,8 @@ list_single_server_status(routerinfo_t *desc, int is_live)
if (!is_live) {
*cp++ = '!';
}
- if (desc->is_valid) {
+ node = node_get_by_id(desc->cache_info.identity_digest);
+ if (node && node->is_valid) {
strlcpy(cp, desc->nickname, sizeof(buf)-(cp-buf));
cp += strlen(cp);
*cp++ = '=';
@@ -957,6 +978,8 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
unreachable.
*/
int answer;
+ node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest);
+ tor_assert(node);
if (router_is_me(router)) {
/* We always know if we are down ourselves. */
@@ -991,7 +1014,7 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now)
rep_hist_note_router_unreachable(router->cache_info.identity_digest, when);
}
- router->is_running = answer;
+ node->is_running = answer;
}
/** Based on the routerinfo_ts in <b>routers</b>, allocate the
@@ -1009,7 +1032,7 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
smartlist_t *rs_entries;
time_t now = time(NULL);
time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
/* We include v2 dir auths here too, because they need to answer
* controllers. Eventually we'll deprecate this whole function;
* see also networkstatus_getinfo_by_purpose(). */
@@ -1019,6 +1042,8 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
rs_entries = smartlist_create();
SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) {
+ const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+ tor_assert(node);
if (authdir) {
/* Update router status in routerinfo_t. */
dirserv_set_router_is_running(ri, now);
@@ -1026,12 +1051,13 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out,
if (for_controller) {
char name_buf[MAX_VERBOSE_NICKNAME_LEN+2];
char *cp = name_buf;
- if (!ri->is_running)
+ if (!node->is_running)
*cp++ = '!';
router_get_verbose_nickname(cp, ri);
smartlist_add(rs_entries, tor_strdup(name_buf));
} else if (ri->cache_info.published_on >= cutoff) {
- smartlist_add(rs_entries, list_single_server_status(ri, ri->is_running));
+ smartlist_add(rs_entries, list_single_server_status(ri,
+ node->is_running));
}
} SMARTLIST_FOREACH_END(ri);
@@ -1069,12 +1095,12 @@ format_versions_list(config_line_t *ln)
* not hibernating, and not too old. Else return 0.
*/
static int
-router_is_active(routerinfo_t *ri, time_t now)
+router_is_active(const routerinfo_t *ri, const node_t *node, time_t now)
{
time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH;
if (ri->cache_info.published_on < cutoff)
return 0;
- if (!ri->is_running || !ri->is_valid || ri->is_hibernating)
+ if (!node->is_running || !node->is_valid || ri->is_hibernating)
return 0;
return 1;
}
@@ -1173,9 +1199,9 @@ dirserv_dump_directory_to_string(char **dir_out,
/** Return 1 if we fetch our directory material directly from the
* authorities, rather than from a mirror. */
int
-directory_fetches_from_authorities(or_options_t *options)
+directory_fetches_from_authorities(const or_options_t *options)
{
- routerinfo_t *me;
+ const routerinfo_t *me;
uint32_t addr;
int refuseunknown;
if (options->FetchDirInfoEarly)
@@ -1200,7 +1226,7 @@ directory_fetches_from_authorities(or_options_t *options)
* on the "mirror" schedule rather than the "client" schedule.
*/
int
-directory_fetches_dir_info_early(or_options_t *options)
+directory_fetches_dir_info_early(const or_options_t *options)
{
return directory_fetches_from_authorities(options);
}
@@ -1212,7 +1238,7 @@ directory_fetches_dir_info_early(or_options_t *options)
* client as a directory guard.
*/
int
-directory_fetches_dir_info_later(or_options_t *options)
+directory_fetches_dir_info_later(const or_options_t *options)
{
return options->UseBridges != 0;
}
@@ -1220,7 +1246,7 @@ directory_fetches_dir_info_later(or_options_t *options)
/** Return 1 if we want to cache v2 dir info (each status file).
*/
int
-directory_caches_v2_dir_info(or_options_t *options)
+directory_caches_v2_dir_info(const or_options_t *options)
{
return options->DirPort != 0;
}
@@ -1229,7 +1255,7 @@ directory_caches_v2_dir_info(or_options_t *options)
* and we're willing to serve them to others. Else return 0.
*/
int
-directory_caches_dir_info(or_options_t *options)
+directory_caches_dir_info(const or_options_t *options)
{
if (options->BridgeRelay || options->DirPort)
return 1;
@@ -1245,7 +1271,7 @@ directory_caches_dir_info(or_options_t *options)
* requests via the "begin_dir" interface, which doesn't require
* having any separate port open. */
int
-directory_permits_begindir_requests(or_options_t *options)
+directory_permits_begindir_requests(const or_options_t *options)
{
return options->BridgeRelay != 0 || options->DirPort != 0;
}
@@ -1254,7 +1280,7 @@ directory_permits_begindir_requests(or_options_t *options)
* requests via the controller interface, which doesn't require
* having any separate port open. */
int
-directory_permits_controller_requests(or_options_t *options)
+directory_permits_controller_requests(const or_options_t *options)
{
return options->DirPort != 0;
}
@@ -1264,7 +1290,8 @@ directory_permits_controller_requests(or_options_t *options)
* lately.
*/
int
-directory_too_idle_to_fetch_descriptors(or_options_t *options, time_t now)
+directory_too_idle_to_fetch_descriptors(const or_options_t *options,
+ time_t now)
{
return !directory_caches_dir_info(options) &&
!options->FetchUselessDescriptors &&
@@ -1287,7 +1314,8 @@ static cached_dir_t cached_runningrouters;
* cached_dir_t. */
static digestmap_t *cached_v2_networkstatus = NULL;
-/** Map from flavor name to the v3 consensuses that we're currently serving. */
+/** Map from flavor name to the cached_dir_t for the v3 consensuses that we're
+ * currently serving. */
static strmap_t *cached_consensuses = NULL;
/** Possibly replace the contents of <b>d</b> with the value of
@@ -1531,11 +1559,11 @@ dirserv_pick_cached_dir_obj(cached_dir_t *cache_src,
cached_dir_t *auth_src,
time_t dirty, cached_dir_t *(*regenerate)(void),
const char *name,
- authority_type_t auth_type)
+ dirinfo_type_t auth_type)
{
- or_options_t *options = get_options();
- int authority = (auth_type == V1_AUTHORITY && authdir_mode_v1(options)) ||
- (auth_type == V2_AUTHORITY && authdir_mode_v2(options));
+ const or_options_t *options = get_options();
+ int authority = (auth_type == V1_DIRINFO && authdir_mode_v1(options)) ||
+ (auth_type == V2_DIRINFO && authdir_mode_v2(options));
if (!authority || authdir_mode_bridge(options)) {
return cache_src;
@@ -1564,7 +1592,7 @@ dirserv_get_directory(void)
return dirserv_pick_cached_dir_obj(cached_directory, the_directory,
the_directory_is_dirty,
dirserv_regenerate_directory,
- "v1 server directory", V1_AUTHORITY);
+ "v1 server directory", V1_DIRINFO);
}
/** Only called by v1 auth dirservers.
@@ -1657,7 +1685,7 @@ dirserv_get_runningrouters(void)
&cached_runningrouters, &the_runningrouters,
runningrouters_is_dirty,
generate_runningrouters,
- "v1 network status list", V1_AUTHORITY);
+ "v1 network status list", V1_DIRINFO);
}
/** Return the latest downloaded consensus networkstatus in encoded, signed,
@@ -1740,7 +1768,7 @@ static uint64_t total_exit_bandwidth = 0;
/** Helper: estimate the uptime of a router given its stated uptime and the
* amount of time since it last stated its stated uptime. */
static INLINE long
-real_uptime(routerinfo_t *router, time_t now)
+real_uptime(const routerinfo_t *router, time_t now)
{
if (now < router->cache_info.published_on)
return router->uptime;
@@ -1794,7 +1822,8 @@ dirserv_thinks_router_is_unreliable(time_t now,
* been set.
*/
static int
-dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
+dirserv_thinks_router_is_hs_dir(const routerinfo_t *router,
+ const node_t *node, time_t now)
{
long uptime;
@@ -1822,7 +1851,7 @@ dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
* to fix the bug was 0.2.2.25-alpha. */
return (router->wants_to_be_hs_dir && router->dir_port &&
uptime > get_options()->MinUptimeHidServDirectoryV2 &&
- router->is_running);
+ node->is_running);
}
/** Look through the routerlist, the Mean Time Between Failure history, and
@@ -1870,19 +1899,22 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
/* Weighted fractional uptime for each active router. */
wfus = tor_malloc(sizeof(double)*smartlist_len(rl->routers));
+ nodelist_assert_ok();
+
/* Now, fill in the arrays. */
- SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
- if (router_is_active(ri, now)) {
+ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
+ routerinfo_t *ri = node->ri;
+ if (ri && router_is_active(ri, node, now)) {
const char *id = ri->cache_info.identity_digest;
uint32_t bw;
- ri->is_exit = (!router_exit_policy_rejects_all(ri) &&
- exit_policy_is_general_exit(ri->exit_policy));
+ node->is_exit = (!router_exit_policy_rejects_all(ri) &&
+ exit_policy_is_general_exit(ri->exit_policy));
uptimes[n_active] = (uint32_t)real_uptime(ri, now);
mtbfs[n_active] = rep_hist_get_stability(id, now);
tks [n_active] = rep_hist_get_weighted_time_known(id, now);
bandwidths[n_active] = bw = router_get_advertised_bandwidth(ri);
total_bandwidth += bw;
- if (ri->is_exit && !ri->is_bad_exit) {
+ if (node->is_exit && !node->is_bad_exit) {
total_exit_bandwidth += bw;
} else {
bandwidths_excluding_exits[n_active_nonexit] = bw;
@@ -1890,7 +1922,7 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
}
++n_active;
}
- });
+ } SMARTLIST_FOREACH_END(node);
/* Now, compute thresholds. */
if (n_active) {
@@ -1916,15 +1948,17 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
/* Now that we have a time-known that 7/8 routers are known longer than,
* fill wfus with the wfu of every such "familiar" router. */
n_familiar = 0;
- SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
- if (router_is_active(ri, now)) {
+
+ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
+ routerinfo_t *ri = node->ri;
+ if (ri && router_is_active(ri, node, now)) {
const char *id = ri->cache_info.identity_digest;
long tk = rep_hist_get_weighted_time_known(id, now);
if (tk < guard_tk)
continue;
wfus[n_familiar++] = rep_hist_get_weighted_fractional_uptime(id, now);
}
- });
+ } SMARTLIST_FOREACH_END(node);
if (n_familiar)
guard_wfu = median_double(wfus, n_familiar);
if (guard_wfu > WFU_TO_GUARANTEE_GUARD)
@@ -1995,24 +2029,20 @@ version_from_platform(const char *platform)
*/
int
routerstatus_format_entry(char *buf, size_t buf_len,
- routerstatus_t *rs, const char *version,
+ const routerstatus_t *rs, const char *version,
routerstatus_format_type_t format)
{
int r;
- struct in_addr in;
char *cp;
char *summary;
char published[ISO_TIME_LEN+1];
- char ipaddr[INET_NTOA_BUF_LEN];
char identity64[BASE64_DIGEST_LEN+1];
char digest64[BASE64_DIGEST_LEN+1];
format_iso_time(published, rs->published_on);
digest_to_base64(identity64, rs->identity_digest);
digest_to_base64(digest64, rs->descriptor_digest);
- in.s_addr = htonl(rs->addr);
- tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr));
r = tor_snprintf(buf, buf_len,
"r %s %s %s%s%s %s %d %d\n",
@@ -2021,7 +2051,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
(format==NS_V3_CONSENSUS_MICRODESC)?"":digest64,
(format==NS_V3_CONSENSUS_MICRODESC)?"":" ",
published,
- ipaddr,
+ fmt_addr32(rs->addr),
(int)rs->or_port,
(int)rs->dir_port);
if (r<0) {
@@ -2049,7 +2079,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
rs->is_possible_guard?" Guard":"",
rs->is_hs_dir?" HSDir":"",
rs->is_named?" Named":"",
- rs->is_running?" Running":"",
+ rs->is_flagged_running?" Running":"",
rs->is_stable?" Stable":"",
rs->is_unnamed?" Unnamed":"",
rs->is_v2_dir?" V2Dir":"",
@@ -2071,7 +2101,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
}
if (format != NS_V2) {
- routerinfo_t* desc = router_get_by_digest(rs->identity_digest);
+ const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest);
uint32_t bw;
if (format != NS_CONTROL_PORT) {
@@ -2167,6 +2197,8 @@ _compare_routerinfo_by_ip_and_bw(const void **a, const void **b)
routerinfo_t *first = *(routerinfo_t **)a, *second = *(routerinfo_t **)b;
int first_is_auth, second_is_auth;
uint32_t bw_first, bw_second;
+ const node_t *node_first, *node_second;
+ int first_is_running, second_is_running;
/* we return -1 if first should appear before second... that is,
* if first is a better router. */
@@ -2189,9 +2221,14 @@ _compare_routerinfo_by_ip_and_bw(const void **a, const void **b)
else if (!first_is_auth && second_is_auth)
return 1;
- else if (first->is_running && !second->is_running)
+ node_first = node_get_by_id(first->cache_info.identity_digest);
+ node_second = node_get_by_id(second->cache_info.identity_digest);
+ first_is_running = node_first && node_first->is_running;
+ second_is_running = node_second && node_second->is_running;
+
+ if (first_is_running && !second_is_running)
return -1;
- else if (!first->is_running && second->is_running)
+ else if (!first_is_running && second_is_running)
return 1;
bw_first = router_get_advertised_bandwidth(first);
@@ -2215,7 +2252,7 @@ _compare_routerinfo_by_ip_and_bw(const void **a, const void **b)
static digestmap_t *
get_possible_sybil_list(const smartlist_t *routers)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
digestmap_t *omit_as_sybil;
smartlist_t *routers_by_ip = smartlist_create();
uint32_t last_addr;
@@ -2328,7 +2365,9 @@ is_router_version_good_for_possible_guard(const char *platform)
*/
void
set_routerstatus_from_routerinfo(routerstatus_t *rs,
- routerinfo_t *ri, time_t now,
+ node_t *node,
+ routerinfo_t *ri,
+ time_t now,
int naming, int listbadexits,
int listbaddirs, int vote_on_hsdirs)
{
@@ -2341,25 +2380,25 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
router_digest_is_trusted_dir(ri->cache_info.identity_digest);
/* Already set by compute_performance_thresholds. */
- rs->is_exit = ri->is_exit;
- rs->is_stable = ri->is_stable =
- router_is_active(ri, now) &&
+ rs->is_exit = node->is_exit;
+ rs->is_stable = node->is_stable =
+ router_is_active(ri, node, now) &&
!dirserv_thinks_router_is_unreliable(now, ri, 1, 0) &&
!unstable_version;
- rs->is_fast = ri->is_fast =
- router_is_active(ri, now) &&
+ rs->is_fast = node->is_fast =
+ router_is_active(ri, node, now) &&
!dirserv_thinks_router_is_unreliable(now, ri, 0, 1);
- rs->is_running = ri->is_running; /* computed above */
+ rs->is_flagged_running = node->is_running; /* computed above */
if (naming) {
uint32_t name_status = dirserv_get_name_status(
- ri->cache_info.identity_digest, ri->nickname);
+ node->identity, ri->nickname);
rs->is_named = (naming && (name_status & FP_NAMED)) ? 1 : 0;
rs->is_unnamed = (naming && (name_status & FP_UNNAMED)) ? 1 : 0;
}
- rs->is_valid = ri->is_valid;
+ rs->is_valid = node->is_valid;
- if (rs->is_fast &&
+ if (node->is_fast &&
(router_get_advertised_bandwidth(ri) >= BANDWIDTH_TO_GUARANTEE_GUARD ||
router_get_advertised_bandwidth(ri) >=
MIN(guard_bandwidth_including_exits,
@@ -2367,24 +2406,25 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
(options->GiveGuardFlagTo_CVE_2011_2768_VulnerableRelays ||
is_router_version_good_for_possible_guard(ri->platform))) {
long tk = rep_hist_get_weighted_time_known(
- ri->cache_info.identity_digest, now);
+ node->identity, now);
double wfu = rep_hist_get_weighted_fractional_uptime(
- ri->cache_info.identity_digest, now);
+ node->identity, now);
rs->is_possible_guard = (wfu >= guard_wfu && tk >= guard_tk) ? 1 : 0;
} else {
rs->is_possible_guard = 0;
}
- rs->is_bad_directory = listbaddirs && ri->is_bad_directory;
- rs->is_bad_exit = listbadexits && ri->is_bad_exit;
- ri->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, now);
- rs->is_hs_dir = vote_on_hsdirs && ri->is_hs_dir;
+
+ rs->is_bad_directory = listbaddirs && node->is_bad_directory;
+ rs->is_bad_exit = listbadexits && node->is_bad_exit;
+ node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now);
+ rs->is_hs_dir = vote_on_hsdirs && node->is_hs_dir;
rs->is_v2_dir = ri->dir_port != 0;
if (!strcasecmp(ri->nickname, UNNAMED_ROUTER_NICKNAME))
rs->is_named = rs->is_unnamed = 0;
rs->published_on = ri->cache_info.published_on;
- memcpy(rs->identity_digest, ri->cache_info.identity_digest, DIGEST_LEN);
+ memcpy(rs->identity_digest, node->identity, DIGEST_LEN);
memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest,
DIGEST_LEN);
rs->addr = ri->addr;
@@ -2401,7 +2441,7 @@ static void
clear_status_flags_on_sybil(routerstatus_t *rs)
{
rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
- rs->is_running = rs->is_named = rs->is_valid = rs->is_v2_dir =
+ rs->is_flagged_running = rs->is_named = rs->is_valid = rs->is_v2_dir =
rs->is_hs_dir = rs->is_possible_guard = rs->is_bad_exit =
rs->is_bad_directory = 0;
/* FFFF we might want some mechanism to check later on if we
@@ -2409,18 +2449,6 @@ clear_status_flags_on_sybil(routerstatus_t *rs)
* forget to add it to this clause. */
}
-/** Clear all the status flags in routerinfo <b>router</b>. We put this
- * function here because it's eerily similar to
- * clear_status_flags_on_sybil() above. One day we should merge them. */
-void
-router_clear_status_flags(routerinfo_t *router)
-{
- router->is_valid = router->is_running = router->is_hs_dir =
- router->is_fast = router->is_stable =
- router->is_possible_guard = router->is_exit =
- router->is_bad_exit = router->is_bad_directory = 0;
-}
-
/**
* Helper function to parse out a line in the measured bandwidth file
* into a measured_bw_line_t output structure. Returns -1 on failure
@@ -2538,7 +2566,7 @@ dirserv_read_measured_bandwidths(const char *from_file,
smartlist_t *routerstatuses)
{
char line[256];
- FILE *fp = fopen(from_file, "r");
+ FILE *fp = tor_fopen_cloexec(from_file, "r");
int applied_lines = 0;
time_t file_time;
int ok;
@@ -2598,7 +2626,7 @@ networkstatus_t *
dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
authority_cert_t *cert)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
networkstatus_t *v3_out = NULL;
uint32_t addr;
char *hostname = NULL, *client_versions = NULL, *server_versions = NULL;
@@ -2669,10 +2697,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
routerstatus_t *rs;
vote_routerstatus_t *vrs;
microdesc_t *md;
+ node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+ if (!node)
+ continue;
vrs = tor_malloc_zero(sizeof(vote_routerstatus_t));
rs = &vrs->status;
- set_routerstatus_from_routerinfo(rs, ri, now,
+ set_routerstatus_from_routerinfo(rs, node, ri, now,
naming, listbadexits, listbaddirs,
vote_on_hsdirs);
@@ -2680,7 +2711,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_env_t *private_key,
clear_status_flags_on_sybil(rs);
if (!vote_on_reachability)
- rs->is_running = 0;
+ rs->is_flagged_running = 0;
vrs->version = version_from_platform(ri->platform);
md = dirvote_create_microdescriptor(ri);
@@ -2814,12 +2845,10 @@ generate_v2_networkstatus_opinion(void)
char *status = NULL, *client_versions = NULL, *server_versions = NULL,
*identity_pkey = NULL, *hostname = NULL;
char *outp, *endp;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
char fingerprint[FINGERPRINT_LEN+1];
- char ipaddr[INET_NTOA_BUF_LEN];
char published[ISO_TIME_LEN+1];
char digest[DIGEST_LEN];
- struct in_addr in;
uint32_t addr;
crypto_pk_env_t *private_key;
routerlist_t *rl = router_get_routerlist();
@@ -2841,8 +2870,6 @@ generate_v2_networkstatus_opinion(void)
log_warn(LD_NET, "Couldn't resolve my hostname");
goto done;
}
- in.s_addr = htonl(addr);
- tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr));
format_iso_time(published, now);
@@ -2888,7 +2915,7 @@ generate_v2_networkstatus_opinion(void)
"dir-options%s%s%s%s\n"
"%s" /* client version line, server version line. */
"dir-signing-key\n%s",
- hostname, ipaddr,
+ hostname, fmt_addr32(addr),
(int)router_get_advertised_dir_port(options, 0),
fingerprint,
contact,
@@ -2920,8 +2947,12 @@ generate_v2_networkstatus_opinion(void)
if (ri->cache_info.published_on >= cutoff) {
routerstatus_t rs;
char *version = version_from_platform(ri->platform);
-
- set_routerstatus_from_routerinfo(&rs, ri, now,
+ node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+ if (!node) {
+ tor_free(version);
+ continue;
+ }
+ set_routerstatus_from_routerinfo(&rs, node, ri, now,
naming, listbadexits, listbaddirs,
vote_on_hsdirs);
@@ -3005,7 +3036,7 @@ dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result,
if (!strcmp(key,"authority")) {
if (authdir_mode_v2(get_options())) {
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
if (me)
smartlist_add(result,
tor_memdup(me->cache_info.identity_digest, DIGEST_LEN));
@@ -3024,7 +3055,7 @@ dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result,
} else {
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, ds,
- if (ds->type & V2_AUTHORITY)
+ if (ds->type & V2_DIRINFO)
smartlist_add(result, tor_memdup(ds->digest, DIGEST_LEN)));
}
smartlist_sort_digests(result);
@@ -3093,7 +3124,7 @@ dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
/* Treat "all" requests as if they were unencrypted */
for_unencrypted_conn = 1;
} else if (!strcmp(key, "authority")) {
- routerinfo_t *ri = router_get_my_routerinfo();
+ const routerinfo_t *ri = router_get_my_routerinfo();
if (ri)
smartlist_add(fps_out,
tor_memdup(ri->cache_info.identity_digest, DIGEST_LEN));
@@ -3113,8 +3144,8 @@ dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
if (for_unencrypted_conn) {
/* Remove anything that insists it not be sent unencrypted. */
- SMARTLIST_FOREACH(fps_out, char *, cp, {
- signed_descriptor_t *sd;
+ SMARTLIST_FOREACH_BEGIN(fps_out, char *, cp) {
+ const signed_descriptor_t *sd;
if (by_id)
sd = get_signed_descriptor_by_fp(cp,is_extrainfo,0);
else if (is_extrainfo)
@@ -3125,7 +3156,7 @@ dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
tor_free(cp);
SMARTLIST_DEL_CURRENT(fps_out, cp);
}
- });
+ } SMARTLIST_FOREACH_END(cp);
}
if (!smartlist_len(fps_out)) {
@@ -3164,9 +3195,9 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
smartlist_add(descs_out, &(r->cache_info)));
} else if (!strcmp(key, "/tor/server/authority")) {
- routerinfo_t *ri = router_get_my_routerinfo();
+ const routerinfo_t *ri = router_get_my_routerinfo();
if (ri)
- smartlist_add(descs_out, &(ri->cache_info));
+ smartlist_add(descs_out, (void*) &(ri->cache_info));
} else if (!strcmpstart(key, "/tor/server/d/")) {
smartlist_t *digests = smartlist_create();
key += strlen("/tor/server/d/");
@@ -3190,17 +3221,17 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
{
if (router_digest_is_me(d)) {
/* make sure desc_routerinfo exists */
- routerinfo_t *ri = router_get_my_routerinfo();
+ const routerinfo_t *ri = router_get_my_routerinfo();
if (ri)
- smartlist_add(descs_out, &(ri->cache_info));
+ smartlist_add(descs_out, (void*) &(ri->cache_info));
} else {
- routerinfo_t *ri = router_get_by_digest(d);
+ const routerinfo_t *ri = router_get_by_id_digest(d);
/* Don't actually serve a descriptor that everyone will think is
* expired. This is an (ugly) workaround to keep buggy 0.1.1.10
* Tors from downloading descriptors that they will throw away.
*/
if (ri && ri->cache_info.published_on > cutoff)
- smartlist_add(descs_out, &(ri->cache_info));
+ smartlist_add(descs_out, (void*) &(ri->cache_info));
}
});
SMARTLIST_FOREACH(digests, char *, d, tor_free(d));
@@ -3249,7 +3280,7 @@ dirserv_orconn_tls_done(const char *address,
log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.",
router_describe(ri),
address, ri->or_port);
- if (tor_addr_from_str(&addr, ri->address) != -1)
+ if (tor_addr_parse(&addr, ri->address) != -1)
addrp = &addr;
else
log_warn(LD_BUG, "Couldn't parse IP address \"%s\"", ri->address);
@@ -3267,7 +3298,8 @@ dirserv_orconn_tls_done(const char *address,
* an upload or a download. Used to decide whether to relaunch reachability
* testing for the server. */
int
-dirserv_should_launch_reachability_test(routerinfo_t *ri, routerinfo_t *ri_old)
+dirserv_should_launch_reachability_test(const routerinfo_t *ri,
+ const routerinfo_t *ri_old)
{
if (!authdir_mode_handles_descs(get_options(), ri->purpose))
return 0;
@@ -3391,7 +3423,7 @@ dirserv_remove_old_statuses(smartlist_t *fps, time_t cutoff)
* its extra-info document if <b>extrainfo</b> is true. Return
* NULL if not found or if the descriptor is older than
* <b>publish_cutoff</b>. */
-static signed_descriptor_t *
+static const signed_descriptor_t *
get_signed_descriptor_by_fp(const char *fp, int extrainfo,
time_t publish_cutoff)
{
@@ -3401,7 +3433,7 @@ get_signed_descriptor_by_fp(const char *fp, int extrainfo,
else
return &(router_get_my_routerinfo()->cache_info);
} else {
- routerinfo_t *ri = router_get_by_digest(fp);
+ const routerinfo_t *ri = router_get_by_id_digest(fp);
if (ri &&
ri->cache_info.published_on > publish_cutoff) {
if (extrainfo)
@@ -3469,7 +3501,7 @@ dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs,
tor_assert(fps);
if (is_serverdescs) {
int n = smartlist_len(fps);
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
result = (me?me->cache_info.signed_descriptor_len:2048) * n;
if (compressed)
result /= 2; /* observed compressibility is between 35 and 55%. */
@@ -3523,20 +3555,19 @@ connection_dirserv_finish_spooling(dir_connection_t *conn)
static int
connection_dirserv_add_servers_to_outbuf(dir_connection_t *conn)
{
-#ifdef TRACK_SERVED_TIME
- time_t now = time(NULL);
-#endif
int by_fp = (conn->dir_spool_src == DIR_SPOOL_SERVER_BY_FP ||
conn->dir_spool_src == DIR_SPOOL_EXTRA_BY_FP);
int extra = (conn->dir_spool_src == DIR_SPOOL_EXTRA_BY_FP ||
conn->dir_spool_src == DIR_SPOOL_EXTRA_BY_DIGEST);
time_t publish_cutoff = time(NULL)-ROUTER_MAX_AGE_TO_PUBLISH;
+ const or_options_t *options = get_options();
+
while (smartlist_len(conn->fingerprint_stack) &&
- buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
+ connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) {
const char *body;
char *fp = smartlist_pop_last(conn->fingerprint_stack);
- signed_descriptor_t *sd = NULL;
+ const signed_descriptor_t *sd = NULL;
if (by_fp) {
sd = get_signed_descriptor_by_fp(fp, extra, publish_cutoff);
} else {
@@ -3553,9 +3584,16 @@ connection_dirserv_add_servers_to_outbuf(dir_connection_t *conn)
* unknown bridge descriptor has shown up between then and now. */
continue;
}
-#ifdef TRACK_SERVED_TIME
- sd->last_served_at = now;
-#endif
+
+ /** If we are the bridge authority and the descriptor is a bridge
+ * descriptor, remember that we served this descriptor for desc stats. */
+ if (options->BridgeAuthoritativeDir && by_fp) {
+ const routerinfo_t *router =
+ router_get_by_id_digest(sd->identity_digest);
+ tor_assert(router);
+ if (router->purpose == ROUTER_PURPOSE_BRIDGE)
+ rep_hist_note_desc_served(sd->identity_digest);
+ }
body = signed_descriptor_get_body(sd);
if (conn->zlib_state) {
/* XXXX022 This 'last' business should actually happen on the last
@@ -3594,7 +3632,7 @@ connection_dirserv_add_microdescs_to_outbuf(dir_connection_t *conn)
{
microdesc_cache_t *cache = get_microdesc_cache();
while (smartlist_len(conn->fingerprint_stack) &&
- buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
+ connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) {
char *fp256 = smartlist_pop_last(conn->fingerprint_stack);
microdesc_t *md = microdesc_cache_lookup_by_digest256(cache, fp256);
tor_free(fp256);
@@ -3633,7 +3671,7 @@ connection_dirserv_add_dir_bytes_to_outbuf(dir_connection_t *conn)
ssize_t bytes;
int64_t remaining;
- bytes = DIRSERV_BUFFER_MIN - buf_datalen(conn->_base.outbuf);
+ bytes = DIRSERV_BUFFER_MIN - connection_get_outbuf_len(TO_CONN(conn));
tor_assert(bytes > 0);
tor_assert(conn->cached_dir);
if (bytes < 8192)
@@ -3672,7 +3710,7 @@ static int
connection_dirserv_add_networkstatus_bytes_to_outbuf(dir_connection_t *conn)
{
- while (buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
+ while (connection_get_outbuf_len(TO_CONN(conn)) < DIRSERV_BUFFER_MIN) {
if (conn->cached_dir) {
int uncompressing = (conn->zlib_state != NULL);
int r = connection_dirserv_add_dir_bytes_to_outbuf(conn);
@@ -3718,7 +3756,7 @@ connection_dirserv_flushed_some(dir_connection_t *conn)
{
tor_assert(conn->_base.state == DIR_CONN_STATE_SERVER_WRITING);
- if (buf_datalen(conn->_base.outbuf) >= DIRSERV_BUFFER_MIN)
+ if (connection_get_outbuf_len(TO_CONN(conn)) >= DIRSERV_BUFFER_MIN)
return 0;
switch (conn->dir_spool_src) {
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index 569abfca2..d3fd90ceb 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -52,8 +52,6 @@
MAX_V_LINE_LEN \
)
-#define UNNAMED_ROUTER_NICKNAME "Unnamed"
-
int connection_dirserv_flushed_some(dir_connection_t *conn);
int dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk);
@@ -73,15 +71,16 @@ int list_server_status_v1(smartlist_t *routers, char **router_status_out,
int dirserv_dump_directory_to_string(char **dir_out,
crypto_pk_env_t *private_key);
-int directory_fetches_from_authorities(or_options_t *options);
-int directory_fetches_dir_info_early(or_options_t *options);
-int directory_fetches_dir_info_later(or_options_t *options);
-int directory_caches_v2_dir_info(or_options_t *options);
+int directory_fetches_from_authorities(const or_options_t *options);
+int directory_fetches_dir_info_early(const or_options_t *options);
+int directory_fetches_dir_info_later(const or_options_t *options);
+int directory_caches_v2_dir_info(const or_options_t *options);
#define directory_caches_v1_dir_info(o) directory_caches_v2_dir_info(o)
-int directory_caches_dir_info(or_options_t *options);
-int directory_permits_begindir_requests(or_options_t *options);
-int directory_permits_controller_requests(or_options_t *options);
-int directory_too_idle_to_fetch_descriptors(or_options_t *options, time_t now);
+int directory_caches_dir_info(const or_options_t *options);
+int directory_permits_begindir_requests(const or_options_t *options);
+int directory_permits_controller_requests(const or_options_t *options);
+int directory_too_idle_to_fetch_descriptors(const or_options_t *options,
+ time_t now);
void directory_set_dirty(void);
cached_dir_t *dirserv_get_directory(void);
@@ -111,13 +110,19 @@ void dirserv_orconn_tls_done(const char *address,
uint16_t or_port,
const char *digest_rcvd,
int as_advertised);
-int dirserv_should_launch_reachability_test(routerinfo_t *ri,
- routerinfo_t *ri_old);
+int dirserv_should_launch_reachability_test(const routerinfo_t *ri,
+ const routerinfo_t *ri_old);
void dirserv_single_reachability_test(time_t now, routerinfo_t *router);
void dirserv_test_reachability(time_t now);
int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
- int complain);
-int dirserv_would_reject_router(routerstatus_t *rs);
+ int complain,
+ int *valid_out);
+uint32_t dirserv_router_get_status(const routerinfo_t *router,
+ const char **msg);
+void dirserv_set_node_flags_from_authoritative_status(node_t *node,
+ uint32_t authstatus);
+
+int dirserv_would_reject_router(const routerstatus_t *rs);
int dirserv_remove_old_statuses(smartlist_t *fps, time_t cutoff);
int dirserv_have_any_serverdesc(smartlist_t *fps, int spool_src);
int dirserv_have_any_microdesc(const smartlist_t *fps);
@@ -126,7 +131,7 @@ size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs,
size_t dirserv_estimate_microdesc_size(const smartlist_t *fps, int compressed);
int routerstatus_format_entry(char *buf, size_t buf_len,
- routerstatus_t *rs, const char *platform,
+ const routerstatus_t *rs, const char *platform,
routerstatus_format_type_t format);
void dirserv_free_all(void);
void cached_dir_decref(cached_dir_t *d);
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index c6ce9f677..bf34c62af 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -83,9 +83,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
const char *client_versions = NULL, *server_versions = NULL;
char *outp, *endp;
char fingerprint[FINGERPRINT_LEN+1];
- char ipaddr[INET_NTOA_BUF_LEN];
char digest[DIGEST_LEN];
- struct in_addr in;
uint32_t addr;
routerlist_t *rl = router_get_routerlist();
char *version_lines = NULL;
@@ -98,8 +96,6 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
voter = smartlist_get(v3_ns->voters, 0);
addr = voter->addr;
- in.s_addr = htonl(addr);
- tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr));
base16_encode(fingerprint, sizeof(fingerprint),
v3_ns->cert->cache_info.identity_digest, DIGEST_LEN);
@@ -186,7 +182,8 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
flags,
params,
voter->nickname, fingerprint, voter->address,
- ipaddr, voter->dir_port, voter->or_port, voter->contact);
+ fmt_addr32(addr), voter->dir_port, voter->or_port,
+ voter->contact);
if (r < 0) {
log_err(LD_BUG, "Insufficient memory for network status line");
@@ -1530,8 +1527,6 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_sort(dir_sources, _compare_dir_src_ents_by_authority_id);
SMARTLIST_FOREACH_BEGIN(dir_sources, const dir_src_ent_t *, e) {
- struct in_addr in;
- char ip[INET_NTOA_BUF_LEN];
char fingerprint[HEX_DIGEST_LEN+1];
char votedigest[HEX_DIGEST_LEN+1];
networkstatus_t *v = e->v;
@@ -1541,8 +1536,6 @@ networkstatus_compute_consensus(smartlist_t *votes,
if (e->is_legacy)
tor_assert(consensus_method >= 2);
- in.s_addr = htonl(voter->addr);
- tor_inet_ntoa(&in, ip, sizeof(ip));
base16_encode(fingerprint, sizeof(fingerprint), e->digest, DIGEST_LEN);
base16_encode(votedigest, sizeof(votedigest), voter->vote_digest,
DIGEST_LEN);
@@ -1550,7 +1543,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
tor_asprintf(&buf,
"dir-source %s%s %s %s %s %d %d\n",
voter->nickname, e->is_legacy ? "-legacy" : "",
- fingerprint, voter->address, ip,
+ fingerprint, voter->address, fmt_addr32(voter->addr),
voter->dir_port,
voter->or_port);
smartlist_add(chunks, buf);
@@ -2511,7 +2504,7 @@ authority_cert_dup(authority_cert_t *cert)
void
dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
tor_assert(timing_out);
@@ -2585,7 +2578,7 @@ static struct {
/** Set voting_schedule to hold the timing for the next vote we should be
* doing. */
void
-dirvote_recalculate_timing(or_options_t *options, time_t now)
+dirvote_recalculate_timing(const or_options_t *options, time_t now)
{
int interval, vote_delay, dist_delay;
time_t start;
@@ -2636,7 +2629,7 @@ dirvote_recalculate_timing(or_options_t *options, time_t now)
/** Entry point: Take whatever voting actions are pending as of <b>now</b>. */
void
-dirvote_act(or_options_t *options, time_t now)
+dirvote_act(const or_options_t *options, time_t now)
{
if (!authdir_mode_v3(options))
return;
@@ -2751,7 +2744,7 @@ dirvote_perform_vote(void)
directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_VOTE,
ROUTER_PURPOSE_GENERAL,
- V3_AUTHORITY,
+ V3_DIRINFO,
pending_vote->vote_body->dir,
pending_vote->vote_body->dir_len, 0);
log_notice(LD_DIR, "Vote posted.");
@@ -2770,7 +2763,7 @@ dirvote_fetch_missing_votes(void)
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, ds,
{
- if (!(ds->type & V3_AUTHORITY))
+ if (!(ds->type & V3_DIRINFO))
continue;
if (!dirvote_get_vote(ds->v3_identity_digest,
DGV_BY_ID|DGV_INCLUDE_PENDING)) {
@@ -2883,7 +2876,7 @@ list_v3_auth_ids(void)
char *keys;
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, ds,
- if ((ds->type & V3_AUTHORITY) &&
+ if ((ds->type & V3_DIRINFO) &&
!tor_digest_is_zero(ds->v3_identity_digest))
smartlist_add(known_v3_keys,
tor_strdup(hex_str(ds->v3_identity_digest, DIGEST_LEN))));
@@ -3078,7 +3071,7 @@ dirvote_compute_consensuses(void)
if (!pending_vote_list)
pending_vote_list = smartlist_create();
- n_voters = get_n_authorities(V3_AUTHORITY);
+ n_voters = get_n_authorities(V3_DIRINFO);
n_votes = smartlist_len(pending_vote_list);
if (n_votes <= n_voters/2) {
log_warn(LD_DIR, "We don't have enough votes to generate a consensus: "
@@ -3217,7 +3210,7 @@ dirvote_compute_consensuses(void)
directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_SIGNATURES,
ROUTER_PURPOSE_GENERAL,
- V3_AUTHORITY,
+ V3_DIRINFO,
pending_consensus_signatures,
strlen(pending_consensus_signatures), 0);
log_notice(LD_DIR, "Signature(s) posted.");
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index de11fcf99..d19635173 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -41,8 +41,8 @@ authority_cert_t *authority_cert_dup(authority_cert_t *cert);
/* vote scheduling */
void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out);
time_t dirvote_get_start_of_next_interval(time_t now, int interval);
-void dirvote_recalculate_timing(or_options_t *options, time_t now);
-void dirvote_act(or_options_t *options, time_t now);
+void dirvote_recalculate_timing(const or_options_t *options, time_t now);
+void dirvote_act(const or_options_t *options, time_t now);
/* invoked on timers and by outside triggers. */
struct pending_vote_t * dirvote_add_vote(const char *vote_body,
@@ -60,6 +60,7 @@ const char *dirvote_get_pending_detached_signatures(void);
#define DGV_INCLUDE_PREVIOUS 4
const cached_dir_t *dirvote_get_vote(const char *fp, int flags);
void set_routerstatus_from_routerinfo(routerstatus_t *rs,
+ node_t *node,
routerinfo_t *ri, time_t now,
int naming, int listbadexits,
int listbaddirs, int vote_on_hsdirs);
diff --git a/src/or/dns.c b/src/or/dns.c
index 9b6b98afa..8ed953690 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -276,7 +276,7 @@ dns_init(void)
int
dns_reset(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (! server_mode(options)) {
if (!the_evdns_base) {
@@ -675,7 +675,7 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
cached_resolve_t *resolve;
cached_resolve_t search;
pending_connection_t *pending_connection;
- routerinfo_t *me;
+ const routerinfo_t *me;
tor_addr_t addr;
time_t now = time(NULL);
uint8_t is_reverse = 0;
@@ -687,7 +687,7 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
/* first check if exitconn->_base.address is an IP. If so, we already
* know the answer. */
- if (tor_addr_from_str(&addr, exitconn->_base.address) >= 0) {
+ if (tor_addr_parse(&addr, exitconn->_base.address) >= 0) {
if (tor_addr_family(&addr) == AF_INET) {
tor_addr_copy(&exitconn->_base.addr, &addr);
exitconn->address_ttl = DEFAULT_DNS_TTL;
@@ -721,7 +721,7 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve,
* .in-addr.arpa address but this isn't a resolve request, kill the
* connection.
*/
- if ((r = tor_addr_parse_reverse_lookup_name(&addr, exitconn->_base.address,
+ if ((r = tor_addr_parse_PTR_name(&addr, exitconn->_base.address,
AF_UNSPEC, 0)) != 0) {
if (r == 1) {
is_reverse = 1;
@@ -1026,7 +1026,7 @@ add_answer_to_cache(const char *address, uint8_t is_reverse, uint32_t addr,
static INLINE int
is_test_address(const char *address)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
return options->ServerDNSTestAddresses &&
smartlist_string_isin_case(options->ServerDNSTestAddresses, address);
}
@@ -1177,7 +1177,7 @@ evdns_err_is_transient(int err)
static int
configure_nameservers(int force)
{
- or_options_t *options;
+ const or_options_t *options;
const char *conf_fname;
struct stat st;
int r;
@@ -1198,7 +1198,7 @@ configure_nameservers(int force)
#ifdef HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS
if (options->OutboundBindAddress) {
tor_addr_t addr;
- if (tor_addr_from_str(&addr, options->OutboundBindAddress) < 0) {
+ if (tor_addr_parse(&addr, options->OutboundBindAddress) < 0) {
log_warn(LD_CONFIG,"Outbound bind address '%s' didn't parse. Ignoring.",
options->OutboundBindAddress);
} else {
@@ -1404,7 +1404,7 @@ launch_resolve(edge_connection_t *exitconn)
}
}
- r = tor_addr_parse_reverse_lookup_name(
+ r = tor_addr_parse_PTR_name(
&a, exitconn->_base.address, AF_UNSPEC, 0);
tor_assert(the_evdns_base);
@@ -1595,7 +1595,7 @@ launch_wildcard_check(int min_len, int max_len, const char *suffix)
static void
launch_test_addresses(int fd, short event, void *args)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
struct evdns_request *req;
(void)fd;
(void)event;
diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c
index f2c473dfc..7f519398f 100644
--- a/src/or/dnsserv.c
+++ b/src/or/dnsserv.c
@@ -29,8 +29,10 @@
* DNSPort. We need to eventually answer the request <b>req</b>.
*/
static void
-evdns_server_callback(struct evdns_server_request *req, void *_data)
+evdns_server_callback(struct evdns_server_request *req, void *data_)
{
+ const listener_connection_t *listener = data_;
+ entry_connection_t *entry_conn;
edge_connection_t *conn;
int i = 0;
struct evdns_server_question *q = NULL;
@@ -43,7 +45,7 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
char *q_name;
tor_assert(req);
- tor_assert(_data == NULL);
+
log_info(LD_APP, "Got a new DNS request!");
req->flags |= 0x80; /* set RA */
@@ -114,8 +116,9 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
}
/* Make a new dummy AP connection, and attach the request to it. */
- conn = edge_connection_new(CONN_TYPE_AP, AF_INET);
- conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
+ entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
+ conn = ENTRY_TO_EDGE_CONN(entry_conn);
+ TO_CONN(conn)->state = AP_CONN_STATE_RESOLVE_WAIT;
conn->is_dns_request = 1;
tor_addr_copy(&TO_CONN(conn)->addr, &tor_addr);
@@ -123,23 +126,27 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
TO_CONN(conn)->address = tor_dup_addr(&tor_addr);
if (q->type == EVDNS_TYPE_A)
- conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
+ entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
else
- conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
+ entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
- strlcpy(conn->socks_request->address, q->name,
- sizeof(conn->socks_request->address));
+ strlcpy(entry_conn->socks_request->address, q->name,
+ sizeof(entry_conn->socks_request->address));
- conn->dns_server_request = req;
+ entry_conn->socks_request->listener_type = listener->_base.type;
+ entry_conn->dns_server_request = req;
+ entry_conn->isolation_flags = listener->isolation_flags;
+ entry_conn->session_group = listener->session_group;
+ entry_conn->nym_epoch = get_signewnym_epoch();
- if (connection_add(TO_CONN(conn)) < 0) {
+ if (connection_add(ENTRY_TO_CONN(entry_conn)) < 0) {
log_warn(LD_APP, "Couldn't register dummy connection for DNS request");
evdns_server_request_respond(req, DNS_ERR_SERVERFAILED);
- connection_free(TO_CONN(conn));
+ connection_free(ENTRY_TO_CONN(entry_conn));
return;
}
- control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
+ control_event_stream_status(entry_conn, STREAM_EVENT_NEW, 0);
/* Now, unless a controller asked us to leave streams unattached,
* throw the connection over to get rewritten (which will
@@ -148,7 +155,7 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
log_info(LD_APP, "Passing request for %s to rewrite_and_attach.",
escaped_safe_str_client(q->name));
q_name = tor_strdup(q->name); /* q could be freed in rewrite_and_attach */
- connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
+ connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL);
/* Now, the connection is marked if it was bad. */
log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.",
@@ -164,22 +171,30 @@ evdns_server_callback(struct evdns_server_request *req, void *_data)
int
dnsserv_launch_request(const char *name, int reverse)
{
+ entry_connection_t *entry_conn;
edge_connection_t *conn;
char *q_name;
/* Make a new dummy AP connection, and attach the request to it. */
- conn = edge_connection_new(CONN_TYPE_AP, AF_INET);
+ entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET);
+ conn = ENTRY_TO_EDGE_CONN(entry_conn);
conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
if (reverse)
- conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
+ entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
else
- conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
+ entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE;
conn->is_dns_request = 1;
- strlcpy(conn->socks_request->address, name,
- sizeof(conn->socks_request->address));
+ strlcpy(entry_conn->socks_request->address, name,
+ sizeof(entry_conn->socks_request->address));
+
+ entry_conn->socks_request->listener_type = CONN_TYPE_CONTROL_LISTENER;
+ entry_conn->original_dest_address = tor_strdup(name);
+ entry_conn->session_group = SESSION_GROUP_CONTROL_RESOLVE;
+ entry_conn->nym_epoch = get_signewnym_epoch();
+ entry_conn->isolation_flags = ISO_DEFAULT;
if (connection_add(TO_CONN(conn))<0) {
log_warn(LD_APP, "Couldn't register dummy connection for RESOLVE request");
@@ -194,7 +209,7 @@ dnsserv_launch_request(const char *name, int reverse)
log_info(LD_APP, "Passing request for %s to rewrite_and_attach.",
escaped_safe_str_client(name));
q_name = tor_strdup(name); /* q could be freed in rewrite_and_attach */
- connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
+ connection_ap_rewrite_and_attach_if_allowed(entry_conn, NULL, NULL);
/* Now, the connection is marked if it was bad. */
log_info(LD_APP, "Passed request for %s to rewrite_and_attach_if_allowed.",
@@ -206,7 +221,7 @@ dnsserv_launch_request(const char *name, int reverse)
/** If there is a pending request on <b>conn</b> that's waiting for an answer,
* send back an error and free the request. */
void
-dnsserv_reject_request(edge_connection_t *conn)
+dnsserv_reject_request(entry_connection_t *conn)
{
if (conn->dns_server_request) {
evdns_server_request_respond(conn->dns_server_request,
@@ -252,7 +267,7 @@ evdns_get_orig_address(const struct evdns_server_request *req,
* <b>answer_len</b>, in <b>answer</b>, with TTL <b>ttl</b>. Doesn't do
* any caching; that's handled elsewhere. */
void
-dnsserv_resolved(edge_connection_t *conn,
+dnsserv_resolved(entry_connection_t *conn,
int answer_type,
size_t answer_len,
const char *answer,
@@ -305,12 +320,15 @@ dnsserv_resolved(edge_connection_t *conn,
void
dnsserv_configure_listener(connection_t *conn)
{
+ listener_connection_t *listener_conn;
tor_assert(conn);
tor_assert(SOCKET_OK(conn->s));
tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);
- conn->dns_server_port =
- tor_evdns_add_server_port(conn->s, 0, evdns_server_callback, NULL);
+ listener_conn = TO_LISTENER_CONN(conn);
+ listener_conn->dns_server_port =
+ tor_evdns_add_server_port(conn->s, 0, evdns_server_callback,
+ listener_conn);
}
/** Free the evdns server port for <b>conn</b>, which must be an
@@ -318,12 +336,15 @@ dnsserv_configure_listener(connection_t *conn)
void
dnsserv_close_listener(connection_t *conn)
{
+ listener_connection_t *listener_conn;
tor_assert(conn);
tor_assert(conn->type == CONN_TYPE_AP_DNS_LISTENER);
- if (conn->dns_server_port) {
- evdns_close_server_port(conn->dns_server_port);
- conn->dns_server_port = NULL;
+ listener_conn = TO_LISTENER_CONN(conn);
+
+ if (listener_conn->dns_server_port) {
+ evdns_close_server_port(listener_conn->dns_server_port);
+ listener_conn->dns_server_port = NULL;
}
}
diff --git a/src/or/dnsserv.h b/src/or/dnsserv.h
index fcca86888..73ec36564 100644
--- a/src/or/dnsserv.h
+++ b/src/or/dnsserv.h
@@ -14,12 +14,12 @@
void dnsserv_configure_listener(connection_t *conn);
void dnsserv_close_listener(connection_t *conn);
-void dnsserv_resolved(edge_connection_t *conn,
+void dnsserv_resolved(entry_connection_t *conn,
int answer_type,
size_t answer_len,
const char *answer,
int ttl);
-void dnsserv_reject_request(edge_connection_t *conn);
+void dnsserv_reject_request(entry_connection_t *conn);
int dnsserv_launch_request(const char *name, int is_reverse);
#endif
diff --git a/src/or/eventdns.c b/src/or/eventdns.c
index 42e16aec7..7fe376baf 100644
--- a/src/or/eventdns.c
+++ b/src/or/eventdns.c
@@ -1831,8 +1831,8 @@ evdns_server_request_respond(struct evdns_server_request *_req, int err)
r = sendto(port->socket, req->response, req->response_len, 0,
(struct sockaddr*) &req->addr, req->addrlen);
if (r<0) {
- int err = last_error(port->socket);
- if (! error_is_eagain(err))
+ int error = last_error(port->socket);
+ if (! error_is_eagain(error))
return -1;
if (port->pending_replies) {
@@ -2291,7 +2291,7 @@ _evdns_nameserver_add_impl(const struct sockaddr *address,
evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
- ns->socket = socket(address->sa_family, SOCK_DGRAM, 0);
+ ns->socket = tor_open_socket(address->sa_family, SOCK_DGRAM, 0);
if (ns->socket < 0) { err = 1; goto out1; }
#ifdef WIN32
{
@@ -3040,7 +3040,7 @@ evdns_resolv_conf_parse(int flags, const char *const filename) {
log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename);
- fd = open(filename, O_RDONLY);
+ fd = tor_open_cloexec(filename, O_RDONLY, 0);
if (fd < 0) {
evdns_resolv_set_defaults(flags);
return 1;
@@ -3460,7 +3460,7 @@ main(int c, char **v) {
if (servertest) {
int sock;
struct sockaddr_in my_addr;
- sock = socket(PF_INET, SOCK_DGRAM, 0);
+ sock = tor_open_socket(PF_INET, SOCK_DGRAM, 0);
fcntl(sock, F_SETFL, O_NONBLOCK);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(10053);
diff --git a/src/or/geoip.c b/src/or/geoip.c
index c51142c82..73194ae9c 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -44,6 +44,9 @@ static strmap_t *country_idxplus1_by_lc_code = NULL;
/** A list of all known geoip_entry_t, sorted by ip_low. */
static smartlist_t *geoip_entries = NULL;
+/** SHA1 digest of the GeoIP file to include in extra-info descriptors. */
+static char geoip_digest[DIGEST_LEN];
+
/** Return the index of the <b>country</b>'s entry in the GeoIP DB
* if it is a valid 2-letter country code, otherwise return -1.
*/
@@ -113,10 +116,10 @@ geoip_parse_entry(const char *line)
++line;
if (*line == '#')
return 0;
- if (sscanf(line,"%u,%u,%2s", &low, &high, b) == 3) {
+ if (tor_sscanf(line,"%u,%u,%2s", &low, &high, b) == 3) {
geoip_add_entry(low, high, b);
return 0;
- } else if (sscanf(line,"\"%u\",\"%u\",\"%2s\",", &low, &high, b) == 3) {
+ } else if (tor_sscanf(line,"\"%u\",\"%u\",\"%2s\",", &low, &high, b) == 3) {
geoip_add_entry(low, high, b);
return 0;
} else {
@@ -159,7 +162,7 @@ _geoip_compare_key_to_entry(const void *_key, const void **_member)
/** Return 1 if we should collect geoip stats on bridge users, and
* include them in our extrainfo descriptor. Else return 0. */
int
-should_record_bridge_info(or_options_t *options)
+should_record_bridge_info(const or_options_t *options)
{
return options->BridgeRelay && options->BridgeRecordUsageByCountry;
}
@@ -196,13 +199,14 @@ init_geoip_countries(void)
* with '#' (comments).
*/
int
-geoip_load_file(const char *filename, or_options_t *options)
+geoip_load_file(const char *filename, const or_options_t *options)
{
FILE *f;
const char *msg = "";
int severity = options_need_geoip_info(options, &msg) ? LOG_WARN : LOG_INFO;
+ crypto_digest_env_t *geoip_digest_env = NULL;
clear_geoip_db();
- if (!(f = fopen(filename, "r"))) {
+ if (!(f = tor_fopen_cloexec(filename, "r"))) {
log_fn(severity, LD_GENERAL, "Failed to open GEOIP file %s. %s",
filename, msg);
return -1;
@@ -214,11 +218,13 @@ geoip_load_file(const char *filename, or_options_t *options)
smartlist_free(geoip_entries);
}
geoip_entries = smartlist_create();
+ geoip_digest_env = crypto_new_digest_env();
log_notice(LD_GENERAL, "Parsing GEOIP file %s.", filename);
while (!feof(f)) {
char buf[512];
if (fgets(buf, (int)sizeof(buf), f) == NULL)
break;
+ crypto_digest_add_bytes(geoip_digest_env, buf, strlen(buf));
/* FFFF track full country name. */
geoip_parse_entry(buf);
}
@@ -231,6 +237,11 @@ geoip_load_file(const char *filename, or_options_t *options)
* country. */
refresh_all_country_info();
+ /* Remember file digest so that we can include it in our extra-info
+ * descriptors. */
+ crypto_digest_get_digest(geoip_digest_env, geoip_digest, DIGEST_LEN);
+ crypto_free_digest_env(geoip_digest_env);
+
return 0;
}
@@ -278,6 +289,15 @@ geoip_is_loaded(void)
return geoip_countries != NULL && geoip_entries != NULL;
}
+/** Return the hex-encoded SHA1 digest of the loaded GeoIP file. The
+ * result does not need to be deallocated, but will be overwritten by the
+ * next call of hex_str(). */
+const char *
+geoip_db_digest(void)
+{
+ return hex_str(geoip_digest, DIGEST_LEN);
+}
+
/** Entry in a map from IP address to the last time we've seen an incoming
* connection from that IP address. Used by bridges only, to track which
* countries have them blocked. */
@@ -404,7 +424,7 @@ void
geoip_note_client_seen(geoip_client_action_t action,
uint32_t addr, time_t now)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
clientmap_entry_t lookup, *ent;
if (action == GEOIP_CLIENT_CONNECT) {
/* Only remember statistics as entry guard or as bridge. */
@@ -910,10 +930,9 @@ geoip_dirreq_stats_init(time_t now)
start_of_dirreq_stats_interval = now;
}
-/** Stop collecting directory request stats in a way that we can re-start
- * doing so in geoip_dirreq_stats_init(). */
+/** Reset counters for dirreq stats. */
void
-geoip_dirreq_stats_term(void)
+geoip_reset_dirreq_stats(time_t now)
{
SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
c->n_v2_ns_requests = c->n_v3_ns_requests = 0;
@@ -945,59 +964,44 @@ geoip_dirreq_stats_term(void)
tor_free(this);
}
}
- start_of_dirreq_stats_interval = 0;
+ start_of_dirreq_stats_interval = now;
}
-/** Write dirreq statistics to $DATADIR/stats/dirreq-stats and return when
- * we would next want to write. */
-time_t
-geoip_dirreq_stats_write(time_t now)
+/** Stop collecting directory request stats in a way that we can re-start
+ * doing so in geoip_dirreq_stats_init(). */
+void
+geoip_dirreq_stats_term(void)
{
- char *statsdir = NULL, *filename = NULL;
- char *data_v2 = NULL, *data_v3 = NULL;
- char written[ISO_TIME_LEN+1];
- open_file_t *open_file = NULL;
+ geoip_reset_dirreq_stats(0);
+}
+
+/** Return a newly allocated string containing the dirreq statistics
+ * until <b>now</b>, or NULL if we're not collecting dirreq stats. Caller
+ * must ensure start_of_dirreq_stats_interval is in the past. */
+char *
+geoip_format_dirreq_stats(time_t now)
+{
+ char t[ISO_TIME_LEN+1];
double v2_share = 0.0, v3_share = 0.0;
- FILE *out;
int i;
+ char *v3_ips_string, *v2_ips_string, *v3_reqs_string, *v2_reqs_string,
+ *v2_share_string = NULL, *v3_share_string = NULL,
+ *v3_direct_dl_string, *v2_direct_dl_string,
+ *v3_tunneled_dl_string, *v2_tunneled_dl_string;
+ char *result;
if (!start_of_dirreq_stats_interval)
- return 0; /* Not initialized. */
- if (start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL > now)
- goto done; /* Not ready to write. */
+ return NULL; /* Not initialized. */
- /* Discard all items in the client history that are too old. */
- geoip_remove_old_clients(start_of_dirreq_stats_interval);
+ tor_assert(now >= start_of_dirreq_stats_interval);
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0)
- goto done;
- filename = get_datadir_fname2("stats", "dirreq-stats");
- data_v2 = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2);
- data_v3 = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS);
- format_iso_time(written, now);
- out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND | O_TEXT,
- 0600, &open_file);
- if (!out)
- goto done;
- if (fprintf(out, "dirreq-stats-end %s (%d s)\ndirreq-v3-ips %s\n"
- "dirreq-v2-ips %s\n", written,
- (unsigned) (now - start_of_dirreq_stats_interval),
- data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0)
- goto done;
- tor_free(data_v2);
- tor_free(data_v3);
+ format_iso_time(t, now);
+ v2_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS_V2);
+ v3_ips_string = geoip_get_client_history(GEOIP_CLIENT_NETWORKSTATUS);
+ v2_reqs_string = geoip_get_request_history(
+ GEOIP_CLIENT_NETWORKSTATUS_V2);
+ v3_reqs_string = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS);
- data_v2 = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS_V2);
- data_v3 = geoip_get_request_history(GEOIP_CLIENT_NETWORKSTATUS);
- if (fprintf(out, "dirreq-v3-reqs %s\ndirreq-v2-reqs %s\n",
- data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0)
- goto done;
- tor_free(data_v2);
- tor_free(data_v3);
- SMARTLIST_FOREACH(geoip_countries, geoip_country_t *, c, {
- c->n_v2_ns_requests = c->n_v3_ns_requests = 0;
- });
#define RESPONSE_GRANULARITY 8
for (i = 0; i < GEOIP_NS_RESPONSE_NUM; i++) {
ns_v2_responses[i] = round_uint32_to_next_multiple_of(
@@ -1006,61 +1010,117 @@ geoip_dirreq_stats_write(time_t now)
ns_v3_responses[i], RESPONSE_GRANULARITY);
}
#undef RESPONSE_GRANULARITY
- if (fprintf(out, "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
- "not-found=%u,not-modified=%u,busy=%u\n",
- ns_v3_responses[GEOIP_SUCCESS],
- ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS],
- ns_v3_responses[GEOIP_REJECT_UNAVAILABLE],
- ns_v3_responses[GEOIP_REJECT_NOT_FOUND],
- ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED],
- ns_v3_responses[GEOIP_REJECT_BUSY]) < 0)
- goto done;
- if (fprintf(out, "dirreq-v2-resp ok=%u,unavailable=%u,"
- "not-found=%u,not-modified=%u,busy=%u\n",
- ns_v2_responses[GEOIP_SUCCESS],
- ns_v2_responses[GEOIP_REJECT_UNAVAILABLE],
- ns_v2_responses[GEOIP_REJECT_NOT_FOUND],
- ns_v2_responses[GEOIP_REJECT_NOT_MODIFIED],
- ns_v2_responses[GEOIP_REJECT_BUSY]) < 0)
- goto done;
- memset(ns_v2_responses, 0, sizeof(ns_v2_responses));
- memset(ns_v3_responses, 0, sizeof(ns_v3_responses));
+
if (!geoip_get_mean_shares(now, &v2_share, &v3_share)) {
- if (fprintf(out, "dirreq-v2-share %0.2lf%%\n", v2_share*100) < 0)
- goto done;
- if (fprintf(out, "dirreq-v3-share %0.2lf%%\n", v3_share*100) < 0)
- goto done;
+ tor_asprintf(&v2_share_string, "dirreq-v2-share %0.2lf%%\n",
+ v2_share*100);
+ tor_asprintf(&v3_share_string, "dirreq-v3-share %0.2lf%%\n",
+ v3_share*100);
}
- data_v2 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2,
- DIRREQ_DIRECT);
- data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS,
- DIRREQ_DIRECT);
- if (fprintf(out, "dirreq-v3-direct-dl %s\ndirreq-v2-direct-dl %s\n",
- data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0)
- goto done;
- tor_free(data_v2);
- tor_free(data_v3);
- data_v2 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS_V2,
- DIRREQ_TUNNELED);
- data_v3 = geoip_get_dirreq_history(GEOIP_CLIENT_NETWORKSTATUS,
- DIRREQ_TUNNELED);
- if (fprintf(out, "dirreq-v3-tunneled-dl %s\ndirreq-v2-tunneled-dl %s\n",
- data_v3 ? data_v3 : "", data_v2 ? data_v2 : "") < 0)
- goto done;
+ v2_direct_dl_string = geoip_get_dirreq_history(
+ GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_DIRECT);
+ v3_direct_dl_string = geoip_get_dirreq_history(
+ GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_DIRECT);
+
+ v2_tunneled_dl_string = geoip_get_dirreq_history(
+ GEOIP_CLIENT_NETWORKSTATUS_V2, DIRREQ_TUNNELED);
+ v3_tunneled_dl_string = geoip_get_dirreq_history(
+ GEOIP_CLIENT_NETWORKSTATUS, DIRREQ_TUNNELED);
+
+ /* Put everything together into a single string. */
+ tor_asprintf(&result, "dirreq-stats-end %s (%d s)\n"
+ "dirreq-v3-ips %s\n"
+ "dirreq-v2-ips %s\n"
+ "dirreq-v3-reqs %s\n"
+ "dirreq-v2-reqs %s\n"
+ "dirreq-v3-resp ok=%u,not-enough-sigs=%u,unavailable=%u,"
+ "not-found=%u,not-modified=%u,busy=%u\n"
+ "dirreq-v2-resp ok=%u,unavailable=%u,"
+ "not-found=%u,not-modified=%u,busy=%u\n"
+ "%s"
+ "%s"
+ "dirreq-v3-direct-dl %s\n"
+ "dirreq-v2-direct-dl %s\n"
+ "dirreq-v3-tunneled-dl %s\n"
+ "dirreq-v2-tunneled-dl %s\n",
+ t,
+ (unsigned) (now - start_of_dirreq_stats_interval),
+ v3_ips_string ? v3_ips_string : "",
+ v2_ips_string ? v2_ips_string : "",
+ v3_reqs_string ? v3_reqs_string : "",
+ v2_reqs_string ? v2_reqs_string : "",
+ ns_v3_responses[GEOIP_SUCCESS],
+ ns_v3_responses[GEOIP_REJECT_NOT_ENOUGH_SIGS],
+ ns_v3_responses[GEOIP_REJECT_UNAVAILABLE],
+ ns_v3_responses[GEOIP_REJECT_NOT_FOUND],
+ ns_v3_responses[GEOIP_REJECT_NOT_MODIFIED],
+ ns_v3_responses[GEOIP_REJECT_BUSY],
+ ns_v2_responses[GEOIP_SUCCESS],
+ ns_v2_responses[GEOIP_REJECT_UNAVAILABLE],
+ ns_v2_responses[GEOIP_REJECT_NOT_FOUND],
+ ns_v2_responses[GEOIP_REJECT_NOT_MODIFIED],
+ ns_v2_responses[GEOIP_REJECT_BUSY],
+ v2_share_string ? v2_share_string : "",
+ v3_share_string ? v3_share_string : "",
+ v3_direct_dl_string ? v3_direct_dl_string : "",
+ v2_direct_dl_string ? v2_direct_dl_string : "",
+ v3_tunneled_dl_string ? v3_tunneled_dl_string : "",
+ v2_tunneled_dl_string ? v2_tunneled_dl_string : "");
+
+ /* Free partial strings. */
+ tor_free(v3_ips_string);
+ tor_free(v2_ips_string);
+ tor_free(v3_reqs_string);
+ tor_free(v2_reqs_string);
+ tor_free(v2_share_string);
+ tor_free(v3_share_string);
+ tor_free(v3_direct_dl_string);
+ tor_free(v2_direct_dl_string);
+ tor_free(v3_tunneled_dl_string);
+ tor_free(v2_tunneled_dl_string);
- finish_writing_to_file(open_file);
- open_file = NULL;
+ return result;
+}
- start_of_dirreq_stats_interval = now;
+/** If 24 hours have passed since the beginning of the current dirreq
+ * stats period, write dirreq stats to $DATADIR/stats/dirreq-stats
+ * (possibly overwriting an existing file) and reset counters. Return
+ * when we would next want to write dirreq stats or 0 if we never want to
+ * write. */
+time_t
+geoip_dirreq_stats_write(time_t now)
+{
+ char *statsdir = NULL, *filename = NULL, *str = NULL;
+
+ if (!start_of_dirreq_stats_interval)
+ return 0; /* Not initialized. */
+ if (start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL > now)
+ goto done; /* Not ready to write. */
+
+ /* Discard all items in the client history that are too old. */
+ geoip_remove_old_clients(start_of_dirreq_stats_interval);
+
+ /* Generate history string .*/
+ str = geoip_format_dirreq_stats(now);
+
+ /* Write dirreq-stats string to disk. */
+ statsdir = get_datadir_fname("stats");
+ if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+ log_warn(LD_HIST, "Unable to create stats/ directory!");
+ goto done;
+ }
+ filename = get_datadir_fname2("stats", "dirreq-stats");
+ if (write_str_to_file(filename, str, 0) < 0)
+ log_warn(LD_HIST, "Unable to write dirreq statistics to disk!");
+
+ /* Reset measurement interval start. */
+ geoip_reset_dirreq_stats(now);
done:
- if (open_file)
- abort_writing_to_file(open_file);
- tor_free(filename);
tor_free(statsdir);
- tor_free(data_v2);
- tor_free(data_v3);
+ tor_free(filename);
+ tor_free(str);
return start_of_dirreq_stats_interval + WRITE_STATS_INTERVAL;
}
@@ -1140,8 +1200,8 @@ static char *bridge_stats_extrainfo = NULL;
/** Return a newly allocated string holding our bridge usage stats by country
* in a format suitable for inclusion in an extrainfo document. Return NULL on
* failure. */
-static char *
-format_bridge_stats_extrainfo(time_t now)
+char *
+geoip_format_bridge_stats(time_t now)
{
char *out = NULL, *data = NULL;
long duration = now - start_of_bridge_stats_interval;
@@ -1149,6 +1209,8 @@ format_bridge_stats_extrainfo(time_t now)
if (duration < 0)
return NULL;
+ if (!start_of_bridge_stats_interval)
+ return NULL; /* Not initialized. */
format_iso_time(written, now);
data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
@@ -1198,7 +1260,7 @@ geoip_bridge_stats_write(time_t now)
geoip_remove_old_clients(start_of_bridge_stats_interval);
/* Generate formatted string */
- val = format_bridge_stats_extrainfo(now);
+ val = geoip_format_bridge_stats(now);
if (val == NULL)
goto done;
@@ -1275,25 +1337,54 @@ geoip_entry_stats_init(time_t now)
start_of_entry_stats_interval = now;
}
+/** Reset counters for entry stats. */
+void
+geoip_reset_entry_stats(time_t now)
+{
+ client_history_clear();
+ start_of_entry_stats_interval = now;
+}
+
/** Stop collecting entry stats in a way that we can re-start doing so in
* geoip_entry_stats_init(). */
void
geoip_entry_stats_term(void)
{
- client_history_clear();
- start_of_entry_stats_interval = 0;
+ geoip_reset_entry_stats(0);
}
-/** Write entry statistics to $DATADIR/stats/entry-stats and return time
- * when we would next want to write. */
+/** Return a newly allocated string containing the entry statistics
+ * until <b>now</b>, or NULL if we're not collecting entry stats. Caller
+ * must ensure start_of_entry_stats_interval lies in the past. */
+char *
+geoip_format_entry_stats(time_t now)
+{
+ char t[ISO_TIME_LEN+1];
+ char *data = NULL;
+ char *result;
+
+ if (!start_of_entry_stats_interval)
+ return NULL; /* Not initialized. */
+
+ tor_assert(now >= start_of_entry_stats_interval);
+
+ data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
+ format_iso_time(t, now);
+ tor_asprintf(&result, "entry-stats-end %s (%u s)\nentry-ips %s\n",
+ t, (unsigned) (now - start_of_entry_stats_interval),
+ data ? data : "");
+ tor_free(data);
+ return result;
+}
+
+/** If 24 hours have passed since the beginning of the current entry stats
+ * period, write entry stats to $DATADIR/stats/entry-stats (possibly
+ * overwriting an existing file) and reset counters. Return when we would
+ * next want to write entry stats or 0 if we never want to write. */
time_t
geoip_entry_stats_write(time_t now)
{
- char *statsdir = NULL, *filename = NULL;
- char *data = NULL;
- char written[ISO_TIME_LEN+1];
- open_file_t *open_file = NULL;
- FILE *out;
+ char *statsdir = NULL, *filename = NULL, *str = NULL;
if (!start_of_entry_stats_interval)
return 0; /* Not initialized. */
@@ -1303,31 +1394,26 @@ geoip_entry_stats_write(time_t now)
/* Discard all items in the client history that are too old. */
geoip_remove_old_clients(start_of_entry_stats_interval);
+ /* Generate history string .*/
+ str = geoip_format_entry_stats(now);
+
+ /* Write entry-stats string to disk. */
statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0)
+ if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+ log_warn(LD_HIST, "Unable to create stats/ directory!");
goto done;
+ }
filename = get_datadir_fname2("stats", "entry-stats");
- data = geoip_get_client_history(GEOIP_CLIENT_CONNECT);
- format_iso_time(written, now);
- out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND | O_TEXT,
- 0600, &open_file);
- if (!out)
- goto done;
- if (fprintf(out, "entry-stats-end %s (%u s)\nentry-ips %s\n",
- written, (unsigned) (now - start_of_entry_stats_interval),
- data ? data : "") < 0)
- goto done;
+ if (write_str_to_file(filename, str, 0) < 0)
+ log_warn(LD_HIST, "Unable to write entry statistics to disk!");
- start_of_entry_stats_interval = now;
+ /* Reset measurement interval start. */
+ geoip_reset_entry_stats(now);
- finish_writing_to_file(open_file);
- open_file = NULL;
done:
- if (open_file)
- abort_writing_to_file(open_file);
- tor_free(filename);
tor_free(statsdir);
- tor_free(data);
+ tor_free(filename);
+ tor_free(str);
return start_of_entry_stats_interval + WRITE_STATS_INTERVAL;
}
diff --git a/src/or/geoip.h b/src/or/geoip.h
index 24f7c5b93..ce3841967 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -15,12 +15,13 @@
#ifdef GEOIP_PRIVATE
int geoip_parse_entry(const char *line);
#endif
-int should_record_bridge_info(or_options_t *options);
-int geoip_load_file(const char *filename, or_options_t *options);
+int should_record_bridge_info(const or_options_t *options);
+int geoip_load_file(const char *filename, const or_options_t *options);
int geoip_get_country_by_ip(uint32_t ipaddr);
int geoip_get_n_countries(void);
const char *geoip_get_country_name(country_t num);
int geoip_is_loaded(void);
+const char *geoip_db_digest(void);
country_t geoip_get_country(const char *countrycode);
void geoip_note_client_seen(geoip_client_action_t action,
@@ -42,12 +43,17 @@ void geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
dirreq_state_t new_state);
void geoip_dirreq_stats_init(time_t now);
+void geoip_reset_dirreq_stats(time_t now);
+char *geoip_format_dirreq_stats(time_t now);
time_t geoip_dirreq_stats_write(time_t now);
void geoip_dirreq_stats_term(void);
void geoip_entry_stats_init(time_t now);
time_t geoip_entry_stats_write(time_t now);
void geoip_entry_stats_term(void);
+void geoip_reset_entry_stats(time_t now);
+char *geoip_format_entry_stats(time_t now);
void geoip_bridge_stats_init(time_t now);
+char *geoip_format_bridge_stats(time_t now);
time_t geoip_bridge_stats_write(time_t now);
void geoip_bridge_stats_term(void);
const char *geoip_get_bridge_stats_extrainfo(time_t);
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 2f7170fa2..6fd2b4f19 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -21,6 +21,7 @@ hibernating, phase 2:
- close all OR/AP/exit conns)
*/
+#define HIBERNATE_PRIVATE
#include "or.h"
#include "config.h"
#include "connection.h"
@@ -29,26 +30,11 @@ hibernating, phase 2:
#include "main.h"
#include "router.h"
-/** Possible values of hibernate_state */
-typedef enum {
- /** We are running normally. */
- HIBERNATE_STATE_LIVE=1,
- /** We're trying to shut down cleanly, and we'll kill all active connections
- * at shutdown_time. */
- HIBERNATE_STATE_EXITING=2,
- /** We're running low on allocated bandwidth for this period, so we won't
- * accept any new connections. */
- HIBERNATE_STATE_LOWBANDWIDTH=3,
- /** We are hibernating, and we won't wake up till there's more bandwidth to
- * use. */
- HIBERNATE_STATE_DORMANT=4
-} hibernate_state_t;
-
extern long stats_n_seconds_working; /* published uptime */
/** Are we currently awake, asleep, running out of bandwidth, or shutting
* down? */
-static hibernate_state_t hibernate_state = HIBERNATE_STATE_LIVE;
+static hibernate_state_t hibernate_state = HIBERNATE_STATE_INITIAL;
/** If are hibernating, when do we plan to wake up? Set to 0 if we
* aren't hibernating. */
static time_t hibernate_end_time = 0;
@@ -134,7 +120,7 @@ static void accounting_set_wakeup_time(void);
* options->AccountingStart. Return 0 on success, -1 on failure. If
* <b>validate_only</b> is true, do not change the current settings. */
int
-accounting_parse_options(or_options_t *options, int validate_only)
+accounting_parse_options(const or_options_t *options, int validate_only)
{
time_unit_t unit;
int ok, idx;
@@ -249,7 +235,7 @@ accounting_parse_options(or_options_t *options, int validate_only)
* hibernate, return 1, else return 0.
*/
int
-accounting_is_enabled(or_options_t *options)
+accounting_is_enabled(const or_options_t *options)
{
if (options->AccountingMax)
return 1;
@@ -411,7 +397,7 @@ static void
update_expected_bandwidth(void)
{
uint64_t expected;
- or_options_t *options= get_options();
+ const or_options_t *options= get_options();
uint64_t max_configured = (options->RelayBandwidthRate > 0 ?
options->RelayBandwidthRate :
options->BandwidthRate) * 60;
@@ -750,7 +736,7 @@ static void
hibernate_begin(hibernate_state_t new_state, time_t now)
{
connection_t *conn;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (new_state == HIBERNATE_STATE_EXITING &&
hibernate_state != HIBERNATE_STATE_LIVE) {
@@ -804,10 +790,12 @@ static void
hibernate_end(hibernate_state_t new_state)
{
tor_assert(hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH ||
- hibernate_state == HIBERNATE_STATE_DORMANT);
+ hibernate_state == HIBERNATE_STATE_DORMANT ||
+ hibernate_state == HIBERNATE_STATE_INITIAL);
/* listeners will be relaunched in run_scheduled_events() in main.c */
- log_notice(LD_ACCT,"Hibernation period ended. Resuming normal activity.");
+ if (hibernate_state != HIBERNATE_STATE_INITIAL)
+ log_notice(LD_ACCT,"Hibernation period ended. Resuming normal activity.");
hibernate_state = new_state;
hibernate_end_time = 0; /* no longer hibernating */
@@ -856,7 +844,7 @@ hibernate_go_dormant(time_t now)
connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_HIBERNATING);
log_info(LD_NET,"Closing conn type %d", conn->type);
if (conn->type == CONN_TYPE_AP) /* send socks failure if needed */
- connection_mark_unattached_ap(TO_EDGE_CONN(conn),
+ connection_mark_unattached_ap(TO_ENTRY_CONN(conn),
END_STREAM_REASON_HIBERNATING);
else
connection_mark_for_close(conn);
@@ -939,7 +927,8 @@ consider_hibernation(time_t now)
/* Else, we aren't hibernating. See if it's time to start hibernating, or to
* go dormant. */
- if (hibernate_state == HIBERNATE_STATE_LIVE) {
+ if (hibernate_state == HIBERNATE_STATE_LIVE ||
+ hibernate_state == HIBERNATE_STATE_INITIAL) {
if (hibernate_soft_limit_reached()) {
log_notice(LD_ACCT,
"Bandwidth soft limit reached; commencing hibernation. "
@@ -951,6 +940,8 @@ consider_hibernation(time_t now)
"Commencing hibernation. We will wake up at %s local time.",
buf);
hibernate_go_dormant(now);
+ } else if (hibernate_state == HIBERNATE_STATE_INITIAL) {
+ hibernate_end(HIBERNATE_STATE_LIVE);
}
}
@@ -1017,3 +1008,13 @@ getinfo_helper_accounting(control_connection_t *conn,
return 0;
}
+/**
+ * Manually change the hibernation state. Private; used only by the unit
+ * tests.
+ */
+void
+hibernate_set_state_for_testing_(hibernate_state_t newstate)
+{
+ hibernate_state = newstate;
+}
+
diff --git a/src/or/hibernate.h b/src/or/hibernate.h
index 2aea0fab0..78e7bb75e 100644
--- a/src/or/hibernate.h
+++ b/src/or/hibernate.h
@@ -12,8 +12,8 @@
#ifndef _TOR_HIBERNATE_H
#define _TOR_HIBERNATE_H
-int accounting_parse_options(or_options_t *options, int validate_only);
-int accounting_is_enabled(or_options_t *options);
+int accounting_parse_options(const or_options_t *options, int validate_only);
+int accounting_is_enabled(const or_options_t *options);
void configure_accounting(time_t now);
void accounting_run_housekeeping(time_t now);
void accounting_add_bytes(size_t n_read, size_t n_written, int seconds);
@@ -25,5 +25,27 @@ int getinfo_helper_accounting(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
+#ifdef HIBERNATE_PRIVATE
+/** Possible values of hibernate_state */
+typedef enum {
+ /** We are running normally. */
+ HIBERNATE_STATE_LIVE=1,
+ /** We're trying to shut down cleanly, and we'll kill all active connections
+ * at shutdown_time. */
+ HIBERNATE_STATE_EXITING=2,
+ /** We're running low on allocated bandwidth for this period, so we won't
+ * accept any new connections. */
+ HIBERNATE_STATE_LOWBANDWIDTH=3,
+ /** We are hibernating, and we won't wake up till there's more bandwidth to
+ * use. */
+ HIBERNATE_STATE_DORMANT=4,
+ /** We start out in state default, which means we havent decided which state
+ * we're in. */
+ HIBERNATE_STATE_INITIAL=5
+} hibernate_state_t;
+
+void hibernate_set_state_for_testing_(hibernate_state_t newstate);
+#endif
+
#endif
diff --git a/src/or/main.c b/src/or/main.c
index b1159746a..c1a7015e6 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -33,9 +33,11 @@
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "ntmain.h"
#include "onion.h"
#include "policies.h"
+#include "transports.h"
#include "relay.h"
#include "rendclient.h"
#include "rendcommon.h"
@@ -44,6 +46,7 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "status.h"
#ifdef USE_DMALLOC
#include <dmalloc.h>
#include <openssl/crypto.h>
@@ -56,6 +59,10 @@
#include <event.h>
#endif
+#ifdef USE_BUFFEREVENTS
+#include <event2/bufferevent.h>
+#endif
+
void evdns_shutdown(int);
/********* PROTOTYPES **********/
@@ -71,6 +78,7 @@ static int connection_should_read_from_linked_conn(connection_t *conn);
/********* START VARIABLES **********/
+#ifndef USE_BUFFEREVENTS
int global_read_bucket; /**< Max number of bytes I can read this second. */
int global_write_bucket; /**< Max number of bytes I can write this second. */
@@ -78,13 +86,17 @@ int global_write_bucket; /**< Max number of bytes I can write this second. */
int global_relayed_read_bucket;
/** Max number of relayed (bandwidth class 1) bytes I can write this second. */
int global_relayed_write_bucket;
-
/** What was the read bucket before the last second_elapsed_callback() call?
* (used to determine how many bytes we've read). */
static int stats_prev_global_read_bucket;
/** What was the write bucket before the last second_elapsed_callback() call?
* (used to determine how many bytes we've written). */
static int stats_prev_global_write_bucket;
+#endif
+
+static uint64_t stats_prev_n_read = 0;
+static uint64_t stats_prev_n_written = 0;
+
/* XXX we might want to keep stats about global_relayed_*_bucket too. Or not.*/
/** How many bytes have we read since we started the process? */
static uint64_t stats_n_bytes_read = 0;
@@ -103,6 +115,8 @@ static time_t time_to_check_for_correct_dns = 0;
static time_t time_of_last_signewnym = 0;
/** Is there a signewnym request we're currently waiting to handle? */
static int signewnym_is_pending = 0;
+/** How many times have we called newnym? */
+static unsigned newnym_epoch = 0;
/** Smartlist of all open connections. */
static smartlist_t *connection_array = NULL;
@@ -141,6 +155,12 @@ int can_complete_circuit=0;
* they are obsolete? */
#define TLS_HANDSHAKE_TIMEOUT (60)
+/** Decides our behavior when no logs are configured/before any
+ * logs have been configured. For 0, we log notice to stdout as normal.
+ * For 1, we log warnings only. For 2, we log nothing.
+ */
+int quiet_level = 0;
+
/********* END VARIABLES ************/
/****************************************************************************
@@ -150,12 +170,38 @@ int can_complete_circuit=0;
*
****************************************************************************/
+#if 0 && defined(USE_BUFFEREVENTS)
+static void
+free_old_inbuf(connection_t *conn)
+{
+ if (! conn->inbuf)
+ return;
+
+ tor_assert(conn->outbuf);
+ tor_assert(buf_datalen(conn->inbuf) == 0);
+ tor_assert(buf_datalen(conn->outbuf) == 0);
+ buf_free(conn->inbuf);
+ buf_free(conn->outbuf);
+ conn->inbuf = conn->outbuf = NULL;
+
+ if (conn->read_event) {
+ event_del(conn->read_event);
+ tor_event_free(conn->read_event);
+ }
+ if (conn->write_event) {
+ event_del(conn->read_event);
+ tor_event_free(conn->write_event);
+ }
+ conn->read_event = conn->write_event = NULL;
+}
+#endif
+
/** Add <b>conn</b> to the array of connections that we can poll on. The
* connection's socket must be set; the connection starts out
* non-reading and non-writing.
*/
int
-connection_add(connection_t *conn)
+connection_add_impl(connection_t *conn, int is_connecting)
{
tor_assert(conn);
tor_assert(SOCKET_OK(conn->s) ||
@@ -167,11 +213,63 @@ connection_add(connection_t *conn)
conn->conn_array_index = smartlist_len(connection_array);
smartlist_add(connection_array, conn);
- if (SOCKET_OK(conn->s) || conn->linked) {
+#ifdef USE_BUFFEREVENTS
+ if (connection_type_uses_bufferevent(conn)) {
+ if (SOCKET_OK(conn->s) && !conn->linked) {
+ conn->bufev = bufferevent_socket_new(
+ tor_libevent_get_base(),
+ conn->s,
+ BEV_OPT_DEFER_CALLBACKS);
+ if (!conn->bufev) {
+ log_warn(LD_BUG, "Unable to create socket bufferevent");
+ smartlist_del(connection_array, conn->conn_array_index);
+ conn->conn_array_index = -1;
+ return -1;
+ }
+ if (is_connecting) {
+ /* Put the bufferevent into a "connecting" state so that we'll get
+ * a "connected" event callback on successful write. */
+ bufferevent_socket_connect(conn->bufev, NULL, 0);
+ }
+ connection_configure_bufferevent_callbacks(conn);
+ } else if (conn->linked && conn->linked_conn &&
+ connection_type_uses_bufferevent(conn->linked_conn)) {
+ tor_assert(!(SOCKET_OK(conn->s)));
+ if (!conn->bufev) {
+ struct bufferevent *pair[2] = { NULL, NULL };
+ if (bufferevent_pair_new(tor_libevent_get_base(),
+ BEV_OPT_DEFER_CALLBACKS,
+ pair) < 0) {
+ log_warn(LD_BUG, "Unable to create bufferevent pair");
+ smartlist_del(connection_array, conn->conn_array_index);
+ conn->conn_array_index = -1;
+ return -1;
+ }
+ tor_assert(pair[0]);
+ conn->bufev = pair[0];
+ conn->linked_conn->bufev = pair[1];
+ } /* else the other side already was added, and got a bufferevent_pair */
+ connection_configure_bufferevent_callbacks(conn);
+ } else {
+ tor_assert(!conn->linked);
+ }
+
+ if (conn->bufev)
+ tor_assert(conn->inbuf == NULL);
+
+ if (conn->linked_conn && conn->linked_conn->bufev)
+ tor_assert(conn->linked_conn->inbuf == NULL);
+ }
+#else
+ (void) is_connecting;
+#endif
+
+ if (!HAS_BUFFEREVENT(conn) && (SOCKET_OK(conn->s) || conn->linked)) {
conn->read_event = tor_event_new(tor_libevent_get_base(),
conn->s, EV_READ|EV_PERSIST, conn_read_callback, conn);
conn->write_event = tor_event_new(tor_libevent_get_base(),
conn->s, EV_WRITE|EV_PERSIST, conn_write_callback, conn);
+ /* XXXX CHECK FOR NULL RETURN! */
}
log_debug(LD_NET,"new conn type %s, socket %d, address %s, n_conns %d.",
@@ -195,7 +293,13 @@ connection_unregister_events(connection_t *conn)
log_warn(LD_BUG, "Error removing write event for %d", (int)conn->s);
tor_free(conn->write_event);
}
- if (conn->dns_server_port) {
+#ifdef USE_BUFFEREVENTS
+ if (conn->bufev) {
+ bufferevent_free(conn->bufev);
+ conn->bufev = NULL;
+ }
+#endif
+ if (conn->type == CONN_TYPE_AP_DNS_LISTENER) {
dnsserv_close_listener(conn);
}
}
@@ -303,12 +407,37 @@ get_connection_array(void)
return connection_array;
}
+/** Provides the traffic read and written over the life of the process. */
+
+uint64_t
+get_bytes_read(void)
+{
+ return stats_n_bytes_read;
+}
+
+uint64_t
+get_bytes_written(void)
+{
+ return stats_n_bytes_written;
+}
+
/** Set the event mask on <b>conn</b> to <b>events</b>. (The event
* mask is a bitmask whose bits are READ_EVENT and WRITE_EVENT)
*/
void
connection_watch_events(connection_t *conn, watchable_events_t events)
{
+ IF_HAS_BUFFEREVENT(conn, {
+ short ev = ((short)events) & (EV_READ|EV_WRITE);
+ short old_ev = bufferevent_get_enabled(conn->bufev);
+ if ((ev & ~old_ev) != 0) {
+ bufferevent_enable(conn->bufev, ev);
+ }
+ if ((old_ev & ~ev) != 0) {
+ bufferevent_disable(conn->bufev, old_ev & ~ev);
+ }
+ return;
+ });
if (events & READ_EVENT)
connection_start_reading(conn);
else
@@ -326,6 +455,9 @@ connection_is_reading(connection_t *conn)
{
tor_assert(conn);
+ IF_HAS_BUFFEREVENT(conn,
+ return (bufferevent_get_enabled(conn->bufev) & EV_READ) != 0;
+ );
return conn->reading_from_linked_conn ||
(conn->read_event && event_pending(conn->read_event, EV_READ, NULL));
}
@@ -335,6 +467,12 @@ void
connection_stop_reading(connection_t *conn)
{
tor_assert(conn);
+
+ IF_HAS_BUFFEREVENT(conn, {
+ bufferevent_disable(conn->bufev, EV_READ);
+ return;
+ });
+
tor_assert(conn->read_event);
if (conn->linked) {
@@ -354,6 +492,12 @@ void
connection_start_reading(connection_t *conn)
{
tor_assert(conn);
+
+ IF_HAS_BUFFEREVENT(conn, {
+ bufferevent_enable(conn->bufev, EV_READ);
+ return;
+ });
+
tor_assert(conn->read_event);
if (conn->linked) {
@@ -375,6 +519,10 @@ connection_is_writing(connection_t *conn)
{
tor_assert(conn);
+ IF_HAS_BUFFEREVENT(conn,
+ return (bufferevent_get_enabled(conn->bufev) & EV_WRITE) != 0;
+ );
+
return conn->writing_to_linked_conn ||
(conn->write_event && event_pending(conn->write_event, EV_WRITE, NULL));
}
@@ -384,6 +532,12 @@ void
connection_stop_writing(connection_t *conn)
{
tor_assert(conn);
+
+ IF_HAS_BUFFEREVENT(conn, {
+ bufferevent_disable(conn->bufev, EV_WRITE);
+ return;
+ });
+
tor_assert(conn->write_event);
if (conn->linked) {
@@ -404,6 +558,12 @@ void
connection_start_writing(connection_t *conn)
{
tor_assert(conn);
+
+ IF_HAS_BUFFEREVENT(conn, {
+ bufferevent_enable(conn->bufev, EV_WRITE);
+ return;
+ });
+
tor_assert(conn->write_event);
if (conn->linked) {
@@ -590,9 +750,32 @@ conn_close_if_marked(int i)
assert_connection_ok(conn, now);
/* assert_all_pending_dns_resolves_ok(); */
- log_debug(LD_NET,"Cleaning up connection (fd %d).",(int)conn->s);
- if ((SOCKET_OK(conn->s) || conn->linked_conn)
- && connection_wants_to_flush(conn)) {
+#ifdef USE_BUFFEREVENTS
+ if (conn->bufev) {
+ if (conn->hold_open_until_flushed &&
+ evbuffer_get_length(bufferevent_get_output(conn->bufev))) {
+ /* don't close yet. */
+ return 0;
+ }
+ if (conn->linked_conn && ! conn->linked_conn->marked_for_close) {
+ /* We need to do this explicitly so that the linked connection
+ * notices that there was an EOF. */
+ bufferevent_flush(conn->bufev, EV_WRITE, BEV_FINISHED);
+ }
+ }
+#endif
+
+ log_debug(LD_NET,"Cleaning up connection (fd %d).",conn->s);
+
+ /* If the connection we are about to close was trying to connect to
+ a proxy server and failed, the client won't be able to use that
+ proxy. We should warn the user about this. */
+ if (conn->proxy_state == PROXY_INFANT)
+ log_failed_proxy_connection(conn);
+
+ IF_HAS_BUFFEREVENT(conn, goto unlink);
+ if ((SOCKET_OK(conn->s) || conn->linked_conn) &&
+ connection_wants_to_flush(conn)) {
/* s == -1 means it's an incomplete edge connection, or that the socket
* has already been closed as unflushable. */
ssize_t sz = connection_bucket_write_limit(conn, now);
@@ -614,8 +797,8 @@ conn_close_if_marked(int i)
}
log_debug(LD_GENERAL, "Flushed last %d bytes from a linked conn; "
"%d left; flushlen %d; wants-to-flush==%d", retval,
- (int)buf_datalen(conn->outbuf),
- (int)conn->outbuf_flushlen,
+ (int)connection_get_outbuf_len(conn),
+ (int)conn->outbuf_flushlen,
connection_wants_to_flush(conn));
} else if (connection_speaks_cells(conn)) {
if (conn->state == OR_CONN_STATE_OPEN) {
@@ -652,13 +835,17 @@ conn_close_if_marked(int i)
"something is wrong with your network connection, or "
"something is wrong with theirs. "
"(fd %d, type %s, state %d, marked at %s:%d).",
- (int)buf_datalen(conn->outbuf),
+ (int)connection_get_outbuf_len(conn),
escaped_safe_str_client(conn->address),
(int)conn->s, conn_type_to_string(conn->type), conn->state,
conn->marked_for_close_file,
conn->marked_for_close);
}
}
+
+#ifdef USE_BUFFEREVENTS
+ unlink:
+#endif
connection_unlink(conn); /* unlink, remove, free */
return 1;
}
@@ -679,13 +866,13 @@ directory_all_unreachable(time_t now)
while ((conn = connection_get_by_type_state(CONN_TYPE_AP,
AP_CONN_STATE_CIRCUIT_WAIT))) {
- edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
+ entry_connection_t *entry_conn = TO_ENTRY_CONN(conn);
log_notice(LD_NET,
"Is your network connection down? "
"Failing connection to '%s:%d'.",
- safe_str_client(edge_conn->socks_request->address),
- edge_conn->socks_request->port);
- connection_mark_unattached_ap(edge_conn,
+ safe_str_client(entry_conn->socks_request->address),
+ entry_conn->socks_request->port);
+ connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_NET_UNREACHABLE);
}
control_event_general_status(LOG_ERR, "DIR_ALL_UNREACHABLE");
@@ -696,18 +883,19 @@ directory_all_unreachable(time_t now)
void
directory_info_has_arrived(time_t now, int from_cache)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (!router_have_minimum_dir_info()) {
int quiet = directory_too_idle_to_fetch_descriptors(options, now);
log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR,
"I learned some more directory information, but not enough to "
"build a circuit: %s", get_dir_info_status_string());
- update_router_descriptor_downloads(now);
+ update_all_descriptor_downloads(now);
return;
} else {
- if (directory_fetches_from_authorities(options))
- update_router_descriptor_downloads(now);
+ if (directory_fetches_from_authorities(options)) {
+ update_all_descriptor_downloads(now);
+ }
/* if we have enough dir info, then update our guard status with
* whatever we just learned. */
@@ -740,12 +928,13 @@ run_connection_housekeeping(int i, time_t now)
{
cell_t cell;
connection_t *conn = smartlist_get(connection_array, i);
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
or_connection_t *or_conn;
int past_keepalive =
now >= conn->timestamp_lastwritten + options->KeepalivePeriod;
- if (conn->outbuf && !buf_datalen(conn->outbuf) && conn->type == CONN_TYPE_OR)
+ if (conn->outbuf && !connection_get_outbuf_len(conn) &&
+ conn->type == CONN_TYPE_OR)
TO_OR_CONN(conn)->timestamp_lastempty = now;
if (conn->marked_for_close) {
@@ -765,7 +954,7 @@ run_connection_housekeeping(int i, time_t now)
/* This check is temporary; it's to let us know whether we should consider
* parsing partial serverdesc responses. */
if (conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC &&
- buf_datalen(conn->inbuf)>=1024) {
+ connection_get_inbuf_len(conn) >= 1024) {
log_info(LD_DIR,"Trying to extract information from wedged server desc "
"download.");
connection_dir_reached_eof(TO_DIR_CONN(conn));
@@ -782,7 +971,11 @@ run_connection_housekeeping(int i, time_t now)
the connection or send a keepalive, depending. */
or_conn = TO_OR_CONN(conn);
+#ifdef USE_BUFFEREVENTS
+ tor_assert(conn->bufev);
+#else
tor_assert(conn->outbuf);
+#endif
if (or_conn->is_bad_for_new_circs && !or_conn->n_circuits) {
/* It's bad for new circuits, and has no unmarked circuits on it:
@@ -794,8 +987,7 @@ run_connection_housekeeping(int i, time_t now)
connection_or_connect_failed(TO_OR_CONN(conn),
END_OR_CONN_REASON_TIMEOUT,
"Tor gave up on the connection");
- connection_mark_for_close(conn);
- conn->hold_open_until_flushed = 1;
+ connection_mark_and_flush(conn);
} else if (!connection_state_is_open(conn)) {
if (past_keepalive) {
/* We never managed to actually get this connection open and happy. */
@@ -804,13 +996,12 @@ run_connection_housekeeping(int i, time_t now)
connection_mark_for_close(conn);
}
} else if (we_are_hibernating() && !or_conn->n_circuits &&
- !buf_datalen(conn->outbuf)) {
+ !connection_get_outbuf_len(conn)) {
/* We're hibernating, there's no circuits, and nothing to flush.*/
log_info(LD_OR,"Expiring non-used OR connection to fd %d (%s:%d) "
"[Hibernating or exiting].",
(int)conn->s,conn->address, conn->port);
- connection_mark_for_close(conn);
- conn->hold_open_until_flushed = 1;
+ connection_mark_and_flush(conn);
} else if (!or_conn->n_circuits &&
now >= or_conn->timestamp_last_added_nonpadding +
IDLE_OR_CONN_TIMEOUT) {
@@ -818,7 +1009,6 @@ run_connection_housekeeping(int i, time_t now)
"[idle %d].", (int)conn->s,conn->address, conn->port,
(int)(now - or_conn->timestamp_last_added_nonpadding));
connection_mark_for_close(conn);
- conn->hold_open_until_flushed = 1;
} else if (
now >= or_conn->timestamp_lastempty + options->KeepalivePeriod*10 &&
now >= conn->timestamp_lastwritten + options->KeepalivePeriod*10) {
@@ -826,10 +1016,10 @@ run_connection_housekeeping(int i, time_t now)
"Expiring stuck OR connection to fd %d (%s:%d). (%d bytes to "
"flush; %d seconds since last write)",
(int)conn->s, conn->address, conn->port,
- (int)buf_datalen(conn->outbuf),
+ (int)connection_get_outbuf_len(conn),
(int)(now-conn->timestamp_lastwritten));
connection_mark_for_close(conn);
- } else if (past_keepalive && !buf_datalen(conn->outbuf)) {
+ } else if (past_keepalive && !connection_get_outbuf_len(conn)) {
/* send a padding cell */
log_fn(LOG_DEBUG,LD_OR,"Sending keepalive to (%s:%d)",
conn->address, conn->port);
@@ -844,7 +1034,7 @@ run_connection_housekeeping(int i, time_t now)
static void
signewnym_impl(time_t now)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (!proxy_mode(options)) {
log_info(LD_CONTROL, "Ignoring SIGNAL NEWNYM because client functionality "
"is disabled.");
@@ -856,6 +1046,17 @@ signewnym_impl(time_t now)
rend_client_purge_state();
time_of_last_signewnym = now;
signewnym_is_pending = 0;
+
+ ++newnym_epoch;
+
+ control_event_signal(SIGNEWNYM);
+}
+
+/** Return the number of times that signewnym has been called. */
+unsigned
+get_signewnym_epoch(void)
+{
+ return newnym_epoch;
}
/** Perform regular maintenance tasks. This function gets run once per
@@ -881,10 +1082,14 @@ run_scheduled_events(time_t now)
static time_t time_to_check_for_expired_networkstatus = 0;
static time_t time_to_write_stats_files = 0;
static time_t time_to_write_bridge_stats = 0;
+ static time_t time_to_check_port_forwarding = 0;
static time_t time_to_launch_reachability_tests = 0;
static int should_init_bridge_stats = 1;
static time_t time_to_retry_dns_init = 0;
- or_options_t *options = get_options();
+ static time_t time_to_next_heartbeat = 0;
+ static int has_validated_pt = 0;
+ const or_options_t *options = get_options();
+
int is_server = server_mode(options);
int i;
int have_dir_info;
@@ -895,6 +1100,16 @@ run_scheduled_events(time_t now)
*/
consider_hibernation(now);
+#if 0
+ {
+ static time_t nl_check_time = 0;
+ if (nl_check_time <= now) {
+ nodelist_assert_ok();
+ nl_check_time = now + 30;
+ }
+ }
+#endif
+
/* 0b. If we've deferred a signewnym, make sure it gets handled
* eventually. */
if (signewnym_is_pending &&
@@ -923,7 +1138,7 @@ run_scheduled_events(time_t now)
}
if (time_to_try_getting_descriptors < now) {
- update_router_descriptor_downloads(now);
+ update_all_descriptor_downloads(now);
update_extrainfo_downloads(now);
if (router_have_minimum_dir_info())
time_to_try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL;
@@ -1048,6 +1263,16 @@ run_scheduled_events(time_t now)
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
+ if (options->ConnDirectionStatistics) {
+ time_t next_write = rep_hist_conn_stats_write(time_to_write_stats_files);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
+ if (options->BridgeAuthoritativeDir) {
+ time_t next_write = rep_hist_desc_stats_write(time_to_write_stats_files);
+ if (next_write && next_write < next_time_to_write_stats_files)
+ next_time_to_write_stats_files = next_write;
+ }
time_to_write_stats_files = next_time_to_write_stats_files;
}
@@ -1076,10 +1301,9 @@ run_scheduled_events(time_t now)
/* Remove old information from rephist and the rend cache. */
if (time_to_clean_caches < now) {
rep_history_clean(now - options->RephistTrackTime);
- rend_cache_clean();
- rend_cache_clean_v2_descs_as_dir();
- if (authdir_mode_v3(options))
- microdesc_cache_rebuild(NULL, 0);
+ rend_cache_clean(now);
+ rend_cache_clean_v2_descs_as_dir(now);
+ microdesc_cache_rebuild(NULL, 0);
#define CLEAN_CACHES_INTERVAL (30*60)
time_to_clean_caches = now + CLEAN_CACHES_INTERVAL;
}
@@ -1088,7 +1312,7 @@ run_scheduled_events(time_t now)
/* If we're a server and initializing dns failed, retry periodically. */
if (time_to_retry_dns_init < now) {
time_to_retry_dns_init = now + RETRY_DNS_INTERVAL;
- if (server_mode(options) && has_dns_init_failed())
+ if (is_server && has_dns_init_failed())
dns_init();
}
@@ -1111,15 +1335,11 @@ run_scheduled_events(time_t now)
time_to_check_ipaddress = now + CHECK_IPADDRESS_INTERVAL;
check_descriptor_ipaddress_changed(now);
}
-/** If our router descriptor ever goes this long without being regenerated
- * because something changed, we force an immediate regenerate-and-upload. */
-#define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (18*60*60)
- mark_my_descriptor_dirty_if_older_than(
- now - FORCE_REGENERATE_DESCRIPTOR_INTERVAL);
+ mark_my_descriptor_dirty_if_too_old(now);
consider_publishable_server(0);
/* also, check religiously for reachability, if it's within the first
* 20 minutes of our uptime. */
- if (server_mode(options) &&
+ if (is_server &&
(can_complete_circuit || !any_predicted_circuits(now)) &&
!we_are_hibernating()) {
if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
@@ -1130,7 +1350,7 @@ run_scheduled_events(time_t now)
/* If we haven't checked for 12 hours and our bandwidth estimate is
* low, do another bandwidth test. This is especially important for
* bridges, since they might go long periods without much use. */
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
if (time_to_recheck_bandwidth && me &&
me->bandwidthcapacity < me->bandwidthrate &&
me->bandwidthcapacity < 51200) {
@@ -1251,13 +1471,44 @@ run_scheduled_events(time_t now)
}
}
- /** 10b. write bridge networkstatus file to disk */
+ /** 10. write bridge networkstatus file to disk */
if (options->BridgeAuthoritativeDir &&
time_to_write_bridge_status_file < now) {
networkstatus_dump_bridge_status_to_file(now);
#define BRIDGE_STATUSFILE_INTERVAL (30*60)
time_to_write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
}
+
+ /** 11. check the port forwarding app */
+ if (time_to_check_port_forwarding < now &&
+ options->PortForwarding &&
+ is_server) {
+#define PORT_FORWARDING_CHECK_INTERVAL 5
+ tor_check_port_forwarding(options->PortForwardingHelper,
+ options->DirPort,
+ options->ORPort,
+ now);
+ time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
+ }
+
+ /** 11b. check pending unconfigured managed proxies */
+ if (pt_proxies_configuration_pending())
+ pt_configure_remaining_proxies();
+
+ /** 11c. validate pluggable transports configuration if we need to */
+ if (!has_validated_pt &&
+ (options->Bridges || options->ClientTransportPlugin)) {
+ if (validate_pluggable_transports_config() == 0) {
+ has_validated_pt = 1;
+ }
+ }
+
+ /** 12. write the heartbeat message */
+ if (options->HeartbeatPeriod &&
+ time_to_next_heartbeat < now) {
+ log_heartbeat(now);
+ time_to_next_heartbeat = now+options->HeartbeatPeriod;
+ }
}
/** Timer: used to invoke second_elapsed_callback() once per second. */
@@ -1277,7 +1528,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
size_t bytes_written;
size_t bytes_read;
int seconds_elapsed;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
(void)timer;
(void)arg;
@@ -1288,21 +1539,30 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
update_approx_time(now);
/* the second has rolled over. check more stuff. */
- bytes_written = stats_prev_global_write_bucket - global_write_bucket;
- bytes_read = stats_prev_global_read_bucket - global_read_bucket;
seconds_elapsed = current_second ? (int)(now - current_second) : 0;
- stats_n_bytes_read += bytes_read;
- stats_n_bytes_written += bytes_written;
- if (accounting_is_enabled(options) && seconds_elapsed >= 0)
- accounting_add_bytes(bytes_read, bytes_written, seconds_elapsed);
+#ifdef USE_BUFFEREVENTS
+ {
+ uint64_t cur_read,cur_written;
+ connection_get_rate_limit_totals(&cur_read, &cur_written);
+ bytes_written = (size_t)(cur_written - stats_prev_n_written);
+ bytes_read = (size_t)(cur_read - stats_prev_n_read);
+ stats_n_bytes_read += bytes_read;
+ stats_n_bytes_written += bytes_written;
+ if (accounting_is_enabled(options) && seconds_elapsed >= 0)
+ accounting_add_bytes(bytes_read, bytes_written, seconds_elapsed);
+ stats_prev_n_written = cur_written;
+ stats_prev_n_read = cur_read;
+ }
+#else
+ bytes_read = (size_t)(stats_n_bytes_read - stats_prev_n_read);
+ bytes_written = (size_t)(stats_n_bytes_written - stats_prev_n_written);
+ stats_prev_n_read = stats_n_bytes_read;
+ stats_prev_n_written = stats_n_bytes_written;
+#endif
+
control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
control_event_stream_bandwidth_used();
- if (seconds_elapsed > 0)
- connection_bucket_refill(seconds_elapsed, now);
- stats_prev_global_read_bucket = global_read_bucket;
- stats_prev_global_write_bucket = global_write_bucket;
-
if (server_mode(options) &&
!we_are_hibernating() &&
seconds_elapsed > 0 &&
@@ -1311,7 +1571,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
(stats_n_seconds_working+seconds_elapsed) /
TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) {
/* every 20 minutes, check and complain if necessary */
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
if (me && !check_whether_orport_reachable()) {
log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that "
"its ORPort is reachable. Please check your firewalls, ports, "
@@ -1350,6 +1610,57 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
current_second = now; /* remember which second it is, for next time */
}
+#ifndef USE_BUFFEREVENTS
+/** Timer: used to invoke refill_callback(). */
+static periodic_timer_t *refill_timer = NULL;
+
+/** Libevent callback: invoked periodically to refill token buckets
+ * and count r/w bytes. It is only used when bufferevents are disabled. */
+static void
+refill_callback(periodic_timer_t *timer, void *arg)
+{
+ static struct timeval current_millisecond;
+ struct timeval now;
+
+ size_t bytes_written;
+ size_t bytes_read;
+ int milliseconds_elapsed = 0;
+ int seconds_rolled_over = 0;
+
+ const or_options_t *options = get_options();
+
+ (void)timer;
+ (void)arg;
+
+ tor_gettimeofday(&now);
+
+ /* If this is our first time, no time has passed. */
+ if (current_millisecond.tv_sec) {
+ long mdiff = tv_mdiff(&current_millisecond, &now);
+ if (mdiff > INT_MAX)
+ mdiff = INT_MAX;
+ milliseconds_elapsed = (int)mdiff;
+ seconds_rolled_over = (int)(now.tv_sec - current_millisecond.tv_sec);
+ }
+
+ bytes_written = stats_prev_global_write_bucket - global_write_bucket;
+ bytes_read = stats_prev_global_read_bucket - global_read_bucket;
+
+ stats_n_bytes_read += bytes_read;
+ stats_n_bytes_written += bytes_written;
+ if (accounting_is_enabled(options) && milliseconds_elapsed >= 0)
+ accounting_add_bytes(bytes_read, bytes_written, seconds_rolled_over);
+
+ if (milliseconds_elapsed > 0)
+ connection_bucket_refill(milliseconds_elapsed, now.tv_sec);
+
+ stats_prev_global_read_bucket = global_read_bucket;
+ stats_prev_global_write_bucket = global_write_bucket;
+
+ current_millisecond = now; /* remember what time it is, for next time */
+}
+#endif
+
#ifndef MS_WINDOWS
/** Called when a possibly ignorable libevent error occurs; ensures that we
* don't get into an infinite loop by ignoring too many errors from
@@ -1410,7 +1721,7 @@ dns_servers_relaunch_checks(void)
static int
do_hup(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
#ifdef USE_DMALLOC
dmalloc_log_stats();
@@ -1502,8 +1813,10 @@ do_main_loop(void)
/* Set up our buckets */
connection_bucket_init();
+#ifndef USE_BUFFEREVENTS
stats_prev_global_read_bucket = global_read_bucket;
stats_prev_global_write_bucket = global_write_bucket;
+#endif
/* initialize the bootstrap status events to know we're starting up */
control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
@@ -1546,6 +1859,22 @@ do_main_loop(void)
tor_assert(second_timer);
}
+#ifndef USE_BUFFEREVENTS
+ if (!refill_timer) {
+ struct timeval refill_interval;
+ int msecs = get_options()->TokenBucketRefillInterval;
+
+ refill_interval.tv_sec = msecs/1000;
+ refill_interval.tv_usec = (msecs%1000)*1000;
+
+ refill_timer = periodic_timer_new(tor_libevent_get_base(),
+ &refill_interval,
+ refill_callback,
+ NULL);
+ tor_assert(refill_timer);
+ }
+#endif
+
for (;;) {
if (nt_service_is_stopping())
return 0;
@@ -1634,11 +1963,13 @@ process_signal(uintptr_t sig)
case SIGUSR1:
/* prefer to log it at INFO, but make sure we always see it */
dumpstats(get_min_log_level()<LOG_INFO ? get_min_log_level() : LOG_INFO);
+ control_event_signal(sig);
break;
case SIGUSR2:
switch_logs_debug();
log_debug(LD_GENERAL,"Caught USR2, going to loglevel debug. "
"Send HUP to change back.");
+ control_event_signal(sig);
break;
case SIGHUP:
if (do_hup() < 0) {
@@ -1646,6 +1977,7 @@ process_signal(uintptr_t sig)
tor_cleanup();
exit(1);
}
+ control_event_signal(sig);
break;
#ifdef SIGCHLD
case SIGCHLD:
@@ -1667,10 +1999,18 @@ process_signal(uintptr_t sig)
}
case SIGCLEARDNSCACHE:
addressmap_clear_transient();
+ control_event_signal(sig);
break;
}
}
+/** Returns Tor's uptime. */
+long
+get_uptime(void)
+{
+ return stats_n_seconds_working;
+}
+
extern uint64_t rephist_total_alloc;
extern uint32_t rephist_total_num;
@@ -1717,13 +2057,13 @@ dumpstats(int severity)
log(severity,LD_GENERAL,
"Conn %d: %d bytes waiting on inbuf (len %d, last read %d secs ago)",
i,
- (int)buf_datalen(conn->inbuf),
+ (int)connection_get_inbuf_len(conn),
(int)buf_allocation(conn->inbuf),
(int)(now - conn->timestamp_lastread));
log(severity,LD_GENERAL,
"Conn %d: %d bytes waiting on outbuf "
"(len %d, last written %d secs ago)",i,
- (int)buf_datalen(conn->outbuf),
+ (int)connection_get_outbuf_len(conn),
(int)buf_allocation(conn->outbuf),
(int)(now - conn->timestamp_lastwritten));
if (conn->type == CONN_TYPE_OR) {
@@ -1895,9 +2235,15 @@ tor_init(int argc, char *argv[])
default:
add_temp_log(LOG_NOTICE);
}
+ quiet_level = quiet;
- log(LOG_NOTICE, LD_GENERAL, "Tor v%s. This is experimental software. "
- "Do not rely on it for strong anonymity. (Running on %s)",get_version(),
+ log(LOG_NOTICE, LD_GENERAL, "Tor v%s%s. This is experimental software. "
+ "Do not rely on it for strong anonymity. (Running on %s)", get_version(),
+#ifdef USE_BUFFEREVENTS
+ " (with bufferevents)",
+#else
+ "",
+#endif
get_uname());
if (network_init()<0) {
@@ -1938,7 +2284,7 @@ static tor_lockfile_t *lockfile = NULL;
* return -1 if we can't get the lockfile. Return 0 on success.
*/
int
-try_locking(or_options_t *options, int err_if_locked)
+try_locking(const or_options_t *options, int err_if_locked)
{
if (lockfile)
return 0;
@@ -2017,9 +2363,11 @@ tor_free_all(int postfork)
clear_pending_onions();
circuit_free_all();
entry_guards_free_all();
+ pt_free_all();
connection_free_all();
buf_shrink_freelists(1);
memarea_clear_freelist();
+ nodelist_free_all();
microdesc_free_all();
if (!postfork) {
config_free_all();
@@ -2051,7 +2399,7 @@ tor_free_all(int postfork)
void
tor_cleanup(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (options->command == CMD_RUN_TOR) {
time_t now = time(NULL);
/* Remove our pid file. We don't care if there was an error when we
diff --git a/src/or/main.h b/src/or/main.h
index 0551f7aaf..c8903642d 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -14,7 +14,9 @@
extern int can_complete_circuit;
-int connection_add(connection_t *conn);
+int connection_add_impl(connection_t *conn, int is_connecting);
+#define connection_add(conn) connection_add_impl((conn), 0)
+#define connection_add_connecting(conn) connection_add_impl((conn), 1)
int connection_remove(connection_t *conn);
void connection_unregister_events(connection_t *conn);
int connection_in_array(connection_t *conn);
@@ -22,10 +24,13 @@ void add_connection_to_closeable_list(connection_t *conn);
int connection_is_on_closeable_list(connection_t *conn);
smartlist_t *get_connection_array(void);
+uint64_t get_bytes_read(void);
+uint64_t get_bytes_written(void);
/** Bitmask for events that we can turn on and off with
* connection_watch_events. */
typedef enum watchable_events {
+ /* Yes, it is intentional that these match Libevent's EV_READ and EV_WRITE */
READ_EVENT=0x02, /**< We want to know when a connection is readable */
WRITE_EVENT=0x04 /**< We want to know when a connection is writable */
} watchable_events_t;
@@ -46,10 +51,13 @@ void directory_info_has_arrived(time_t now, int from_cache);
void ip_address_changed(int at_interface);
void dns_servers_relaunch_checks(void);
+long get_uptime(void);
+unsigned get_signewnym_epoch(void);
+
void handle_signals(int is_parent);
void process_signal(uintptr_t sig);
-int try_locking(or_options_t *options, int err_if_locked);
+int try_locking(const or_options_t *options, int err_if_locked);
int have_lockfile(void);
void release_lockfile(void);
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 7c67d5144..92f5c0358 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -3,7 +3,14 @@
#include "or.h"
#include "config.h"
+#include "directory.h"
+#include "dirserv.h"
#include "microdesc.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "policies.h"
+#include "router.h"
+#include "routerlist.h"
#include "routerparse.h"
/** A data structure to hold a bunch of cached microdescriptors. There are
@@ -121,15 +128,19 @@ get_microdesc_cache(void)
* ending at <b>eos</b>, and store them in <b>cache</b>. If <b>no-save</b>,
* mark them as non-writable to disk. If <b>where</b> is SAVED_IN_CACHE,
* leave their bodies as pointers to the mmap'd cache. If where is
- * <b>SAVED_NOWHERE</b>, do not allow annotations. Return a list of the added
- * microdescriptors. */
+ * <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is positive,
+ * set the last_listed field of every microdesc to listed_at. If
+ * requested_digests is non-null, then it contains a list of digests we mean
+ * to allow, so we should reject any non-requested microdesc with a different
+ * digest, and alter the list to contain only the digests of those microdescs
+ * we didn't find.
+ * Return a newly allocated list of the added microdescriptors, or NULL */
smartlist_t *
microdescs_add_to_cache(microdesc_cache_t *cache,
const char *s, const char *eos, saved_location_t where,
- int no_save)
+ int no_save, time_t listed_at,
+ smartlist_t *requested_digests256)
{
- /*XXXX need an argument that sets last_listed as appropriate. */
-
smartlist_t *descriptors, *added;
const int allow_annotations = (where != SAVED_NOWHERE);
const int copy_body = (where != SAVED_IN_CACHE);
@@ -137,6 +148,33 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
descriptors = microdescs_parse_from_string(s, eos,
allow_annotations,
copy_body);
+ if (listed_at > 0) {
+ SMARTLIST_FOREACH(descriptors, microdesc_t *, md,
+ md->last_listed = listed_at);
+ }
+ if (requested_digests256) {
+ digestmap_t *requested; /* XXXX actuqlly we should just use a
+ digest256map */
+ requested = digestmap_new();
+ SMARTLIST_FOREACH(requested_digests256, const char *, cp,
+ digestmap_set(requested, cp, (void*)1));
+ SMARTLIST_FOREACH_BEGIN(descriptors, microdesc_t *, md) {
+ if (digestmap_get(requested, md->digest)) {
+ digestmap_set(requested, md->digest, (void*)2);
+ } else {
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Received non-requested microcdesc");
+ microdesc_free(md);
+ SMARTLIST_DEL_CURRENT(descriptors, md);
+ }
+ } SMARTLIST_FOREACH_END(md);
+ SMARTLIST_FOREACH_BEGIN(requested_digests256, char *, cp) {
+ if (digestmap_get(requested, cp) == (void*)2) {
+ tor_free(cp);
+ SMARTLIST_DEL_CURRENT(requested_digests256, cp);
+ }
+ } SMARTLIST_FOREACH_END(cp);
+ digestmap_free(requested, NULL);
+ }
added = microdescs_add_list_to_cache(cache, descriptors, where, no_save);
smartlist_free(descriptors);
@@ -144,7 +182,7 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
}
/* As microdescs_add_to_cache, but takes a list of micrdescriptors instead of
- * a string to encode. Frees any members of <b>descriptors</b> that it does
+ * a string to decode. Frees any members of <b>descriptors</b> that it does
* not add. */
smartlist_t *
microdescs_add_list_to_cache(microdesc_cache_t *cache,
@@ -200,6 +238,7 @@ microdescs_add_list_to_cache(microdesc_cache_t *cache,
md->no_save = no_save;
HT_INSERT(microdesc_map, &cache->map, md);
+ md->held_in_map = 1;
smartlist_add(added, md);
++cache->n_seen;
cache->total_len_seen += md->bodylen;
@@ -208,6 +247,15 @@ microdescs_add_list_to_cache(microdesc_cache_t *cache,
if (f)
finish_writing_to_file(open_file); /*XXX Check me.*/
+ {
+ networkstatus_t *ns = networkstatus_get_latest_consensus();
+ if (ns && ns->flavor == FLAV_MICRODESC)
+ SMARTLIST_FOREACH(added, microdesc_t *, md, nodelist_add_microdesc(md));
+ }
+
+ if (smartlist_len(added))
+ router_dir_info_changed();
+
return added;
}
@@ -219,6 +267,7 @@ microdesc_cache_clear(microdesc_cache_t *cache)
for (entry = HT_START(microdesc_map, &cache->map); entry; entry = next) {
microdesc_t *md = *entry;
next = HT_NEXT_RMV(microdesc_map, &cache->map, entry);
+ md->held_in_map = 0;
microdesc_free(md);
}
HT_CLEAR(microdesc_map, &cache->map);
@@ -247,7 +296,7 @@ microdesc_cache_reload(microdesc_cache_t *cache)
mm = cache->cache_content = tor_mmap_file(cache->cache_fname);
if (mm) {
added = microdescs_add_to_cache(cache, mm->data, mm->data+mm->size,
- SAVED_IN_CACHE, 0);
+ SAVED_IN_CACHE, 0, -1, NULL);
if (added) {
total += smartlist_len(added);
smartlist_free(added);
@@ -260,7 +309,7 @@ microdesc_cache_reload(microdesc_cache_t *cache)
cache->journal_len = (size_t) st.st_size;
added = microdescs_add_to_cache(cache, journal_content,
journal_content+st.st_size,
- SAVED_IN_JOURNAL, 0);
+ SAVED_IN_JOURNAL, 0, -1, NULL);
if (added) {
total += smartlist_len(added);
smartlist_free(added);
@@ -293,9 +342,11 @@ microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force)
size_t bytes_dropped = 0;
time_t now = time(NULL);
- (void) force;
- /* In 0.2.2, we let this proceed unconditionally: only authorities have
- * microdesc caches. */
+ /* If we don't know a live consensus, don't believe last_listed values: we
+ * might be starting up after being down for a while. */
+ if (! force &&
+ ! networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC))
+ return;
if (cutoff <= 0)
cutoff = now - TOLERATE_MICRODESC_AGE;
@@ -305,6 +356,7 @@ microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force)
++dropped;
victim = *mdp;
mdp = HT_NEXT_RMV(microdesc_map, &cache->map, mdp);
+ victim->held_in_map = 0;
bytes_dropped += victim->bodylen;
microdesc_free(victim);
} else {
@@ -390,6 +442,7 @@ microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
/* log? return -1? die? coredump the universe? */
continue;
}
+ tor_assert(((size_t)size) == annotation_len + md->bodylen);
md->off = off + annotation_len;
off += size;
if (md->saved_location != SAVED_IN_CACHE) {
@@ -415,7 +468,21 @@ microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
SMARTLIST_FOREACH_BEGIN(wrote, microdesc_t *, md) {
tor_assert(md->saved_location == SAVED_IN_CACHE);
md->body = (char*)cache->cache_content->data + md->off;
- tor_assert(fast_memeq(md->body, "onion-key", 9));
+ if (PREDICT_UNLIKELY(
+ md->bodylen < 9 || fast_memneq(md->body, "onion-key", 9) != 0)) {
+ /* XXXX023 once bug 2022 is solved, we can kill this block and turn it
+ * into just the tor_assert(!memcmp) */
+ off_t avail = cache->cache_content->size - md->off;
+ char *bad_str;
+ tor_assert(avail >= 0);
+ bad_str = tor_strndup(md->body, MIN(128, (size_t)avail));
+ log_err(LD_BUG, "After rebuilding microdesc cache, offsets seem wrong. "
+ " At offset %d, I expected to find a microdescriptor starting "
+ " with \"onion-key\". Instead I got %s.",
+ (int)md->off, escaped(bad_str));
+ tor_free(bad_str);
+ tor_assert(fast_memeq(md->body, "onion-key", 9));
+ }
} SMARTLIST_FOREACH_END(md);
smartlist_free(wrote);
@@ -432,6 +499,28 @@ microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
return 0;
}
+/** Make sure that the reference count of every microdescriptor in cache is
+ * accurate. */
+void
+microdesc_check_counts(void)
+{
+ microdesc_t **mdp;
+ if (!the_microdesc_cache)
+ return;
+
+ HT_FOREACH(mdp, microdesc_map, &the_microdesc_cache->map) {
+ microdesc_t *md = *mdp;
+ unsigned int found=0;
+ const smartlist_t *nodes = nodelist_get_list();
+ SMARTLIST_FOREACH(nodes, node_t *, node, {
+ if (node->md == md) {
+ ++found;
+ }
+ });
+ tor_assert(found == md->held_by_nodes);
+ }
+}
+
/** Deallocate a single microdescriptor. Note: the microdescriptor MUST have
* previously been removed from the cache if it had ever been inserted. */
void
@@ -439,7 +528,43 @@ microdesc_free(microdesc_t *md)
{
if (!md)
return;
- /* Must be removed from hash table! */
+
+ /* Make sure that the microdesc was really removed from the appropriate data
+ structures. */
+ if (md->held_in_map) {
+ microdesc_cache_t *cache = get_microdesc_cache();
+ microdesc_t *md2 = HT_FIND(microdesc_map, &cache->map, md);
+ if (md2 == md) {
+ log_warn(LD_BUG, "microdesc_free() called, but md was still in "
+ "microdesc_map");
+ HT_REMOVE(microdesc_map, &cache->map, md);
+ } else {
+ log_warn(LD_BUG, "microdesc_free() called with held_in_map set, but "
+ "microdesc was not in the map.");
+ }
+ tor_fragile_assert();
+ }
+ if (md->held_by_nodes) {
+ int found=0;
+ const smartlist_t *nodes = nodelist_get_list();
+ SMARTLIST_FOREACH(nodes, node_t *, node, {
+ if (node->md == md) {
+ ++found;
+ node->md = NULL;
+ }
+ });
+ if (found) {
+ log_warn(LD_BUG, "microdesc_free() called, but md was still referenced "
+ "%d node(s); held_by_nodes == %u", found, md->held_by_nodes);
+ } else {
+ log_warn(LD_BUG, "microdesc_free() called with held_by_nodes set to %u, "
+ "but md was not referenced by any nodes", md->held_by_nodes);
+ }
+ tor_fragile_assert();
+ }
+ //tor_assert(md->held_in_map == 0);
+ //tor_assert(md->held_by_nodes == 0);
+
if (md->onion_pkey)
crypto_free_pk_env(md->onion_pkey);
if (md->body && md->saved_location != SAVED_IN_CACHE)
@@ -449,7 +574,7 @@ microdesc_free(microdesc_t *md)
SMARTLIST_FOREACH(md->family, char *, cp, tor_free(cp));
smartlist_free(md->family);
}
- tor_free(md->exitsummary);
+ short_policy_free(md->exit_policy);
tor_free(md);
}
@@ -491,3 +616,147 @@ microdesc_average_size(microdesc_cache_t *cache)
return (size_t)(cache->total_len_seen / cache->n_seen);
}
+/** Return a smartlist of all the sha256 digest of the microdescriptors that
+ * are listed in <b>ns</b> but not present in <b>cache</b>. Returns pointers
+ * to internals of <b>ns</b>; you should not free the members of the resulting
+ * smartlist. Omit all microdescriptors whose digest appear in <b>skip</b>. */
+smartlist_t *
+microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
+ int downloadable_only, digestmap_t *skip)
+{
+ smartlist_t *result = smartlist_create();
+ time_t now = time(NULL);
+ tor_assert(ns->flavor == FLAV_MICRODESC);
+ SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
+ if (microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest))
+ continue;
+ if (downloadable_only &&
+ !download_status_is_ready(&rs->dl_status, now,
+ MAX_MICRODESC_DOWNLOAD_FAILURES))
+ continue;
+ if (skip && digestmap_get(skip, rs->descriptor_digest))
+ continue;
+ if (tor_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN))
+ continue; /* This indicates a bug somewhere XXXX023*/
+ /* XXXX Also skip if we're a noncache and wouldn't use this router.
+ * XXXX NM Microdesc
+ */
+ smartlist_add(result, rs->descriptor_digest);
+ } SMARTLIST_FOREACH_END(rs);
+ return result;
+}
+
+/** Launch download requests for mircodescriptors as appropriate.
+ *
+ * Specifically, we should launch download requests if we are configured to
+ * download mirodescriptors, and there are some microdescriptors listed the
+ * current microdesc consensus that we don't have, and either we never asked
+ * for them, or we failed to download them but we're willing to retry.
+ */
+void
+update_microdesc_downloads(time_t now)
+{
+ const or_options_t *options = get_options();
+ networkstatus_t *consensus;
+ smartlist_t *missing;
+ digestmap_t *pending;
+
+ if (should_delay_dir_fetches(options))
+ return;
+ if (directory_too_idle_to_fetch_descriptors(options, now))
+ return;
+
+ consensus = networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC);
+ if (!consensus)
+ return;
+
+ if (!we_fetch_microdescriptors(options))
+ return;
+
+ pending = digestmap_new();
+ list_pending_microdesc_downloads(pending);
+
+ missing = microdesc_list_missing_digest256(consensus,
+ get_microdesc_cache(),
+ 1,
+ pending);
+ digestmap_free(pending, NULL);
+
+ launch_descriptor_downloads(DIR_PURPOSE_FETCH_MICRODESC,
+ missing, NULL, now);
+
+ smartlist_free(missing);
+}
+
+/** For every microdescriptor listed in the current microdecriptor consensus,
+ * update its last_listed field to be at least as recent as the publication
+ * time of the current microdescriptor consensus.
+ */
+void
+update_microdescs_from_networkstatus(time_t now)
+{
+ microdesc_cache_t *cache = get_microdesc_cache();
+ microdesc_t *md;
+ networkstatus_t *ns =
+ networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC);
+
+ if (! ns)
+ return;
+
+ tor_assert(ns->flavor == FLAV_MICRODESC);
+
+ SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
+ md = microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest);
+ if (md && ns->valid_after > md->last_listed)
+ md->last_listed = ns->valid_after;
+ } SMARTLIST_FOREACH_END(rs);
+}
+
+/** Return true iff we should prefer to use microdescriptors rather than
+ * routerdescs for building circuits. */
+int
+we_use_microdescriptors_for_circuits(const or_options_t *options)
+{
+ int ret = options->UseMicrodescriptors;
+ if (ret == -1) {
+ /* UseMicrodescriptors is "auto"; we need to decide: */
+ /* So we decide that we'll use microdescriptors iff we are not a server,
+ * and we're not autofetching everything. */
+ ret = !server_mode(options) && !options->FetchUselessDescriptors;
+ }
+ return ret;
+}
+
+/** Return true iff we should try to download microdescriptors at all. */
+int
+we_fetch_microdescriptors(const or_options_t *options)
+{
+ if (directory_caches_dir_info(options))
+ return 1;
+ if (options->FetchUselessDescriptors)
+ return 1;
+ return we_use_microdescriptors_for_circuits(options);
+}
+
+/** Return true iff we should try to download router descriptors at all. */
+int
+we_fetch_router_descriptors(const or_options_t *options)
+{
+ if (directory_caches_dir_info(options))
+ return 1;
+ if (options->FetchUselessDescriptors)
+ return 1;
+ return ! we_use_microdescriptors_for_circuits(options);
+}
+
+/** Return the consensus flavor we actually want to use to build circuits. */
+int
+usable_consensus_flavor(void)
+{
+ if (we_use_microdescriptors_for_circuits(get_options())) {
+ return FLAV_MICRODESC;
+ } else {
+ return FLAV_NS;
+ }
+}
+
diff --git a/src/or/microdesc.h b/src/or/microdesc.h
index 77ce8536b..456413281 100644
--- a/src/or/microdesc.h
+++ b/src/or/microdesc.h
@@ -14,9 +14,12 @@
microdesc_cache_t *get_microdesc_cache(void);
+void microdesc_check_counts(void);
+
smartlist_t *microdescs_add_to_cache(microdesc_cache_t *cache,
const char *s, const char *eos, saved_location_t where,
- int no_save);
+ int no_save, time_t listed_at,
+ smartlist_t *requested_digests256);
smartlist_t *microdescs_add_list_to_cache(microdesc_cache_t *cache,
smartlist_t *descriptors, saved_location_t where,
int no_save);
@@ -31,8 +34,21 @@ microdesc_t *microdesc_cache_lookup_by_digest256(microdesc_cache_t *cache,
size_t microdesc_average_size(microdesc_cache_t *cache);
+smartlist_t *microdesc_list_missing_digest256(networkstatus_t *ns,
+ microdesc_cache_t *cache,
+ int downloadable_only,
+ digestmap_t *skip);
+
void microdesc_free(microdesc_t *md);
void microdesc_free_all(void);
+void update_microdesc_downloads(time_t now);
+void update_microdescs_from_networkstatus(time_t now);
+
+int usable_consensus_flavor(void);
+int we_fetch_microdescriptors(const or_options_t *options);
+int we_fetch_router_descriptors(const or_options_t *options);
+int we_use_microdescriptors_for_circuits(const or_options_t *options);
+
#endif
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index b0ef74b02..7cd9d02c3 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -20,7 +20,9 @@
#include "dirserv.h"
#include "dirvote.h"
#include "main.h"
+#include "microdesc.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "relay.h"
#include "router.h"
#include "routerlist.h"
@@ -44,8 +46,21 @@ static strmap_t *named_server_map = NULL;
* as unnamed for some server in the consensus. */
static strmap_t *unnamed_server_map = NULL;
-/** Most recently received and validated v3 consensus network status. */
-static networkstatus_t *current_consensus = NULL;
+/** Most recently received and validated v3 consensus network status,
+ * of whichever type we are using for our own circuits. This will be the same
+ * as one of current_ns_consensus or current_md_consensus.
+ */
+#define current_consensus \
+ (we_use_microdescriptors_for_circuits(get_options()) ? \
+ current_md_consensus : current_ns_consensus)
+
+/** Most recently received and validated v3 "ns"-flavored consensus network
+ * status. */
+static networkstatus_t *current_ns_consensus = NULL;
+
+/** Most recently received and validated v3 "microdec"-flavored consensus
+ * network status. */
+static networkstatus_t *current_md_consensus = NULL;
/** A v3 consensus networkstatus that we've received, but which we don't
* have enough certificates to be happy about. */
@@ -94,9 +109,8 @@ void
networkstatus_reset_warnings(void)
{
if (current_consensus) {
- SMARTLIST_FOREACH(current_consensus->routerstatus_list,
- routerstatus_t *, rs,
- rs->name_lookup_warned = 0);
+ SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
+ node->name_lookup_warned = 0);
}
have_warned_about_old_version = 0;
@@ -199,7 +213,7 @@ router_reload_consensus_networkstatus(void)
char *filename;
char *s;
struct stat st;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
const unsigned int flags = NSSET_FROM_CACHE | NSSET_DONT_DOWNLOAD_CERTS;
int flav;
@@ -271,6 +285,7 @@ router_reload_consensus_networkstatus(void)
update_certificate_downloads(time(NULL));
routers_update_all_from_networkstatus(time(NULL), 3);
+ update_microdescs_from_networkstatus(time(NULL));
return 0;
}
@@ -469,7 +484,7 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus,
int n_bad = 0;
int n_unknown = 0;
int n_no_signature = 0;
- int n_v3_authorities = get_n_authorities(V3_AUTHORITY);
+ int n_v3_authorities = get_n_authorities(V3_DIRINFO);
int n_required = n_v3_authorities/2 + 1;
smartlist_t *need_certs_from = smartlist_create();
smartlist_t *unrecognized = smartlist_create();
@@ -540,7 +555,7 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus,
SMARTLIST_FOREACH(router_get_trusted_dir_servers(),
trusted_dir_server_t *, ds,
{
- if ((ds->type & V3_AUTHORITY) &&
+ if ((ds->type & V3_DIRINFO) &&
!networkstatus_get_voter_by_id(consensus, ds->v3_identity_digest))
smartlist_add(missing_authorities, ds);
});
@@ -723,7 +738,7 @@ router_set_networkstatus_v2(const char *s, time_t arrived_at,
base16_encode(fp, HEX_DIGEST_LEN+1, ns->identity_digest, DIGEST_LEN);
if (!(trusted_dir =
router_get_trusteddirserver_by_digest(ns->identity_digest)) ||
- !(trusted_dir->type & V2_AUTHORITY)) {
+ !(trusted_dir->type & V2_DIRINFO)) {
log_info(LD_DIR, "Network status was signed, but not by an authoritative "
"directory we recognize.");
source_desc = fp;
@@ -927,10 +942,9 @@ compare_digest_to_routerstatus_entry(const void *_key, const void **_member)
return tor_memcmp(key, rs->identity_digest, DIGEST_LEN);
}
-/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
- * NULL if none was found. */
+/** As networkstatus_v2_find_entry, but do not return a const pointer */
routerstatus_t *
-networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
+networkstatus_v2_find_mutable_entry(networkstatus_v2_t *ns, const char *digest)
{
return smartlist_bsearch(ns->entries, digest,
compare_digest_to_routerstatus_entry);
@@ -938,14 +952,29 @@ networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
* NULL if none was found. */
+const routerstatus_t *
+networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
+{
+ return networkstatus_v2_find_mutable_entry(ns, digest);
+}
+
+/** As networkstatus_find_entry, but do not return a const pointer */
routerstatus_t *
-networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest)
+networkstatus_vote_find_mutable_entry(networkstatus_t *ns, const char *digest)
{
return smartlist_bsearch(ns->routerstatus_list, digest,
compare_digest_to_routerstatus_entry);
}
-/*XXXX make this static once functions are moved into this file. */
+/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
+ * NULL if none was found. */
+const routerstatus_t *
+networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest)
+{
+ return networkstatus_vote_find_mutable_entry(ns, digest);
+}
+
+/*XXXX MOVE make this static once functions are moved into this file. */
/** Search the routerstatuses in <b>ns</b> for one whose identity digest is
* <b>digest</b>. Return value and set *<b>found_out</b> as for
* smartlist_bsearch_idx(). */
@@ -967,22 +996,37 @@ networkstatus_get_v2_list(void)
return networkstatus_v2_list;
}
-/** Return the consensus view of the status of the router whose current
- * <i>descriptor</i> digest is <b>digest</b>, or NULL if no such router is
- * known. */
+/* As router_get_consensus_status_by_descriptor_digest, but does not return
+ * a const pointer */
routerstatus_t *
-router_get_consensus_status_by_descriptor_digest(const char *digest)
+router_get_mutable_consensus_status_by_descriptor_digest(
+ networkstatus_t *consensus,
+ const char *digest)
{
- if (!current_consensus) return NULL;
- if (!current_consensus->desc_digest_map) {
- digestmap_t * m = current_consensus->desc_digest_map = digestmap_new();
- SMARTLIST_FOREACH(current_consensus->routerstatus_list,
+ if (!consensus)
+ consensus = current_consensus;
+ if (!consensus)
+ return NULL;
+ if (!consensus->desc_digest_map) {
+ digestmap_t *m = consensus->desc_digest_map = digestmap_new();
+ SMARTLIST_FOREACH(consensus->routerstatus_list,
routerstatus_t *, rs,
{
digestmap_set(m, rs->descriptor_digest, rs);
});
}
- return digestmap_get(current_consensus->desc_digest_map, digest);
+ return digestmap_get(consensus->desc_digest_map, digest);
+}
+
+/** Return the consensus view of the status of the router whose current
+ * <i>descriptor</i> digest in <b>consensus</b> is <b>digest</b>, or NULL if
+ * no such router is known. */
+const routerstatus_t *
+router_get_consensus_status_by_descriptor_digest(networkstatus_t *consensus,
+ const char *digest)
+{
+ return router_get_mutable_consensus_status_by_descriptor_digest(
+ consensus, digest);
}
/** Given the digest of a router descriptor, return its current download
@@ -991,7 +1035,10 @@ download_status_t *
router_get_dl_status_by_descriptor_digest(const char *d)
{
routerstatus_t *rs;
- if ((rs = router_get_consensus_status_by_descriptor_digest(d)))
+ if (!current_ns_consensus)
+ return NULL;
+ if ((rs = router_get_mutable_consensus_status_by_descriptor_digest(
+ current_ns_consensus, d)))
return &rs->dl_status;
if (v2_download_status_map)
return digestmap_get(v2_download_status_map, d);
@@ -999,10 +1046,9 @@ router_get_dl_status_by_descriptor_digest(const char *d)
return NULL;
}
-/** Return the consensus view of the status of the router whose identity
- * digest is <b>digest</b>, or NULL if we don't know about any such router. */
+/** As router_get_consensus_status_by_id, but do not return a const pointer */
routerstatus_t *
-router_get_consensus_status_by_id(const char *digest)
+router_get_mutable_consensus_status_by_id(const char *digest)
{
if (!current_consensus)
return NULL;
@@ -1010,100 +1056,27 @@ router_get_consensus_status_by_id(const char *digest)
compare_digest_to_routerstatus_entry);
}
+/** Return the consensus view of the status of the router whose identity
+ * digest is <b>digest</b>, or NULL if we don't know about any such router. */
+const routerstatus_t *
+router_get_consensus_status_by_id(const char *digest)
+{
+ return router_get_mutable_consensus_status_by_id(digest);
+}
+
/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
* the corresponding routerstatus_t, or NULL if none exists. Warn the
* user if <b>warn_if_unnamed</b> is set, and they have specified a router by
* nickname, but the Named flag isn't set for that router. */
-routerstatus_t *
+const routerstatus_t *
router_get_consensus_status_by_nickname(const char *nickname,
int warn_if_unnamed)
{
- char digest[DIGEST_LEN];
- routerstatus_t *best=NULL;
- smartlist_t *matches=NULL;
- const char *named_id=NULL;
-
- if (!current_consensus || !nickname)
- return NULL;
-
- /* Is this name really a hexadecimal identity digest? */
- if (nickname[0] == '$') {
- if (base16_decode(digest, DIGEST_LEN, nickname+1, strlen(nickname+1))<0)
- return NULL;
- return networkstatus_vote_find_entry(current_consensus, digest);
- } else if (strlen(nickname) == HEX_DIGEST_LEN &&
- (base16_decode(digest, DIGEST_LEN, nickname, strlen(nickname))==0)) {
- return networkstatus_vote_find_entry(current_consensus, digest);
- }
-
- /* Is there a server that is Named with this name? */
- if (named_server_map)
- named_id = strmap_get_lc(named_server_map, nickname);
- if (named_id)
- return networkstatus_vote_find_entry(current_consensus, named_id);
-
- /* Okay; is this name listed as Unnamed? */
- if (unnamed_server_map &&
- strmap_get_lc(unnamed_server_map, nickname)) {
- log_info(LD_GENERAL, "The name %s is listed as Unnamed; it is not the "
- "canonical name of any server we know.", escaped(nickname));
+ const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
+ if (node)
+ return node->rs;
+ else
return NULL;
- }
-
- /* This name is not canonical for any server; go through the list and
- * see who it matches. */
- /*XXXX This is inefficient; optimize it if it matters. */
- matches = smartlist_create();
- SMARTLIST_FOREACH(current_consensus->routerstatus_list,
- routerstatus_t *, lrs,
- {
- if (!strcasecmp(lrs->nickname, nickname)) {
- if (lrs->is_named) {
- tor_fragile_assert() /* This should never happen. */
- smartlist_free(matches);
- return lrs;
- } else {
- if (lrs->is_unnamed) {
- tor_fragile_assert(); /* nor should this. */
- smartlist_clear(matches);
- best=NULL;
- break;
- }
- smartlist_add(matches, lrs);
- best = lrs;
- }
- }
- });
-
- if (smartlist_len(matches)>1 && warn_if_unnamed) {
- int any_unwarned=0;
- SMARTLIST_FOREACH(matches, routerstatus_t *, lrs,
- {
- if (! lrs->name_lookup_warned) {
- lrs->name_lookup_warned=1;
- any_unwarned=1;
- }
- });
- if (any_unwarned) {
- log_warn(LD_CONFIG,"There are multiple matches for the nickname \"%s\","
- " but none is listed as named by the directory authorities. "
- "Choosing one arbitrarily.", nickname);
- }
- } else if (warn_if_unnamed && best && !best->name_lookup_warned) {
- char fp[HEX_DIGEST_LEN+1];
- base16_encode(fp, sizeof(fp),
- best->identity_digest, DIGEST_LEN);
- log_warn(LD_CONFIG,
- "When looking up a status, you specified a server \"%s\" by name, "
- "but the directory authorities do not have any key registered for "
- "this nickname -- so it could be used by any server, "
- "not just the one you meant. "
- "To make sure you get the same server in the future, refer to "
- "it by key, as \"$%s\".", nickname, fp);
- best->name_lookup_warned = 1;
- }
- smartlist_free(matches);
- return best;
}
/** Return the identity digest that's mapped to officially by
@@ -1159,7 +1132,7 @@ update_v2_networkstatus_cache_downloads(time_t now)
{
char resource[HEX_DIGEST_LEN+6]; /* fp/hexdigit.z\0 */
tor_addr_t addr;
- if (!(ds->type & V2_AUTHORITY))
+ if (!(ds->type & V2_DIRINFO))
continue;
if (router_digest_is_me(ds->digest))
continue;
@@ -1200,6 +1173,29 @@ update_v2_networkstatus_cache_downloads(time_t now)
}
}
+/** DOCDOC */
+static int
+we_want_to_fetch_flavor(const or_options_t *options, int flavor)
+{
+ if (flavor < 0 || flavor > N_CONSENSUS_FLAVORS) {
+ /* This flavor is crazy; we don't want it */
+ /*XXXX handle unrecognized flavors later */
+ return 0;
+ }
+ if (authdir_mode_v3(options) || directory_caches_dir_info(options)) {
+ /* We want to serve all flavors to others, regardless if we would use
+ * it ourselves. */
+ return 1;
+ }
+ if (options->FetchUselessDescriptors) {
+ /* In order to get all descriptors, we need to fetch all consensuses. */
+ return 1;
+ }
+ /* Otherwise, we want the flavor only if we want to use it to build
+ * circuits. */
+ return flavor == usable_consensus_flavor();
+}
+
/** How many times will we try to fetch a consensus before we give up? */
#define CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES 8
/** How long will we hang onto a possibly live consensus for which we're
@@ -1212,48 +1208,65 @@ static void
update_consensus_networkstatus_downloads(time_t now)
{
int i;
+ const or_options_t *options = get_options();
+
if (!networkstatus_get_live_consensus(now))
time_to_download_next_consensus = now; /* No live consensus? Get one now!*/
if (time_to_download_next_consensus > now)
return; /* Wait until the current consensus is older. */
- /* XXXXNM Microdescs: may need to download more types. */
- if (!download_status_is_ready(&consensus_dl_status[FLAV_NS], now,
- CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
- return; /* We failed downloading a consensus too recently. */
- if (connection_get_by_type_purpose(CONN_TYPE_DIR,
- DIR_PURPOSE_FETCH_CONSENSUS))
- return; /* There's an in-progress download.*/
for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
- consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i];
+ /* XXXX need some way to download unknown flavors if we are caching. */
+ const char *resource;
+ consensus_waiting_for_certs_t *waiting;
+
+ if (! we_want_to_fetch_flavor(options, i))
+ continue;
+
+ resource = networkstatus_get_flavor_name(i);
+
+ if (!download_status_is_ready(&consensus_dl_status[i], now,
+ CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
+ continue; /* We failed downloading a consensus too recently. */
+ if (connection_dir_get_by_purpose_and_resource(
+ DIR_PURPOSE_FETCH_CONSENSUS, resource))
+ continue; /* There's an in-progress download.*/
+
+ waiting = &consensus_waiting_for_certs[i];
if (waiting->consensus) {
/* XXXX make sure this doesn't delay sane downloads. */
- if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now)
- return; /* We're still getting certs for this one. */
- else {
+ if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
+ continue; /* We're still getting certs for this one. */
+ } else {
if (!waiting->dl_failed) {
- download_status_failed(&consensus_dl_status[FLAV_NS], 0);
+ download_status_failed(&consensus_dl_status[i], 0);
waiting->dl_failed=1;
}
}
}
- }
- log_info(LD_DIR, "Launching networkstatus consensus download.");
- directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
- ROUTER_PURPOSE_GENERAL, NULL,
- PDS_RETRY_IF_NO_SERVERS);
+ log_info(LD_DIR, "Launching %s networkstatus consensus download.",
+ networkstatus_get_flavor_name(i));
+
+ directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
+ ROUTER_PURPOSE_GENERAL, resource,
+ PDS_RETRY_IF_NO_SERVERS);
+ }
}
/** Called when an attempt to download a consensus fails: note that the
* failure occurred, and possibly retry. */
void
-networkstatus_consensus_download_failed(int status_code)
+networkstatus_consensus_download_failed(int status_code, const char *flavname)
{
- /* XXXXNM Microdescs: may need to handle more types. */
- download_status_failed(&consensus_dl_status[FLAV_NS], status_code);
- /* Retry immediately, if appropriate. */
- update_consensus_networkstatus_downloads(time(NULL));
+ int flav = networkstatus_parse_flavor_name(flavname);
+ if (flav >= 0) {
+ tor_assert(flav < N_CONSENSUS_FLAVORS);
+ /* XXXX handle unrecognized flavors */
+ download_status_failed(&consensus_dl_status[flav], status_code);
+ /* Retry immediately, if appropriate. */
+ update_consensus_networkstatus_downloads(time(NULL));
+ }
}
/** How long do we (as a cache) wait after a consensus becomes non-fresh
@@ -1265,7 +1278,7 @@ networkstatus_consensus_download_failed(int status_code)
void
update_consensus_networkstatus_fetch_time(time_t now)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
networkstatus_t *c = networkstatus_get_live_consensus(now);
if (c) {
long dl_interval;
@@ -1339,7 +1352,7 @@ update_consensus_networkstatus_fetch_time(time_t now)
* fetches yet (e.g. we demand bridges and none are yet known).
* Else return 0. */
int
-should_delay_dir_fetches(or_options_t *options)
+should_delay_dir_fetches(const or_options_t *options)
{
if (options->UseBridges && !any_bridge_descriptors_known()) {
log_info(LD_DIR, "delaying dir fetches (no running bridges known)");
@@ -1353,7 +1366,7 @@ should_delay_dir_fetches(or_options_t *options)
void
update_networkstatus_downloads(time_t now)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (should_delay_dir_fetches(options))
return;
if (authdir_mode_any_main(options) || options->FetchV2Networkstatus)
@@ -1374,7 +1387,10 @@ update_certificate_downloads(time_t now)
now);
}
- authority_certs_fetch_missing(current_consensus, now);
+ if (current_ns_consensus)
+ authority_certs_fetch_missing(current_ns_consensus, now);
+ if (current_md_consensus)
+ authority_certs_fetch_missing(current_md_consensus, now);
}
/** Return 1 if we have a consensus but we don't have enough certificates
@@ -1382,7 +1398,7 @@ update_certificate_downloads(time_t now)
int
consensus_is_waiting_for_certs(void)
{
- return consensus_waiting_for_certs[USABLE_CONSENSUS_FLAVOR].consensus
+ return consensus_waiting_for_certs[usable_consensus_flavor()].consensus
? 1 : 0;
}
@@ -1406,6 +1422,18 @@ networkstatus_get_latest_consensus(void)
return current_consensus;
}
+/** DOCDOC */
+networkstatus_t *
+networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
+{
+ if (f == FLAV_NS)
+ return current_ns_consensus;
+ else if (f == FLAV_MICRODESC)
+ return current_md_consensus;
+ else
+ tor_assert(0);
+}
+
/** Return the most recent consensus that we have downloaded, or NULL if it is
* no longer live. */
networkstatus_t *
@@ -1425,13 +1453,15 @@ networkstatus_get_live_consensus(time_t now)
/** As networkstatus_get_live_consensus(), but is way more tolerant of expired
* consensuses. */
networkstatus_t *
-networkstatus_get_reasonably_live_consensus(time_t now)
+networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
{
#define REASONABLY_LIVE_TIME (24*60*60)
- if (current_consensus &&
- current_consensus->valid_after <= now &&
- now <= current_consensus->valid_until+REASONABLY_LIVE_TIME)
- return current_consensus;
+ networkstatus_t *consensus =
+ networkstatus_get_latest_consensus_by_flavor(flavor);
+ if (consensus &&
+ consensus->valid_after <= now &&
+ now <= consensus->valid_until+REASONABLY_LIVE_TIME)
+ return consensus;
else
return NULL;
}
@@ -1452,7 +1482,7 @@ routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b)
a->is_exit != b->is_exit ||
a->is_stable != b->is_stable ||
a->is_fast != b->is_fast ||
- a->is_running != b->is_running ||
+ a->is_flagged_running != b->is_flagged_running ||
a->is_named != b->is_named ||
a->is_unnamed != b->is_unnamed ||
a->is_valid != b->is_valid ||
@@ -1493,13 +1523,14 @@ notify_control_networkstatus_changed(const networkstatus_t *old_c,
}
changed = smartlist_create();
- SMARTLIST_FOREACH_JOIN(old_c->routerstatus_list, routerstatus_t *, rs_old,
- new_c->routerstatus_list, routerstatus_t *, rs_new,
- tor_memcmp(rs_old->identity_digest,
- rs_new->identity_digest, DIGEST_LEN),
- smartlist_add(changed, rs_new)) {
+ SMARTLIST_FOREACH_JOIN(
+ old_c->routerstatus_list, const routerstatus_t *, rs_old,
+ new_c->routerstatus_list, const routerstatus_t *, rs_new,
+ tor_memcmp(rs_old->identity_digest,
+ rs_new->identity_digest, DIGEST_LEN),
+ smartlist_add(changed, (void*) rs_new)) {
if (routerstatus_has_changed(rs_old, rs_new))
- smartlist_add(changed, rs_new);
+ smartlist_add(changed, (void*)rs_new);
} SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new);
control_event_networkstatus_changed(changed);
@@ -1523,7 +1554,6 @@ networkstatus_copy_old_consensus_info(networkstatus_t *new_c,
rs_new->identity_digest, DIGEST_LEN),
STMT_NIL) {
/* Okay, so we're looking at the same identity. */
- rs_new->name_lookup_warned = rs_old->name_lookup_warned;
rs_new->last_dir_503_at = rs_old->last_dir_503_at;
if (tor_memeq(rs_old->descriptor_digest, rs_new->descriptor_digest,
@@ -1559,7 +1589,7 @@ networkstatus_set_current_consensus(const char *consensus,
networkstatus_t *c=NULL;
int r, result = -1;
time_t now = time(NULL);
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
char *unverified_fname = NULL, *consensus_fname = NULL;
int flav = networkstatus_parse_flavor_name(flavor);
const unsigned from_cache = flags & NSSET_FROM_CACHE;
@@ -1597,7 +1627,7 @@ networkstatus_set_current_consensus(const char *consensus,
flavor = networkstatus_get_flavor_name(flav);
}
- if (flav != USABLE_CONSENSUS_FLAVOR &&
+ if (flav != usable_consensus_flavor() &&
!directory_caches_dir_info(options)) {
/* This consensus is totally boring to us: we won't use it, and we won't
* serve it. Drop it. */
@@ -1616,9 +1646,16 @@ networkstatus_set_current_consensus(const char *consensus,
if (!strcmp(flavor, "ns")) {
consensus_fname = get_datadir_fname("cached-consensus");
unverified_fname = get_datadir_fname("unverified-consensus");
- if (current_consensus) {
- current_digests = &current_consensus->digests;
- current_valid_after = current_consensus->valid_after;
+ if (current_ns_consensus) {
+ current_digests = &current_ns_consensus->digests;
+ current_valid_after = current_ns_consensus->valid_after;
+ }
+ } else if (!strcmp(flavor, "microdesc")) {
+ consensus_fname = get_datadir_fname("cached-microdesc-consensus");
+ unverified_fname = get_datadir_fname("unverified-microdesc-consensus");
+ if (current_md_consensus) {
+ current_digests = &current_md_consensus->digests;
+ current_valid_after = current_md_consensus->valid_after;
}
} else {
cached_dir_t *cur;
@@ -1695,24 +1732,36 @@ networkstatus_set_current_consensus(const char *consensus,
}
}
- if (!from_cache && flav == USABLE_CONSENSUS_FLAVOR)
+ if (!from_cache && flav == usable_consensus_flavor())
control_event_client_status(LOG_NOTICE, "CONSENSUS_ARRIVED");
/* Are we missing any certificates at all? */
if (r != 1 && dl_certs)
authority_certs_fetch_missing(c, now);
- if (flav == USABLE_CONSENSUS_FLAVOR) {
+ if (flav == usable_consensus_flavor()) {
notify_control_networkstatus_changed(current_consensus, c);
-
- if (current_consensus) {
- networkstatus_copy_old_consensus_info(c, current_consensus);
- networkstatus_vote_free(current_consensus);
+ }
+ if (flav == FLAV_NS) {
+ if (current_ns_consensus) {
+ networkstatus_copy_old_consensus_info(c, current_ns_consensus);
+ networkstatus_vote_free(current_ns_consensus);
/* Defensive programming : we should set current_consensus very soon,
* but we're about to call some stuff in the meantime, and leaving this
* dangling pointer around has proven to be trouble. */
- current_consensus = NULL;
+ current_ns_consensus = NULL;
+ }
+ current_ns_consensus = c;
+ free_consensus = 0; /* avoid free */
+ } else if (flav == FLAV_MICRODESC) {
+ if (current_md_consensus) {
+ networkstatus_copy_old_consensus_info(c, current_md_consensus);
+ networkstatus_vote_free(current_md_consensus);
+ /* more defensive programming */
+ current_md_consensus = NULL;
}
+ current_md_consensus = c;
+ free_consensus = 0; /* avoid free */
}
waiting = &consensus_waiting_for_certs[flav];
@@ -1737,12 +1786,12 @@ networkstatus_set_current_consensus(const char *consensus,
download_status_failed(&consensus_dl_status[flav], 0);
}
- if (flav == USABLE_CONSENSUS_FLAVOR) {
- current_consensus = c;
- free_consensus = 0; /* Prevent free. */
-
- /* XXXXNM Microdescs: needs a non-ns variant. */
+ if (flav == usable_consensus_flavor()) {
+ /* XXXXNM Microdescs: needs a non-ns variant. ???? NM*/
update_consensus_networkstatus_fetch_time(now);
+
+ nodelist_set_consensus(current_consensus);
+
dirvote_recalculate_timing(options, now);
routerstatus_list_update_named_server_map();
cell_ewma_set_scale_factor(options, current_consensus);
@@ -1769,11 +1818,11 @@ networkstatus_set_current_consensus(const char *consensus,
* valid-after time, declare that our clock is skewed. */
#define EARLY_CONSENSUS_NOTICE_SKEW 60
- if (now < current_consensus->valid_after - EARLY_CONSENSUS_NOTICE_SKEW) {
+ if (now < c->valid_after - EARLY_CONSENSUS_NOTICE_SKEW) {
char tbuf[ISO_TIME_LEN+1];
char dbuf[64];
- long delta = now - current_consensus->valid_after;
- format_iso_time(tbuf, current_consensus->valid_after);
+ long delta = now - c->valid_after;
+ format_iso_time(tbuf, c->valid_after);
format_time_interval(dbuf, sizeof(dbuf), delta);
log_warn(LD_GENERAL, "Our clock is %s behind the time published in the "
"consensus network status document (%s GMT). Tor needs an "
@@ -1824,7 +1873,8 @@ void
routers_update_all_from_networkstatus(time_t now, int dir_version)
{
routerlist_t *rl = router_get_routerlist();
- networkstatus_t *consensus = networkstatus_get_live_consensus(now);
+ networkstatus_t *consensus = networkstatus_get_reasonably_live_consensus(now,
+ FLAV_NS);
if (networkstatus_v2_list_has_changed)
download_status_map_update_from_v2_networkstatus();
@@ -1896,7 +1946,7 @@ download_status_map_update_from_v2_networkstatus(void)
dl_status = digestmap_new();
SMARTLIST_FOREACH_BEGIN(networkstatus_v2_list, networkstatus_v2_t *, ns) {
- SMARTLIST_FOREACH_BEGIN(ns->entries, routerstatus_t *, rs) {
+ SMARTLIST_FOREACH_BEGIN(ns->entries, const routerstatus_t *, rs) {
const char *d = rs->descriptor_digest;
download_status_t *s;
if (digestmap_get(dl_status, d))
@@ -1924,7 +1974,8 @@ routerstatus_list_update_named_server_map(void)
named_server_map = strmap_new();
strmap_free(unnamed_server_map, NULL);
unnamed_server_map = strmap_new();
- SMARTLIST_FOREACH(current_consensus->routerstatus_list, routerstatus_t *, rs,
+ SMARTLIST_FOREACH(current_consensus->routerstatus_list,
+ const routerstatus_t *, rs,
{
if (rs->is_named) {
strmap_set_lc(named_server_map, rs->nickname,
@@ -1944,9 +1995,8 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
int reset_failures)
{
trusted_dir_server_t *ds;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
- int namingdir = authdir && options->NamingAuthoritativeDir;
networkstatus_t *ns = current_consensus;
if (!ns || !smartlist_len(ns->routerstatus_list))
return;
@@ -1960,25 +2010,12 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
tor_memcmp(rs->identity_digest,
router->cache_info.identity_digest, DIGEST_LEN),
{
- /* We have no routerstatus for this router. Clear flags and skip it. */
- if (!namingdir)
- router->is_named = 0;
- if (!authdir) {
- if (router->purpose == ROUTER_PURPOSE_GENERAL)
- router_clear_status_flags(router);
- }
}) {
/* We have a routerstatus for this router. */
const char *digest = router->cache_info.identity_digest;
ds = router_get_trusteddirserver_by_digest(digest);
- if (!namingdir) {
- if (rs->is_named && !strcasecmp(router->nickname, rs->nickname))
- router->is_named = 1;
- else
- router->is_named = 0;
- }
/* Is it the same descriptor, or only the same identity? */
if (tor_memeq(router->cache_info.signed_descriptor_digest,
rs->descriptor_digest, DIGEST_LEN)) {
@@ -1986,28 +2023,17 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
router->cache_info.last_listed_as_valid_until = ns->valid_until;
}
- if (!authdir) {
- /* If we're not an authdir, believe others. */
- router->is_valid = rs->is_valid;
- router->is_running = rs->is_running;
- router->is_fast = rs->is_fast;
- router->is_stable = rs->is_stable;
- router->is_possible_guard = rs->is_possible_guard;
- router->is_exit = rs->is_exit;
- router->is_bad_directory = rs->is_bad_directory;
- router->is_bad_exit = rs->is_bad_exit;
- router->is_hs_dir = rs->is_hs_dir;
- } else {
+ if (authdir) {
/* If we _are_ an authority, we should check whether this router
* is one that will cause us to need a reachability test. */
routerinfo_t *old_router =
- router_get_by_digest(router->cache_info.identity_digest);
+ router_get_mutable_by_digest(router->cache_info.identity_digest);
if (old_router != router) {
router->needs_retest_if_added =
dirserv_should_launch_reachability_test(router, old_router);
}
}
- if (router->is_running && ds) {
+ if (rs->is_flagged_running && ds) {
download_status_reset(&ds->v2_ns_dl_status);
}
if (reset_failures) {
@@ -2016,10 +2042,9 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
} SMARTLIST_FOREACH_JOIN_END(rs, router);
/* Now update last_listed_as_valid_until from v2 networkstatuses. */
- /* XXXX If this is slow, we need to rethink the code. */
SMARTLIST_FOREACH(networkstatus_v2_list, networkstatus_v2_t *, ns, {
time_t live_until = ns->published_on + V2_NETWORKSTATUS_ROUTER_LIFETIME;
- SMARTLIST_FOREACH_JOIN(ns->entries, routerstatus_t *, rs,
+ SMARTLIST_FOREACH_JOIN(ns->entries, const routerstatus_t *, rs,
routers, routerinfo_t *, ri,
tor_memcmp(rs->identity_digest,
ri->cache_info.identity_digest, DIGEST_LEN),
@@ -2040,7 +2065,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
void
signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
{
- networkstatus_t *ns = current_consensus;
+ networkstatus_t *ns = current_ns_consensus;
if (!ns)
return;
@@ -2048,11 +2073,11 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
char dummy[DIGEST_LEN];
/* instantiates the digest map. */
memset(dummy, 0, sizeof(dummy));
- router_get_consensus_status_by_descriptor_digest(dummy);
+ router_get_consensus_status_by_descriptor_digest(ns, dummy);
}
SMARTLIST_FOREACH(descs, signed_descriptor_t *, d,
{
- routerstatus_t *rs = digestmap_get(ns->desc_digest_map,
+ const routerstatus_t *rs = digestmap_get(ns->desc_digest_map,
d->signed_descriptor_digest);
if (rs) {
if (ns->valid_until > d->last_listed_as_valid_until)
@@ -2065,7 +2090,7 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
* return the result in a newly allocated string. Used only by controller
* interface (for now.) */
char *
-networkstatus_getinfo_helper_single(routerstatus_t *rs)
+networkstatus_getinfo_helper_single(const routerstatus_t *rs)
{
char buf[RS_ENTRY_LEN+1];
routerstatus_format_entry(buf, sizeof(buf), rs, NULL, NS_CONTROL_PORT);
@@ -2098,6 +2123,9 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
statuses = smartlist_create();
SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, {
+ node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+ if (!node)
+ continue;
if (ri->cache_info.published_on < cutoff)
continue;
if (ri->purpose != purpose)
@@ -2105,7 +2133,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now)
if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE)
dirserv_set_router_is_running(ri, now);
/* then generate and write out status lines for each of them */
- set_routerstatus_from_routerinfo(&rs, ri, now, 0, 0, 0, 0);
+ set_routerstatus_from_routerinfo(&rs, node, ri, now, 0, 0, 0, 0);
smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs));
});
@@ -2120,7 +2148,7 @@ void
networkstatus_dump_bridge_status_to_file(time_t now)
{
char *status = networkstatus_getinfo_by_purpose("bridge", now);
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
size_t len = strlen(options->DataDirectory) + 32;
char *fname = tor_malloc(len);
tor_snprintf(fname, len, "%s"PATH_SEPARATOR"networkstatus-bridges",
@@ -2174,7 +2202,7 @@ get_net_param_from_list(smartlist_t *net_params, const char *param_name,
* <b>min_val</b> and at most <b>max_val</b> and raise/cap the parsed value
* if necessary. */
int32_t
-networkstatus_get_param(networkstatus_t *ns, const char *param_name,
+networkstatus_get_param(const networkstatus_t *ns, const char *param_name,
int32_t default_val, int32_t min_val, int32_t max_val)
{
if (!ns) /* if they pass in null, go find it ourselves */
@@ -2253,7 +2281,7 @@ getinfo_helper_networkstatus(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg)
{
- routerstatus_t *status;
+ const routerstatus_t *status;
(void) conn;
if (!current_consensus) {
@@ -2264,7 +2292,7 @@ getinfo_helper_networkstatus(control_connection_t *conn,
if (!strcmp(question, "ns/all")) {
smartlist_t *statuses = smartlist_create();
SMARTLIST_FOREACH(current_consensus->routerstatus_list,
- routerstatus_t *, rs,
+ const routerstatus_t *, rs,
{
smartlist_add(statuses, networkstatus_getinfo_helper_single(rs));
});
@@ -2308,8 +2336,9 @@ networkstatus_free_all(void)
digestmap_free(v2_download_status_map, _tor_free);
v2_download_status_map = NULL;
- networkstatus_vote_free(current_consensus);
- current_consensus = NULL;
+ networkstatus_vote_free(current_ns_consensus);
+ networkstatus_vote_free(current_md_consensus);
+ current_md_consensus = current_ns_consensus = NULL;
for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i];
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index ec2e8f884..1b10f2738 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -38,31 +38,46 @@ int router_set_networkstatus_v2(const char *s, time_t arrived_at,
void networkstatus_v2_list_clean(time_t now);
int compare_digest_to_routerstatus_entry(const void *_key,
const void **_member);
-routerstatus_t *networkstatus_v2_find_entry(networkstatus_v2_t *ns,
+const routerstatus_t *networkstatus_v2_find_entry(networkstatus_v2_t *ns,
const char *digest);
-routerstatus_t *networkstatus_vote_find_entry(networkstatus_t *ns,
+const routerstatus_t *networkstatus_vote_find_entry(networkstatus_t *ns,
+ const char *digest);
+routerstatus_t *networkstatus_v2_find_mutable_entry(networkstatus_v2_t *ns,
+ const char *digest);
+routerstatus_t *networkstatus_vote_find_mutable_entry(networkstatus_t *ns,
const char *digest);
int networkstatus_vote_find_entry_idx(networkstatus_t *ns,
const char *digest, int *found_out);
const smartlist_t *networkstatus_get_v2_list(void);
download_status_t *router_get_dl_status_by_descriptor_digest(const char *d);
-routerstatus_t *router_get_consensus_status_by_id(const char *digest);
-routerstatus_t *router_get_consensus_status_by_descriptor_digest(
- const char *digest);
-routerstatus_t *router_get_consensus_status_by_nickname(const char *nickname,
- int warn_if_unnamed);
+const routerstatus_t *router_get_consensus_status_by_id(const char *digest);
+routerstatus_t *router_get_mutable_consensus_status_by_id(
+ const char *digest);
+const routerstatus_t *router_get_consensus_status_by_descriptor_digest(
+ networkstatus_t *consensus,
+ const char *digest);
+routerstatus_t *router_get_mutable_consensus_status_by_descriptor_digest(
+ networkstatus_t *consensus,
+ const char *digest);
+const routerstatus_t *router_get_consensus_status_by_nickname(
+ const char *nickname,
+ int warn_if_unnamed);
const char *networkstatus_get_router_digest_by_nickname(const char *nickname);
int networkstatus_nickname_is_unnamed(const char *nickname);
-void networkstatus_consensus_download_failed(int status_code);
+void networkstatus_consensus_download_failed(int status_code,
+ const char *flavname);
void update_consensus_networkstatus_fetch_time(time_t now);
-int should_delay_dir_fetches(or_options_t *options);
+int should_delay_dir_fetches(const or_options_t *options);
void update_networkstatus_downloads(time_t now);
void update_certificate_downloads(time_t now);
int consensus_is_waiting_for_certs(void);
networkstatus_v2_t *networkstatus_v2_get_by_digest(const char *digest);
networkstatus_t *networkstatus_get_latest_consensus(void);
+networkstatus_t *networkstatus_get_latest_consensus_by_flavor(
+ consensus_flavor_t f);
networkstatus_t *networkstatus_get_live_consensus(time_t now);
-networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now);
+networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
+ int flavor);
#define NSSET_FROM_CACHE 1
#define NSSET_WAS_WAITING_FOR_CERTS 2
#define NSSET_DONT_DOWNLOAD_CERTS 4
@@ -78,10 +93,11 @@ void routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
void signed_descs_update_status_from_consensus_networkstatus(
smartlist_t *descs);
-char *networkstatus_getinfo_helper_single(routerstatus_t *rs);
+char *networkstatus_getinfo_helper_single(const routerstatus_t *rs);
char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now);
void networkstatus_dump_bridge_status_to_file(time_t now);
-int32_t networkstatus_get_param(networkstatus_t *ns, const char *param_name,
+int32_t networkstatus_get_param(const networkstatus_t *ns,
+ const char *param_name,
int32_t default_val, int32_t min_val,
int32_t max_val);
int getinfo_helper_networkstatus(control_connection_t *conn,
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
new file mode 100644
index 000000000..b93b919c1
--- /dev/null
+++ b/src/or/nodelist.c
@@ -0,0 +1,758 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "config.h"
+#include "dirserv.h"
+#include "microdesc.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "policies.h"
+#include "router.h"
+#include "routerlist.h"
+
+#include <string.h>
+
+static void nodelist_drop_node(node_t *node, int remove_from_ht);
+static void node_free(node_t *node);
+
+/** A nodelist_t holds a node_t object for every router we're "willing to use
+ * for something". Specifically, it should hold a node_t for every node that
+ * is currently in the routerlist, or currently in the consensus we're using.
+ */
+typedef struct nodelist_t {
+ /* A list of all the nodes. */
+ smartlist_t *nodes;
+ /* Hash table to map from node ID digest to node. */
+ HT_HEAD(nodelist_map, node_t) nodes_by_id;
+
+} nodelist_t;
+
+static INLINE unsigned int
+node_id_hash(const node_t *node)
+{
+#if SIZEOF_INT == 4
+ const uint32_t *p = (const uint32_t*)node->identity;
+ return p[0] ^ p[1] ^ p[2] ^ p[3] ^ p[4];
+#elif SIZEOF_INT == 8
+ const uint64_t *p = (const uint32_t*)node->identity;
+ const uint32_t *p32 = (const uint32_t*)node->identity;
+ return p[0] ^ p[1] ^ p32[4];
+#endif
+}
+
+static INLINE unsigned int
+node_id_eq(const node_t *node1, const node_t *node2)
+{
+ return tor_memeq(node1->identity, node2->identity, DIGEST_LEN);
+}
+
+HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq);
+HT_GENERATE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq,
+ 0.6, malloc, realloc, free);
+
+/** The global nodelist. */
+static nodelist_t *the_nodelist=NULL;
+
+/** Create an empty nodelist if we haven't done so already. */
+static void
+init_nodelist(void)
+{
+ if (PREDICT_UNLIKELY(the_nodelist == NULL)) {
+ the_nodelist = tor_malloc_zero(sizeof(nodelist_t));
+ HT_INIT(nodelist_map, &the_nodelist->nodes_by_id);
+ the_nodelist->nodes = smartlist_create();
+ }
+}
+
+/** As node_get_by_id, but returns a non-const pointer */
+node_t *
+node_get_mutable_by_id(const char *identity_digest)
+{
+ node_t search, *node;
+ if (PREDICT_UNLIKELY(the_nodelist == NULL))
+ return NULL;
+
+ memcpy(&search.identity, identity_digest, DIGEST_LEN);
+ node = HT_FIND(nodelist_map, &the_nodelist->nodes_by_id, &search);
+ return node;
+}
+
+/** Return the node_t whose identity is <b>identity_digest</b>, or NULL
+ * if no such node exists. */
+const node_t *
+node_get_by_id(const char *identity_digest)
+{
+ return node_get_mutable_by_id(identity_digest);
+}
+
+/** Internal: return the node_t whose identity_digest is
+ * <b>identity_digest</b>. If none exists, create a new one, add it to the
+ * nodelist, and return it.
+ *
+ * Requires that the nodelist be initialized.
+ */
+static node_t *
+node_get_or_create(const char *identity_digest)
+{
+ node_t *node;
+
+ if ((node = node_get_mutable_by_id(identity_digest)))
+ return node;
+
+ node = tor_malloc_zero(sizeof(node_t));
+ memcpy(node->identity, identity_digest, DIGEST_LEN);
+ HT_INSERT(nodelist_map, &the_nodelist->nodes_by_id, node);
+
+ smartlist_add(the_nodelist->nodes, node);
+ node->nodelist_idx = smartlist_len(the_nodelist->nodes) - 1;
+
+ node->country = -1;
+
+ return node;
+}
+
+/** Add <b>ri</b> to the nodelist. */
+node_t *
+nodelist_add_routerinfo(routerinfo_t *ri)
+{
+ node_t *node;
+ init_nodelist();
+ node = node_get_or_create(ri->cache_info.identity_digest);
+ node->ri = ri;
+
+ if (node->country == -1)
+ node_set_country(node);
+
+ if (authdir_mode(get_options())) {
+ const char *discard=NULL;
+ uint32_t status = dirserv_router_get_status(ri, &discard);
+ dirserv_set_node_flags_from_authoritative_status(node, status);
+ }
+
+ return node;
+}
+
+/** Set the appropriate node_t to use <b>md</b> as its microdescriptor.
+ *
+ * Called when a new microdesc has arrived and the usable consensus flavor
+ * is "microdesc".
+ **/
+node_t *
+nodelist_add_microdesc(microdesc_t *md)
+{
+ networkstatus_t *ns =
+ networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC);
+ const routerstatus_t *rs;
+ node_t *node;
+ if (ns == NULL)
+ return NULL;
+ init_nodelist();
+
+ /* Microdescriptors don't carry an identity digest, so we need to figure
+ * it out by looking up the routerstatus. */
+ rs = router_get_consensus_status_by_descriptor_digest(ns, md->digest);
+ if (rs == NULL)
+ return NULL;
+ node = node_get_mutable_by_id(rs->identity_digest);
+ if (node) {
+ if (node->md)
+ node->md->held_by_nodes--;
+ node->md = md;
+ md->held_by_nodes++;
+ }
+ return node;
+}
+
+/** Tell the nodelist that the current usable consensus to <b>ns</b>.
+ * This makes the nodelist change all of the routerstatus entries for
+ * the nodes, drop nodes that no longer have enough info to get used,
+ * and grab microdescriptors into nodes as appropriate.
+ */
+void
+nodelist_set_consensus(networkstatus_t *ns)
+{
+ const or_options_t *options = get_options();
+ int authdir = authdir_mode_v2(options) || authdir_mode_v3(options);
+
+ init_nodelist();
+ if (ns->flavor == FLAV_MICRODESC)
+ (void) get_microdesc_cache(); /* Make sure it exists first. */
+
+ SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node,
+ node->rs = NULL);
+
+ SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
+ node_t *node = node_get_or_create(rs->identity_digest);
+ node->rs = rs;
+ if (ns->flavor == FLAV_MICRODESC) {
+ if (node->md == NULL ||
+ tor_memneq(node->md->digest,rs->descriptor_digest,DIGEST256_LEN)) {
+ if (node->md)
+ node->md->held_by_nodes--;
+ node->md = microdesc_cache_lookup_by_digest256(NULL,
+ rs->descriptor_digest);
+ if (node->md)
+ node->md->held_by_nodes++;
+ }
+ }
+
+ node_set_country(node);
+
+ /* If we're not an authdir, believe others. */
+ if (!authdir) {
+ node->is_valid = rs->is_valid;
+ node->is_running = rs->is_flagged_running;
+ node->is_fast = rs->is_fast;
+ node->is_stable = rs->is_stable;
+ node->is_possible_guard = rs->is_possible_guard;
+ node->is_exit = rs->is_exit;
+ node->is_bad_directory = rs->is_bad_directory;
+ node->is_bad_exit = rs->is_bad_exit;
+ node->is_hs_dir = rs->is_hs_dir;
+ }
+
+ } SMARTLIST_FOREACH_END(rs);
+
+ nodelist_purge();
+
+ if (! authdir) {
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ /* We have no routerstatus for this router. Clear flags so we can skip
+ * it, maybe.*/
+ if (!node->rs) {
+ tor_assert(node->ri); /* if it had only an md, or nothing, purge
+ * would have removed it. */
+ if (node->ri->purpose == ROUTER_PURPOSE_GENERAL) {
+ /* Clear all flags. */
+ node->is_valid = node->is_running = node->is_hs_dir =
+ node->is_fast = node->is_stable =
+ node->is_possible_guard = node->is_exit =
+ node->is_bad_exit = node->is_bad_directory = 0;
+ }
+ }
+ } SMARTLIST_FOREACH_END(node);
+ }
+}
+
+/** Helper: return true iff a node has a usable amount of information*/
+static INLINE int
+node_is_usable(const node_t *node)
+{
+ return (node->rs) || (node->ri);
+}
+
+/** Tell the nodelist that <b>md</b> is no longer a microdescriptor for the
+ * node with <b>identity_digest</b>. */
+void
+nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md)
+{
+ node_t *node = node_get_mutable_by_id(identity_digest);
+ if (node && node->md == md) {
+ node->md = NULL;
+ md->held_by_nodes--;
+ }
+}
+
+/** Tell the nodelist that <b>ri</b> is no longer in the routerlist. */
+void
+nodelist_remove_routerinfo(routerinfo_t *ri)
+{
+ node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+ if (node && node->ri == ri) {
+ node->ri = NULL;
+ if (! node_is_usable(node)) {
+ nodelist_drop_node(node, 1);
+ node_free(node);
+ }
+ }
+}
+
+/** Remove <b>node</b> from the nodelist. (Asserts that it was there to begin
+ * with.) */
+static void
+nodelist_drop_node(node_t *node, int remove_from_ht)
+{
+ node_t *tmp;
+ int idx;
+ if (remove_from_ht) {
+ tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node);
+ tor_assert(tmp == node);
+ }
+
+ idx = node->nodelist_idx;
+ tor_assert(idx >= 0);
+
+ tor_assert(node == smartlist_get(the_nodelist->nodes, idx));
+ smartlist_del(the_nodelist->nodes, idx);
+ if (idx < smartlist_len(the_nodelist->nodes)) {
+ tmp = smartlist_get(the_nodelist->nodes, idx);
+ tmp->nodelist_idx = idx;
+ }
+ node->nodelist_idx = -1;
+}
+
+/** Release storage held by <b>node</b> */
+static void
+node_free(node_t *node)
+{
+ if (!node)
+ return;
+ if (node->md)
+ node->md->held_by_nodes--;
+ tor_assert(node->nodelist_idx == -1);
+ tor_free(node);
+}
+
+/** Remove all entries from the nodelist that don't have enough info to be
+ * usable for anything. */
+void
+nodelist_purge(void)
+{
+ node_t **iter;
+ if (PREDICT_UNLIKELY(the_nodelist == NULL))
+ return;
+
+ /* Remove the non-usable nodes. */
+ for (iter = HT_START(nodelist_map, &the_nodelist->nodes_by_id); iter; ) {
+ node_t *node = *iter;
+
+ if (node->md && !node->rs) {
+ /* An md is only useful if there is an rs. */
+ node->md->held_by_nodes--;
+ node->md = NULL;
+ }
+
+ if (node_is_usable(node)) {
+ iter = HT_NEXT(nodelist_map, &the_nodelist->nodes_by_id, iter);
+ } else {
+ iter = HT_NEXT_RMV(nodelist_map, &the_nodelist->nodes_by_id, iter);
+ nodelist_drop_node(node, 0);
+ node_free(node);
+ }
+ }
+ nodelist_assert_ok();
+}
+
+/** Release all storage held by the nodelist. */
+void
+nodelist_free_all(void)
+{
+ if (PREDICT_UNLIKELY(the_nodelist == NULL))
+ return;
+
+ HT_CLEAR(nodelist_map, &the_nodelist->nodes_by_id);
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ node->nodelist_idx = -1;
+ node_free(node);
+ } SMARTLIST_FOREACH_END(node);
+
+ smartlist_free(the_nodelist->nodes);
+
+ tor_free(the_nodelist);
+}
+
+/** Check that the nodelist is internally consistent, and consistent with
+ * the directory info it's derived from.
+ */
+void
+nodelist_assert_ok(void)
+{
+ routerlist_t *rl = router_get_routerlist();
+ networkstatus_t *ns = networkstatus_get_latest_consensus();
+ digestmap_t *dm;
+
+ if (!the_nodelist)
+ return;
+
+ dm = digestmap_new();
+
+ /* every routerinfo in rl->routers should be in the nodelist. */
+ if (rl) {
+ SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) {
+ const node_t *node = node_get_by_id(ri->cache_info.identity_digest);
+ tor_assert(node && node->ri == ri);
+ tor_assert(fast_memeq(ri->cache_info.identity_digest,
+ node->identity, DIGEST_LEN));
+ tor_assert(! digestmap_get(dm, node->identity));
+ digestmap_set(dm, node->identity, (void*)node);
+ } SMARTLIST_FOREACH_END(ri);
+ }
+
+ /* every routerstatus in ns should be in the nodelist */
+ if (ns) {
+ SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
+ const node_t *node = node_get_by_id(rs->identity_digest);
+ tor_assert(node && node->rs == rs);
+ tor_assert(fast_memeq(rs->identity_digest, node->identity, DIGEST_LEN));
+ digestmap_set(dm, node->identity, (void*)node);
+ if (ns->flavor == FLAV_MICRODESC) {
+ /* If it's a microdesc consensus, every entry that has a
+ * microdescriptor should be in the nodelist.
+ */
+ microdesc_t *md =
+ microdesc_cache_lookup_by_digest256(NULL, rs->descriptor_digest);
+ tor_assert(md == node->md);
+ if (md)
+ tor_assert(md->held_by_nodes >= 1);
+ }
+ } SMARTLIST_FOREACH_END(rs);
+ }
+
+ /* The nodelist should have no other entries, and its entries should be
+ * well-formed. */
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ tor_assert(digestmap_get(dm, node->identity) != NULL);
+ tor_assert(node_sl_idx == node->nodelist_idx);
+ } SMARTLIST_FOREACH_END(node);
+
+ tor_assert((long)smartlist_len(the_nodelist->nodes) ==
+ (long)HT_SIZE(&the_nodelist->nodes_by_id));
+
+ digestmap_free(dm, NULL);
+}
+
+/** Return a list of a node_t * for every node we know about. The caller
+ * MUST NOT modify the list. (You can set and clear flags in the nodes if
+ * you must, but you must not add or remove nodes.) */
+smartlist_t *
+nodelist_get_list(void)
+{
+ init_nodelist();
+ return the_nodelist->nodes;
+}
+
+/** Given a hex-encoded nickname of the format DIGEST, $DIGEST, $DIGEST=name,
+ * or $DIGEST~name, return the node with the matching identity digest and
+ * nickname (if any). Return NULL if no such node exists, or if <b>hex_id</b>
+ * is not well-formed. */
+const node_t *
+node_get_by_hex_id(const char *hex_id)
+{
+ char digest_buf[DIGEST_LEN];
+ char nn_buf[MAX_NICKNAME_LEN+1];
+ char nn_char='\0';
+
+ if (hex_digest_nickname_decode(hex_id, digest_buf, &nn_char, nn_buf)==0) {
+ const node_t *node = node_get_by_id(digest_buf);
+ if (!node)
+ return NULL;
+ if (nn_char) {
+ const char *real_name = node_get_nickname(node);
+ if (!real_name || strcasecmp(real_name, nn_buf))
+ return NULL;
+ if (nn_char == '=') {
+ const char *named_id =
+ networkstatus_get_router_digest_by_nickname(nn_buf);
+ if (!named_id || tor_memneq(named_id, digest_buf, DIGEST_LEN))
+ return NULL;
+ }
+ }
+ return node;
+ }
+
+ return NULL;
+}
+
+/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
+ * the corresponding node_t, or NULL if none exists. Warn the user if
+ * <b>warn_if_unnamed</b> is set, and they have specified a router by
+ * nickname, but the Named flag isn't set for that router. */
+const node_t *
+node_get_by_nickname(const char *nickname, int warn_if_unnamed)
+{
+ const node_t *node;
+ if (!the_nodelist)
+ return NULL;
+
+ /* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */
+ if ((node = node_get_by_hex_id(nickname)) != NULL)
+ return node;
+
+ if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME))
+ return NULL;
+
+ /* Okay, so if we get here, the nickname is just a nickname. Is there
+ * a binding for it in the consensus? */
+ {
+ const char *named_id =
+ networkstatus_get_router_digest_by_nickname(nickname);
+ if (named_id)
+ return node_get_by_id(named_id);
+ }
+
+ /* Is it marked as owned-by-someone-else? */
+ if (networkstatus_nickname_is_unnamed(nickname)) {
+ log_info(LD_GENERAL, "The name %s is listed as Unnamed: there is some "
+ "router that holds it, but not one listed in the current "
+ "consensus.", escaped(nickname));
+ return NULL;
+ }
+
+ /* Okay, so the name is not canonical for anybody. */
+ {
+ smartlist_t *matches = smartlist_create();
+ const node_t *choice = NULL;
+
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ if (!strcasecmp(node_get_nickname(node), nickname))
+ smartlist_add(matches, node);
+ } SMARTLIST_FOREACH_END(node);
+
+ if (smartlist_len(matches)>1 && warn_if_unnamed) {
+ int any_unwarned = 0;
+ SMARTLIST_FOREACH_BEGIN(matches, node_t *, node) {
+ if (!node->name_lookup_warned) {
+ node->name_lookup_warned = 1;
+ any_unwarned = 1;
+ }
+ } SMARTLIST_FOREACH_END(node);
+
+ if (any_unwarned) {
+ log_warn(LD_CONFIG, "There are multiple matches for the name %s, "
+ "but none is listed as Named in the directory consensus. "
+ "Choosing one arbitrarily.", nickname);
+ }
+ } else if (smartlist_len(matches)>1 && warn_if_unnamed) {
+ char fp[HEX_DIGEST_LEN+1];
+ node_t *node = smartlist_get(matches, 0);
+ if (node->name_lookup_warned) {
+ base16_encode(fp, sizeof(fp), node->identity, DIGEST_LEN);
+ log_warn(LD_CONFIG,
+ "You specified a server \"%s\" by name, but the directory "
+ "authorities do not have any key registered for this "
+ "nickname -- so it could be used by any server, not just "
+ "the one you meant. "
+ "To make sure you get the same server in the future, refer "
+ "to it by key, as \"$%s\".", nickname, fp);
+ node->name_lookup_warned = 1;
+ }
+ }
+
+ if (smartlist_len(matches))
+ choice = smartlist_get(matches, 0);
+
+ smartlist_free(matches);
+ return choice;
+ }
+}
+
+/** Return the nickname of <b>node</b>, or NULL if we can't find one. */
+const char *
+node_get_nickname(const node_t *node)
+{
+ tor_assert(node);
+ if (node->rs)
+ return node->rs->nickname;
+ else if (node->ri)
+ return node->ri->nickname;
+ else
+ return NULL;
+}
+
+/** Return true iff the nickname of <b>node</b> is canonical, based on the
+ * latest consensus. */
+int
+node_is_named(const node_t *node)
+{
+ const char *named_id;
+ const char *nickname = node_get_nickname(node);
+ if (!nickname)
+ return 0;
+ named_id = networkstatus_get_router_digest_by_nickname(nickname);
+ if (!named_id)
+ return 0;
+ return tor_memeq(named_id, node->identity, DIGEST_LEN);
+}
+
+/** Return true iff <b>node</b> appears to be a directory authority or
+ * directory cache */
+int
+node_is_dir(const node_t *node)
+{
+ if (node->rs)
+ return node->rs->dir_port != 0;
+ else if (node->ri)
+ return node->ri->dir_port != 0;
+ else
+ return 0;
+}
+
+/** Return true iff <b>node</b> has either kind of usable descriptor -- that
+ * is, a routerdecriptor or a microdescriptor. */
+int
+node_has_descriptor(const node_t *node)
+{
+ return (node->ri ||
+ (node->rs && node->md));
+}
+
+/** Return the router_purpose of <b>node</b>. */
+int
+node_get_purpose(const node_t *node)
+{
+ if (node->ri)
+ return node->ri->purpose;
+ else
+ return ROUTER_PURPOSE_GENERAL;
+}
+
+/** Compute the verbose ("extended") nickname of <b>node</b> and store it
+ * into the MAX_VERBOSE_NICKNAME_LEN+1 character buffer at
+ * <b>verbose_nickname_out</b> */
+void
+node_get_verbose_nickname(const node_t *node,
+ char *verbose_name_out)
+{
+ const char *nickname = node_get_nickname(node);
+ int is_named = node_is_named(node);
+ verbose_name_out[0] = '$';
+ base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, node->identity,
+ DIGEST_LEN);
+ if (!nickname)
+ return;
+ verbose_name_out[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
+ strlcpy(verbose_name_out+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1);
+}
+
+/** Return true iff it seems that <b>node</b> allows circuits to exit
+ * through it directlry from the client. */
+int
+node_allows_single_hop_exits(const node_t *node)
+{
+ if (node && node->ri)
+ return node->ri->allow_single_hop_exits;
+ else
+ return 0;
+}
+
+/** Return true iff it seems that <b>node</b> has an exit policy that doesn't
+ * actually permit anything to exit, or we don't know its exit policy */
+int
+node_exit_policy_rejects_all(const node_t *node)
+{
+ if (node->rejects_all)
+ return 1;
+
+ if (node->ri)
+ return node->ri->policy_is_reject_star;
+ else if (node->md)
+ return node->md->exit_policy == NULL ||
+ short_policy_is_reject_star(node->md->exit_policy);
+ else
+ return 1;
+}
+
+/** Copy the address for <b>node</b> into *<b>addr_out</b>. */
+int
+node_get_addr(const node_t *node, tor_addr_t *addr_out)
+{
+ if (node->ri) {
+ tor_addr_from_ipv4h(addr_out, node->ri->addr);
+ return 0;
+ } else if (node->rs) {
+ tor_addr_from_ipv4h(addr_out, node->rs->addr);
+ return 0;
+ }
+ return -1;
+}
+
+/** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't
+ * seem to have one. */
+uint32_t
+node_get_addr_ipv4h(const node_t *node)
+{
+ if (node->ri) {
+ return node->ri->addr;
+ } else if (node->rs) {
+ return node->rs->addr;
+ }
+ return 0;
+}
+
+/** Copy a string representation of the IP address for <b>node</b> into the
+ * <b>len</b>-byte buffer at <b>buf</b>.
+ */
+void
+node_get_address_string(const node_t *node, char *buf, size_t len)
+{
+ if (node->ri) {
+ strlcpy(buf, node->ri->address, len);
+ } else if (node->rs) {
+ tor_addr_t addr;
+ tor_addr_from_ipv4h(&addr, node->rs->addr);
+ tor_addr_to_str(buf, &addr, len, 0);
+ } else {
+ buf[0] = '\0';
+ }
+}
+
+/** Return <b>node</b>'s declared uptime, or -1 if it doesn't seem to have
+ * one. */
+long
+node_get_declared_uptime(const node_t *node)
+{
+ if (node->ri)
+ return node->ri->uptime;
+ else
+ return -1;
+}
+
+/** Return <b>node</b>'s declared or_port */
+uint16_t
+node_get_orport(const node_t *node)
+{
+ if (node->ri)
+ return node->ri->or_port;
+ else if (node->rs)
+ return node->rs->or_port;
+ else
+ return 0;
+}
+
+/** Return <b>node</b>'s platform string, or NULL if we don't know it. */
+const char *
+node_get_platform(const node_t *node)
+{
+ /* If we wanted, we could record the version in the routerstatus_t, since
+ * the consensus lists it. We don't, though, so this function just won't
+ * work with microdescriptors. */
+ if (node->ri)
+ return node->ri->platform;
+ else
+ return NULL;
+}
+
+/** Return <b>node</b>'s time of publication, or 0 if we don't have one. */
+time_t
+node_get_published_on(const node_t *node)
+{
+ if (node->ri)
+ return node->ri->cache_info.published_on;
+ else
+ return 0;
+}
+
+/** Return true iff <b>node</b> is one representing this router. */
+int
+node_is_me(const node_t *node)
+{
+ return router_digest_is_me(node->identity);
+}
+
+/** Return <b>node</b> declared family (as a list of names), or NULL if
+ * the node didn't declare a family. */
+const smartlist_t *
+node_get_declared_family(const node_t *node)
+{
+ if (node->ri && node->ri->declared_family)
+ return node->ri->declared_family;
+ else if (node->md && node->md->family)
+ return node->md->family;
+ else
+ return NULL;
+}
+
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
new file mode 100644
index 000000000..bd2e63953
--- /dev/null
+++ b/src/or/nodelist.h
@@ -0,0 +1,60 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file microdesc.h
+ * \brief Header file for microdesc.c.
+ **/
+
+#ifndef _TOR_NODELIST_H
+#define _TOR_NODELIST_H
+
+node_t *node_get_mutable_by_id(const char *identity_digest);
+const node_t *node_get_by_id(const char *identity_digest);
+const node_t *node_get_by_hex_id(const char *identity_digest);
+node_t *nodelist_add_routerinfo(routerinfo_t *ri);
+node_t *nodelist_add_microdesc(microdesc_t *md);
+void nodelist_set_consensus(networkstatus_t *ns);
+
+void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md);
+void nodelist_remove_routerinfo(routerinfo_t *ri);
+void nodelist_purge(void);
+
+void nodelist_free_all(void);
+void nodelist_assert_ok(void);
+
+const node_t *node_get_by_nickname(const char *nickname, int warn_if_unnamed);
+void node_get_verbose_nickname(const node_t *node,
+ char *verbose_name_out);
+int node_is_named(const node_t *node);
+int node_is_dir(const node_t *node);
+int node_has_descriptor(const node_t *node);
+int node_get_purpose(const node_t *node);
+#define node_is_bridge(node) \
+ (node_get_purpose((node)) == ROUTER_PURPOSE_BRIDGE)
+int node_is_me(const node_t *node);
+int node_exit_policy_rejects_all(const node_t *node);
+int node_get_addr(const node_t *node, tor_addr_t *addr_out);
+uint32_t node_get_addr_ipv4h(const node_t *node);
+int node_allows_single_hop_exits(const node_t *node);
+uint16_t node_get_orport(const node_t *node);
+const char *node_get_nickname(const node_t *node);
+const char *node_get_platform(const node_t *node);
+void node_get_address_string(const node_t *node, char *cp, size_t len);
+long node_get_declared_uptime(const node_t *node);
+time_t node_get_published_on(const node_t *node);
+const smartlist_t *node_get_declared_family(const node_t *node);
+
+smartlist_t *nodelist_get_list(void);
+
+/* XXXX These need to move out of routerlist.c */
+void nodelist_refresh_countries(void);
+void node_set_country(node_t *node);
+void nodelist_add_node_and_family(smartlist_t *nodes, const node_t *node);
+int nodes_in_same_family(const node_t *node1, const node_t *node2);
+
+#endif
+
diff --git a/src/or/ntmain.c b/src/or/ntmain.c
index b2fee648c..4eb487e97 100644
--- a/src/or/ntmain.c
+++ b/src/or/ntmain.c
@@ -193,7 +193,6 @@ nt_service_loadlibrary(void)
*/
int
nt_service_is_stopping(void)
-/* XXXX this function would probably _love_ to be inline, in 0.2.0. */
{
/* If we haven't loaded the function pointers, we can't possibly be an NT
* service trying to shut down. */
diff --git a/src/or/or.h b/src/or/or.h
index 7d50e1f50..e4f9b9b2b 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -23,8 +23,12 @@
#endif
#ifdef MS_WINDOWS
+#ifndef WIN32_WINNT
#define WIN32_WINNT 0x400
+#endif
+#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x400
+#endif
#define WIN32_LEAN_AND_MEAN
#endif
@@ -83,6 +87,13 @@
#define snprintf _snprintf
#endif
+#ifdef USE_BUFFEREVENTS
+#include <event2/bufferevent.h>
+#include <event2/buffer.h>
+#include <event2/util.h>
+#endif
+
+#include "crypto.h"
#include "tortls.h"
#include "../common/torlog.h"
#include "container.h"
@@ -225,15 +236,30 @@ typedef enum {
#define PROXY_CONNECT 1
#define PROXY_SOCKS4 2
#define PROXY_SOCKS5 3
+/* !!!! If there is ever a PROXY_* type over 2, we must grow the proxy_type
+ * field in or_connection_t */
+/* pluggable transports proxy type */
+#define PROXY_PLUGGABLE 4
/* Proxy client handshake states */
-#define PROXY_HTTPS_WANT_CONNECT_OK 1
-#define PROXY_SOCKS4_WANT_CONNECT_OK 2
-#define PROXY_SOCKS5_WANT_AUTH_METHOD_NONE 3
-#define PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929 4
-#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 5
-#define PROXY_SOCKS5_WANT_CONNECT_OK 6
-#define PROXY_CONNECTED 7
+/* We use a proxy but we haven't even connected to it yet. */
+#define PROXY_INFANT 1
+/* We use an HTTP proxy and we've sent the CONNECT command. */
+#define PROXY_HTTPS_WANT_CONNECT_OK 2
+/* We use a SOCKS4 proxy and we've sent the CONNECT command. */
+#define PROXY_SOCKS4_WANT_CONNECT_OK 3
+/* We use a SOCKS5 proxy and we try to negotiate without
+ any authentication . */
+#define PROXY_SOCKS5_WANT_AUTH_METHOD_NONE 4
+/* We use a SOCKS5 proxy and we try to negotiate with
+ Username/Password authentication . */
+#define PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929 5
+/* We use a SOCKS5 proxy and we just sent our credentials. */
+#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 6
+/* We use a SOCKS5 proxy and we just sent our CONNECT command. */
+#define PROXY_SOCKS5_WANT_CONNECT_OK 7
+/* We use a proxy and we CONNECTed successfully!. */
+#define PROXY_CONNECTED 8
/** True iff <b>x</b> is an edge connection. */
#define CONN_IS_EDGE(x) \
@@ -257,22 +283,27 @@ typedef enum {
#define OR_CONN_STATE_CONNECTING 1
/** State for a connection to an OR: waiting for proxy handshake to complete */
#define OR_CONN_STATE_PROXY_HANDSHAKING 2
-/** State for a connection to an OR or client: SSL is handshaking, not done
+/** State for an OR connection client: SSL is handshaking, not done
* yet. */
#define OR_CONN_STATE_TLS_HANDSHAKING 3
/** State for a connection to an OR: We're doing a second SSL handshake for
- * renegotiation purposes. */
+ * renegotiation purposes. (V2 handshake only.) */
#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 4
/** State for a connection at an OR: We're waiting for the client to
- * renegotiate. */
+ * renegotiate (to indicate a v2 handshake) or send a versions cell (to
+ * indicate a v3 handshake) */
#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 5
-/** State for a connection to an OR: We're done with our SSL handshake, but we
- * haven't yet negotiated link protocol versions and sent a netinfo cell.
- */
-#define OR_CONN_STATE_OR_HANDSHAKING 6
-/** State for a connection to an OR: Ready to send/receive cells. */
-#define OR_CONN_STATE_OPEN 7
-#define _OR_CONN_STATE_MAX 7
+/** State for an OR connection: We're done with our SSL handshake, we've done
+ * renegotiation, but we haven't yet negotiated link protocol versions and
+ * sent a netinfo cell. */
+#define OR_CONN_STATE_OR_HANDSHAKING_V2 6
+/** State for an OR connection: We're done with our SSL handshake, but we
+ * haven't yet negotiated link protocol versions, done a V3 handshake, and
+ * sent a netinfo cell. */
+#define OR_CONN_STATE_OR_HANDSHAKING_V3 7
+/** State for an OR connection: Ready to send/receive cells. */
+#define OR_CONN_STATE_OPEN 8
+#define _OR_CONN_STATE_MAX 8
#define _EXIT_CONN_STATE_MIN 1
/** State for an exit connection: waiting for response from DNS farm. */
@@ -386,7 +417,9 @@ typedef enum {
/** A connection to a hidden service directory server: download a v2 rendezvous
* descriptor. */
#define DIR_PURPOSE_FETCH_RENDDESC_V2 18
-#define _DIR_PURPOSE_MAX 18
+/** A connection to a directory server: download a microdescriptor. */
+#define DIR_PURPOSE_FETCH_MICRODESC 19
+#define _DIR_PURPOSE_MAX 19
/** True iff <b>p</b> is a purpose corresponding to uploading data to a
* directory server. */
@@ -792,9 +825,10 @@ typedef enum {
#define CELL_NETINFO 8
#define CELL_RELAY_EARLY 9
-/** True iff the cell command <b>x</b> is one that implies a variable-length
- * cell. */
-#define CELL_COMMAND_IS_VAR_LENGTH(x) ((x) == CELL_VERSIONS)
+#define CELL_VPADDING 128
+#define CELL_CERT 129
+#define CELL_AUTH_CHALLENGE 130
+#define CELL_AUTHENTICATE 131
/** How long to test reachability before complaining to the user. */
#define TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT (20*60)
@@ -807,6 +841,9 @@ typedef enum {
* Tor 0.1.2.x is obsolete, we can remove this. */
#define DEFAULT_CLIENT_NICKNAME "client"
+/** Name chosen by routers that don't configure nicknames */
+#define UNNAMED_ROUTER_NICKNAME "Unnamed"
+
/** Number of bytes in a SOCKS4 header. */
#define SOCKS4_NETWORK_LEN 8
@@ -857,7 +894,7 @@ typedef struct var_cell_t {
/** Number of bytes actually stored in <b>payload</b> */
uint16_t payload_len;
/** Payload of this cell */
- uint8_t payload[1];
+ uint8_t payload[FLEXIBLE_ARRAY_MEMBER];
} var_cell_t;
/** A cell as packed for writing to the network. */
@@ -901,14 +938,21 @@ typedef struct {
typedef struct buf_t buf_t;
typedef struct socks_request_t socks_request_t;
+#ifdef USE_BUFFEREVENTS
+#define generic_buffer_t struct evbuffer
+#else
+#define generic_buffer_t buf_t
+#endif
/* Values for connection_t.magic: used to make sure that downcasts (casts from
* connection_t to foo_connection_t) are safe. */
#define BASE_CONNECTION_MAGIC 0x7C3C304Eu
#define OR_CONNECTION_MAGIC 0x7D31FF03u
#define EDGE_CONNECTION_MAGIC 0xF0374013u
+#define ENTRY_CONNECTION_MAGIC 0xbb4a5703
#define DIR_CONNECTION_MAGIC 0x9988ffeeu
#define CONTROL_CONNECTION_MAGIC 0x8abc765du
+#define LISTENER_CONNECTION_MAGIC 0x1a1ac741u
/** Description of a connection to another host or process, and associated
* data.
@@ -974,6 +1018,7 @@ typedef struct connection_t {
/** Our socket; -1 if this connection is closed, or has no socket. */
tor_socket_t s;
int conn_array_index; /**< Index into the global connection array. */
+
struct event *read_event; /**< Libevent event structure. */
struct event *write_event; /**< Libevent event structure. */
buf_t *inbuf; /**< Buffer holding data read over this connection. */
@@ -984,6 +1029,11 @@ typedef struct connection_t {
* read? */
time_t timestamp_lastwritten; /**< When was the last time libevent said we
* could write? */
+
+#ifdef USE_BUFFEREVENTS
+ struct bufferevent *bufev; /**< A Libevent buffered IO structure. */
+#endif
+
time_t timestamp_created; /**< When was this connection_t created? */
/* XXXX_IP6 make this IPv6-capable */
@@ -1008,17 +1058,73 @@ typedef struct connection_t {
/** Unique identifier for this connection on this Tor instance. */
uint64_t global_identifier;
- /* XXXX023 move this field, and all the listener-only fields (just
- socket_family, I think), into a new listener_connection_t subtype. */
+ /** Unique ID for measuring tunneled network status requests. */
+ uint64_t dirreq_id;
+} connection_t;
+
+typedef struct listener_connection_t {
+ connection_t _base;
+
/** If the connection is a CONN_TYPE_AP_DNS_LISTENER, this field points
* to the evdns_server_port it uses to listen to and answer connections. */
struct evdns_server_port *dns_server_port;
- /** Unique ID for measuring tunneled network status requests. */
- uint64_t dirreq_id;
-} connection_t;
+ /** @name Isolation parameters
+ *
+ * For an AP listener, these fields describe how to isolate streams that
+ * arrive on the listener.
+ *
+ * @{
+ */
+ /** The session group for this listener. */
+ int session_group;
+ /** One or more ISO_ flags to describe how to isolate streams. */
+ uint8_t isolation_flags;
+ /**@}*/
+
+} listener_connection_t;
-/** Stores flags and information related to the portion of a v2 Tor OR
+/** Minimum length of the random part of an AUTH_CHALLENGE cell. */
+#define OR_AUTH_CHALLENGE_LEN 32
+
+/**
+ * @name Certificate types for CERT cells.
+ *
+ * These values are defined by the protocol, and affect how an X509
+ * certificate in a CERT cell is interpreted and used.
+ *
+ * @{ */
+/** A certificate that authenticates a TLS link key. The subject key
+ * must match the key used in the TLS handshake; it must be signed by
+ * the identity key. */
+#define OR_CERT_TYPE_TLS_LINK 1
+/** A self-signed identity certificate. The subject key must be a
+ * 1024-bit RSA key. */
+#define OR_CERT_TYPE_ID_1024 2
+/** A certificate that authenticates a key used in an AUTHENTICATE cell
+ * in the v3 handshake. The subject key must be a 1024-bit RSA key; it
+ * must be signed by the identity key */
+#define OR_CERT_TYPE_AUTH_1024 3
+/**@}*/
+
+/** The one currently supported type of AUTHENTICATE cell. It contains
+ * a bunch of structures signed with an RSA1024 key. The signed
+ * structures include a HMAC using negotiated TLS secrets, and a digest
+ * of all cells sent or received before the AUTHENTICATE cell (including
+ * the random server-generated AUTH_CHALLENGE cell).
+ */
+#define AUTHTYPE_RSA_SHA256_TLSSECRET 1
+
+/** The length of the part of the AUTHENTICATE cell body that the client and
+ * server can generate independently (when using RSA_SHA256_TLSSECRET). It
+ * contains everything except the client's timestamp, the client's randomly
+ * generated nonce, and the signature. */
+#define V3_AUTH_FIXED_PART_LEN (8+(32*6))
+/** The length of the part of the AUTHENTICATE cell body that the client
+ * signs. */
+#define V3_AUTH_BODY_LEN (V3_AUTH_FIXED_PART_LEN + 8 + 16)
+
+/** Stores flags and information related to the portion of a v2/v3 Tor OR
* connection handshake that happens after the TLS handshake is finished.
*/
typedef struct or_handshake_state_t {
@@ -1029,6 +1135,52 @@ typedef struct or_handshake_state_t {
unsigned int started_here : 1;
/** True iff we have received and processed a VERSIONS cell. */
unsigned int received_versions : 1;
+ /** True iff we have received and processed an AUTH_CHALLENGE cell */
+ unsigned int received_auth_challenge : 1;
+ /** True iff we have received and processed a CERT cell. */
+ unsigned int received_cert_cell : 1;
+ /** True iff we have received and processed an AUTHENTICATE cell */
+ unsigned int received_authenticate : 1;
+
+ /* True iff we've received valid authentication to some identity. */
+ unsigned int authenticated : 1;
+
+ /** True iff we should feed outgoing cells into digest_sent and
+ * digest_received respectively.
+ *
+ * From the server's side of the v3 handshake, we want to capture everything
+ * from the VERSIONS cell through and including the AUTH_CHALLENGE cell.
+ * From the client's, we want to capture everything from the VERSIONS cell
+ * through but *not* including the AUTHENTICATE cell.
+ *
+ * @{ */
+ unsigned int digest_sent_data : 1;
+ unsigned int digest_received_data : 1;
+ /**@}*/
+
+ /** Identity digest that we have received and authenticated for our peer
+ * on this connection. */
+ uint8_t authenticated_peer_id[DIGEST_LEN];
+
+ /** Digests of the cells that we have sent or received as part of a V3
+ * handshake. Used for making and checking AUTHENTICATE cells.
+ *
+ * @{
+ */
+ crypto_digest_env_t *digest_sent;
+ crypto_digest_env_t *digest_received;
+ /** @} */
+
+ /** Certificates that a connection initiator sent us in a CERT cell; we're
+ * holding on to them until we get an AUTHENTICATE cell.
+ *
+ * @{
+ */
+ /** The cert for the key that's supposed to sign the AUTHENTICATE cell */
+ tor_cert_t *auth_cert;
+ /** A self-signed identity certificate */
+ tor_cert_t *id_cert;
+ /**@}*/
} or_handshake_state_t;
/** Subtype of connection_t for an "OR connection" -- that is, one that speaks
@@ -1074,6 +1226,7 @@ typedef struct or_connection_t {
unsigned int is_connection_with_client:1;
/** True iff this is an outgoing connection. */
unsigned int is_outgoing:1;
+ unsigned int proxy_type:2; /**< One of PROXY_NONE...PROXY_SOCKS5 */
uint8_t link_proto; /**< What protocol version are we using? 0 for
* "none negotiated yet." */
circid_t next_circ_id; /**< Which circ_id do we try to use next on
@@ -1089,10 +1242,16 @@ typedef struct or_connection_t {
/* bandwidth* and *_bucket only used by ORs in OPEN state: */
int bandwidthrate; /**< Bytes/s added to the bucket. (OPEN ORs only.) */
int bandwidthburst; /**< Max bucket size for this conn. (OPEN ORs only.) */
+#ifndef USE_BUFFEREVENTS
int read_bucket; /**< When this hits 0, stop receiving. Every second we
* add 'bandwidthrate' to this, capping it at
* bandwidthburst. (OPEN ORs only) */
int write_bucket; /**< When this hits 0, stop writing. Like read_bucket. */
+#else
+ /** DOCDOC */
+ /* XXXX we could share this among all connections. */
+ struct ev_token_bucket_cfg *bucket_cfg;
+#endif
int n_circuits; /**< How many circuits use this connection as p_conn or
* n_conn ? */
@@ -1115,27 +1274,27 @@ typedef struct or_connection_t {
* identity digest as this one. */
} or_connection_t;
-/** Subtype of connection_t for an "edge connection" -- that is, a socks (ap)
+/** Subtype of connection_t for an "edge connection" -- that is, an entry (ap)
* connection, or an exit. */
typedef struct edge_connection_t {
connection_t _base;
struct edge_connection_t *next_stream; /**< Points to the next stream at this
* edge, if any */
- struct crypt_path_t *cpath_layer; /**< A pointer to which node in the circ
- * this conn exits at. */
int package_window; /**< How many more relay cells can I send into the
* circuit? */
int deliver_window; /**< How many more relay cells can end at me? */
- /** Nickname of planned exit node -- used with .exit support. */
- char *chosen_exit_name;
-
- socks_request_t *socks_request; /**< SOCKS structure describing request (AP
- * only.) */
struct circuit_t *on_circuit; /**< The circuit (if any) that this edge
* connection is using. */
+ /** A pointer to which node in the circ this conn exits at. Set for AP
+ * connections and for hidden service exit connections. */
+ struct crypt_path_t *cpath_layer;
+ /** What rendezvous service are we querying for (if an AP) or providing (if
+ * an exit)? */
+ rend_data_t *rend_data;
+
uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit
* connection. Exit connections only. */
@@ -1151,14 +1310,62 @@ typedef struct edge_connection_t {
/** Bytes written since last call to control_event_stream_bandwidth_used() */
uint32_t n_written;
- /** What rendezvous service are we querying for? (AP only) */
- rend_data_t *rend_data;
+ /** True iff this connection is for a DNS request only. */
+ unsigned int is_dns_request:1;
+
+ unsigned int edge_has_sent_end:1; /**< For debugging; only used on edge
+ * connections. Set once we've set the stream end,
+ * and check in connection_about_to_close_connection().
+ */
+ /** True iff we've blocked reading until the circuit has fewer queued
+ * cells. */
+ unsigned int edge_blocked_on_circ:1;
+
+} edge_connection_t;
+
+/** Subtype of edge_connection_t for an "entry connection" -- that is, a SOCKS
+ * connection, a DNS request, a TransPort connection or a NATD connection */
+typedef struct entry_connection_t {
+ edge_connection_t _edge;
+
+ /** Nickname of planned exit node -- used with .exit support. */
+ char *chosen_exit_name;
+
+ socks_request_t *socks_request; /**< SOCKS structure describing request (AP
+ * only.) */
+
+ /* === Isolation related, AP only. === */
+ /** AP only: based on which factors do we isolate this stream? */
+ uint8_t isolation_flags;
+ /** AP only: what session group is this stream in? */
+ int session_group;
+ /** AP only: The newnym epoch in which we created this connection. */
+ unsigned nym_epoch;
+ /** AP only: The original requested address before we rewrote it. */
+ char *original_dest_address;
+ /* Other fields to isolate on already exist. The ClientAddr is addr. The
+ ClientProtocol is a combination of type and socks_request->
+ socks_version. SocksAuth is socks_request->username/password.
+ DestAddr is in socks_request->address. */
/** Number of times we've reassigned this application connection to
* a new circuit. We keep track because the timeout is longer if we've
* already retried several times. */
uint8_t num_socks_retries;
+ /** For AP connections only: buffer for data that we have sent
+ * optimistically, which we might need to re-send if we have to
+ * retry this connection. */
+ generic_buffer_t *pending_optimistic_data;
+ /* For AP connections only: buffer for data that we previously sent
+ * optimistically which we are currently re-sending as we retry this
+ * connection. */
+ generic_buffer_t *sending_optimistic_data;
+
+ /** If this is a DNSPort connection, this field holds the pending DNS
+ * request that we're going to try to answer. */
+ struct evdns_server_request *dns_server_request;
+
#define NUM_CIRCUITS_LAUNCHED_THRESHOLD 10
/** Number of times we've launched a circuit to handle this stream. If
* it gets too high, that could indicate an inconsistency between our
@@ -1166,9 +1373,6 @@ typedef struct edge_connection_t {
* stream to one of the available circuits" logic. */
unsigned int num_circuits_launched:4;
- /** True iff this connection is for a DNS request only. */
- unsigned int is_dns_request:1;
-
/** True iff this stream must attach to a one-hop circuit (e.g. for
* begin_dir). */
unsigned int want_onehop:1;
@@ -1176,13 +1380,6 @@ typedef struct edge_connection_t {
* itself rather than BEGIN (either via onehop or via a whole circuit). */
unsigned int use_begindir:1;
- unsigned int edge_has_sent_end:1; /**< For debugging; only used on edge
- * connections. Set once we've set the stream end,
- * and check in connection_about_to_close_connection().
- */
- /** True iff we've blocked reading until the circuit has fewer queued
- * cells. */
- unsigned int edge_blocked_on_circ:1;
/** For AP connections only. If 1, and we fail to reach the chosen exit,
* stop requiring it. */
unsigned int chosen_exit_optional:1;
@@ -1196,19 +1393,26 @@ typedef struct edge_connection_t {
* NATd connection */
unsigned int is_transparent_ap:1;
- /** If this is a DNSPort connection, this field holds the pending DNS
- * request that we're going to try to answer. */
- struct evdns_server_request *dns_server_request;
+ /** For AP connections only: Set if this connection's target exit node
+ * allows optimistic data (that is, data sent on this stream before
+ * the exit has sent a CONNECTED cell) and we have chosen to use it.
+ */
+ unsigned int may_use_optimistic_data : 1;
-} edge_connection_t;
+} entry_connection_t;
/** Subtype of connection_t for an "directory connection" -- that is, an HTTP
* connection to retrieve or serve directory material. */
typedef struct dir_connection_t {
connection_t _base;
- char *requested_resource; /**< Which 'resource' did we ask the directory
- * for? */
+ /** Which 'resource' did we ask the directory for? This is typically the part
+ * of the URL string that defines, relative to the directory conn purpose,
+ * what thing we want. For example, in router descriptor downloads by
+ * descriptor digest, it contains "d/", then one ore more +-separated
+ * fingerprints.
+ **/
+ char *requested_resource;
unsigned int dirconn_direct:1; /**< Is this dirconn direct, or via Tor? */
/* Used only for server sides of some dir connections, to implement
@@ -1268,6 +1472,11 @@ typedef struct control_connection_t {
/** Helper macro: Given a pointer to to._base, of type from*, return &to. */
#define DOWNCAST(to, ptr) ((to*)SUBTYPE_P(ptr, to, _base))
+/** Cast a entry_connection_t subtype pointer to a edge_connection_t **/
+#define ENTRY_TO_EDGE_CONN(c) (&(((c))->_edge))
+/** Cast a entry_connection_t subtype pointer to a connection_t **/
+#define ENTRY_TO_CONN(c) (TO_CONN(ENTRY_TO_EDGE_CONN(c)))
+
/** Convert a connection_t* to an or_connection_t*; assert if the cast is
* invalid. */
static or_connection_t *TO_OR_CONN(connection_t *);
@@ -1277,9 +1486,18 @@ static dir_connection_t *TO_DIR_CONN(connection_t *);
/** Convert a connection_t* to an edge_connection_t*; assert if the cast is
* invalid. */
static edge_connection_t *TO_EDGE_CONN(connection_t *);
+/** Convert a connection_t* to an entry_connection_t*; assert if the cast is
+ * invalid. */
+static entry_connection_t *TO_ENTRY_CONN(connection_t *);
+/** Convert a edge_connection_t* to an entry_connection_t*; assert if the cast
+ * is invalid. */
+static entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *);
/** Convert a connection_t* to an control_connection_t*; assert if the cast is
* invalid. */
static control_connection_t *TO_CONTROL_CONN(connection_t *);
+/** Convert a connection_t* to an listener_connection_t*; assert if the cast is
+ * invalid. */
+static listener_connection_t *TO_LISTENER_CONN(connection_t *);
static INLINE or_connection_t *TO_OR_CONN(connection_t *c)
{
@@ -1293,14 +1511,75 @@ static INLINE dir_connection_t *TO_DIR_CONN(connection_t *c)
}
static INLINE edge_connection_t *TO_EDGE_CONN(connection_t *c)
{
- tor_assert(c->magic == EDGE_CONNECTION_MAGIC);
+ tor_assert(c->magic == EDGE_CONNECTION_MAGIC ||
+ c->magic == ENTRY_CONNECTION_MAGIC);
return DOWNCAST(edge_connection_t, c);
}
+static INLINE entry_connection_t *TO_ENTRY_CONN(connection_t *c)
+{
+ tor_assert(c->magic == ENTRY_CONNECTION_MAGIC);
+ return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, _edge._base);
+}
+static INLINE entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *c)
+{
+ tor_assert(c->_base.magic == ENTRY_CONNECTION_MAGIC);
+ return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, _edge);
+}
static INLINE control_connection_t *TO_CONTROL_CONN(connection_t *c)
{
tor_assert(c->magic == CONTROL_CONNECTION_MAGIC);
return DOWNCAST(control_connection_t, c);
}
+static INLINE listener_connection_t *TO_LISTENER_CONN(connection_t *c)
+{
+ tor_assert(c->magic == LISTENER_CONNECTION_MAGIC);
+ return DOWNCAST(listener_connection_t, c);
+}
+
+/* Conditional macros to help write code that works whether bufferevents are
+ disabled or not.
+
+ We can't just write:
+ if (conn->bufev) {
+ do bufferevent stuff;
+ } else {
+ do other stuff;
+ }
+ because the bufferevent stuff won't even compile unless we have a fairly
+ new version of Libevent. Instead, we say:
+ IF_HAS_BUFFEREVENT(conn, { do_bufferevent_stuff } );
+ or:
+ IF_HAS_BUFFEREVENT(conn, {
+ do bufferevent stuff;
+ }) ELSE_IF_NO_BUFFEREVENT {
+ do non-bufferevent stuff;
+ }
+ If we're compiling with bufferevent support, then the macros expand more or
+ less to:
+ if (conn->bufev) {
+ do_bufferevent_stuff;
+ } else {
+ do non-bufferevent stuff;
+ }
+ and if we aren't using bufferevents, they expand more or less to:
+ { do non-bufferevent stuff; }
+*/
+#ifdef USE_BUFFEREVENTS
+#define HAS_BUFFEREVENT(c) (((c)->bufev) != NULL)
+#define IF_HAS_BUFFEREVENT(c, stmt) \
+ if ((c)->bufev) do { \
+ stmt ; \
+ } while (0)
+#define ELSE_IF_NO_BUFFEREVENT ; else
+#define IF_HAS_NO_BUFFEREVENT(c) \
+ if (NULL == (c)->bufev)
+#else
+#define HAS_BUFFEREVENT(c) (0)
+#define IF_HAS_BUFFEREVENT(c, stmt) (void)0
+#define ELSE_IF_NO_BUFFEREVENT ;
+#define IF_HAS_NO_BUFFEREVENT(c) \
+ if (1)
+#endif
/** What action type does an address policy indicate: accept or reject? */
typedef enum {
@@ -1417,11 +1696,6 @@ typedef struct signed_descriptor_t {
* networkstatus that listed it. 0 for "never listed in a consensus or
* status, so far as we know." */
time_t last_listed_as_valid_until;
-#ifdef TRACK_SERVED_TIME
- /** The last time we served anybody this descriptor. Used for internal
- * testing to see whether we're holding on to descriptors too long. */
- time_t last_served_at; /*XXXX remove if not useful. */
-#endif
/* If true, we do not ever try to save this object in the cache. */
unsigned int do_not_cache : 1;
/* If true, this item is meant to represent an extrainfo. */
@@ -1465,59 +1739,49 @@ typedef struct {
char *contact_info; /**< Declared contact info for this router. */
unsigned int is_hibernating:1; /**< Whether the router claims to be
* hibernating */
- unsigned int has_old_dnsworkers:1; /**< Whether the router is using
- * dnsworker code. */
- unsigned int caches_extra_info:1; /**< Whether the router caches and serves
- * extrainfo documents. */
- unsigned int allow_single_hop_exits:1; /**< Whether the router allows
- * single hop exits. */
-
- /* local info */
- unsigned int is_running:1; /**< As far as we know, is this OR currently
- * running? */
- unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
- * (For Authdir: Have we validated this OR?)
- */
- unsigned int is_named:1; /**< Do we believe the nickname that this OR gives
- * us? */
- unsigned int is_fast:1; /** Do we think this is a fast OR? */
- unsigned int is_stable:1; /** Do we think this is a stable OR? */
- unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
- unsigned int is_exit:1; /**< Do we think this is an OK exit? */
- unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
- * or otherwise nasty? */
- unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
- * underpowered, or otherwise useless? */
+ unsigned int caches_extra_info:1; /**< Whether the router says it caches and
+ * serves extrainfo documents. */
+ unsigned int allow_single_hop_exits:1; /**< Whether the router says
+ * it allows single hop exits. */
+
unsigned int wants_to_be_hs_dir:1; /**< True iff this router claims to be
* a hidden service directory. */
- unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
- * directory according to the authorities. */
unsigned int policy_is_reject_star:1; /**< True iff the exit policy for this
* router rejects everything. */
/** True if, after we have added this router, we should re-launch
* tests for it. */
unsigned int needs_retest_if_added:1;
-/** Tor can use this router for general positions in circuits. */
+/** Tor can use this router for general positions in circuits; we got it
+ * from a directory server as usual, or we're an authority and a server
+ * uploaded it. */
#define ROUTER_PURPOSE_GENERAL 0
-/** Tor should avoid using this router for circuit-building. */
+/** Tor should avoid using this router for circuit-building: we got it
+ * from a crontroller. If the controller wants to use it, it'll have to
+ * ask for it by identity. */
#define ROUTER_PURPOSE_CONTROLLER 1
-/** Tor should use this router only for bridge positions in circuits. */
+/** Tor should use this router only for bridge positions in circuits: we got
+ * it via a directory request from the bridge itself, or a bridge
+ * authority. x*/
#define ROUTER_PURPOSE_BRIDGE 2
/** Tor should not use this router; it was marked in cached-descriptors with
* a purpose we didn't recognize. */
#define ROUTER_PURPOSE_UNKNOWN 255
- uint8_t purpose; /** What positions in a circuit is this router good for? */
+ /* In what way did we find out about this router? One of ROUTER_PURPOSE_*.
+ * Routers of different purposes are kept segregated and used for different
+ * things; see notes on ROUTER_PURPOSE_* macros above.
+ */
+ uint8_t purpose;
/* The below items are used only by authdirservers for
* reachability testing. */
+
/** When was the last time we could reach this OR? */
time_t last_reachable;
/** When did we start testing reachability for this OR? */
time_t testing_since;
- /** According to the geoip db what country is this router in? */
- country_t country;
+
} routerinfo_t;
/** Information needed to keep and cache a signed extra-info document. */
@@ -1543,8 +1807,9 @@ typedef struct routerstatus_t {
* has. */
char identity_digest[DIGEST_LEN]; /**< Digest of the router's identity
* key. */
- char descriptor_digest[DIGEST_LEN]; /**< Digest of the router's most recent
- * descriptor. */
+ /** Digest of the router's most recent descriptor or microdescriptor.
+ * If it's a descriptor, we only use the first DIGEST_LEN bytes. */
+ char descriptor_digest[DIGEST256_LEN];
uint32_t addr; /**< IPv4 address for this router. */
uint16_t or_port; /**< OR port for this router. */
uint16_t dir_port; /**< Directory port for this router. */
@@ -1552,7 +1817,11 @@ typedef struct routerstatus_t {
unsigned int is_exit:1; /**< True iff this router is a good exit. */
unsigned int is_stable:1; /**< True iff this router stays up a long time. */
unsigned int is_fast:1; /**< True iff this router has good bandwidth. */
- unsigned int is_running:1; /**< True iff this router is up. */
+ /** True iff this router is called 'running' in the consensus. We give it
+ * this funny name so that we don't accidentally use this bit as a view of
+ * whether we think the router is *currently* running. If that's what you
+ * want to know, look at is_running in node_t. */
+ unsigned int is_flagged_running:1;
unsigned int is_named:1; /**< True iff "nickname" belongs to this router. */
unsigned int is_unnamed:1; /**< True iff "nickname" belongs to another
* router. */
@@ -1583,6 +1852,12 @@ typedef struct routerstatus_t {
/** True iff this router is a version that, if it caches directory info,
* we can get v3 downloads from. */
unsigned int version_supports_v3_dir:1;
+ /** True iff this router is a version that, if it caches directory info,
+ * we can get microdescriptors from. */
+ unsigned int version_supports_microdesc_cache:1;
+ /** True iff this router is a version that allows DATA cells to arrive on
+ * a stream before it has sent a CONNECTED cell. */
+ unsigned int version_supports_optimistic_data:1;
unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
@@ -1604,15 +1879,31 @@ typedef struct routerstatus_t {
* from this authority.) Applies in v2 networkstatus document only.
*/
unsigned int need_to_mirror:1;
- unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
- * to this (unnamed) router by nickname?
- */
time_t last_dir_503_at; /**< When did this router last tell us that it
* was too busy to serve directory info? */
download_status_t dl_status;
} routerstatus_t;
+/** A single entry in a parsed policy summary, describing a range of ports. */
+typedef struct short_policy_entry_t {
+ uint16_t min_port, max_port;
+} short_policy_entry_t;
+
+/** A short_poliy_t is the parsed version of a policy summary. */
+typedef struct short_policy_t {
+ /** True if the members of 'entries' are port ranges to accept; false if
+ * they are port ranges to reject */
+ unsigned int is_accept : 1;
+ /** The actual number of values in 'entries'. */
+ unsigned int n_entries : 31;
+ /** An array of 0 or more short_policy_entry_t values, each describing a
+ * range of ports that this policy accepts or rejects (depending on the
+ * value of is_accept).
+ */
+ short_policy_entry_t entries[FLEXIBLE_ARRAY_MEMBER];
+} short_policy_t;
+
/** A microdescriptor is the smallest amount of information needed to build a
* circuit through a router. They are generated by the directory authorities,
* using information from the uploaded routerinfo documents. They are not
@@ -1632,6 +1923,11 @@ typedef struct microdesc_t {
saved_location_t saved_location : 3;
/** If true, do not attempt to cache this microdescriptor on disk. */
unsigned int no_save : 1;
+ /** If true, this microdesc has an entry in the microdesc_map */
+ unsigned int held_in_map : 1;
+ /** Reference count: how many node_ts have a reference to this microdesc? */
+ unsigned int held_by_nodes;
+
/** If saved_location == SAVED_IN_CACHE, this field holds the offset of the
* microdescriptor in the cache. */
off_t off;
@@ -1654,15 +1950,83 @@ typedef struct microdesc_t {
crypto_pk_env_t *onion_pkey;
/** As routerinfo_t.family */
smartlist_t *family;
- /** Encoded exit policy summary */
- char *exitsummary; /**< exit policy summary -
- * XXX this probably should not stay a string. */
+ /** Exit policy summary */
+ short_policy_t *exit_policy;
} microdesc_t;
+/** A node_t represents a Tor router.
+ *
+ * Specifically, a node_t is a Tor router as we are using it: a router that
+ * we are considering for circuits, connections, and so on. A node_t is a
+ * thin wrapper around the routerstatus, routerinfo, and microdesc for a
+ * single wrapper, and provides a consistent interface for all of them.
+ *
+ * Also, a node_t has mutable state. While a routerinfo, a routerstatus,
+ * and a microdesc have[*] only the information read from a router
+ * descriptor, a consensus entry, and a microdescriptor (respectively)...
+ * a node_t has flags based on *our own current opinion* of the node.
+ *
+ * [*] Actually, there is some leftover information in each that is mutable.
+ * We should try to excise that.
+ */
+typedef struct node_t {
+ /* Indexing information */
+
+ /** Used to look up the node_t by its identity digest. */
+ HT_ENTRY(node_t) ht_ent;
+ /** Position of the node within the list of nodes */
+ int nodelist_idx;
+
+ /** The identity digest of this node_t. No more than one node_t per
+ * identity may exist at a time. */
+ char identity[DIGEST_LEN];
+
+ microdesc_t *md;
+ routerinfo_t *ri;
+ routerstatus_t *rs;
+
+ /* local info: copied from routerstatus, then possibly frobbed based
+ * on experience. Authorities set this stuff directly. */
+
+ unsigned int is_running:1; /**< As far as we know, is this OR currently
+ * running? */
+ unsigned int is_valid:1; /**< Has a trusted dirserver validated this OR?
+ * (For Authdir: Have we validated this OR?)
+ */
+ unsigned int is_fast:1; /** Do we think this is a fast OR? */
+ unsigned int is_stable:1; /** Do we think this is a stable OR? */
+ unsigned int is_possible_guard:1; /**< Do we think this is an OK guard? */
+ unsigned int is_exit:1; /**< Do we think this is an OK exit? */
+ unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
+ * or otherwise nasty? */
+ unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
+ * underpowered, or otherwise useless? */
+ unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
+ * directory according to the authorities. */
+
+ /* Local info: warning state. */
+
+ unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
+ * to this (unnamed) router by nickname?
+ */
+
+ /** Local info: we treat this node as if it rejects everything */
+ unsigned int rejects_all:1;
+
+ /* Local info: derived. */
+
+ /** According to the geoip db what country is this router in? */
+ country_t country;
+} node_t;
+
/** How many times will we try to download a router's descriptor before giving
* up? */
#define MAX_ROUTERDESC_DOWNLOAD_FAILURES 8
+/** How many times will we try to download a microdescriptor before giving
+ * up? */
+#define MAX_MICRODESC_DOWNLOAD_FAILURES 8
+
/** Contents of a v2 (non-consensus, non-vote) network status object. */
typedef struct networkstatus_v2_t {
/** When did we receive the network-status document? */
@@ -1776,9 +2140,6 @@ typedef enum {
FLAV_MICRODESC = 1,
} consensus_flavor_t;
-/** Which consensus flavor do we actually want to use to build circuits? */
-#define USABLE_CONSENSUS_FLAVOR FLAV_NS
-
/** How many different consensus flavors are there? */
#define N_CONSENSUS_FLAVORS ((int)(FLAV_MICRODESC)+1)
@@ -1948,24 +2309,33 @@ typedef struct authority_cert_t {
uint8_t is_cross_certified;
} authority_cert_t;
-/** Bitfield enum type listing types of directory authority/directory
- * server. */
+/** Bitfield enum type listing types of information that directory authorities
+ * can be authoritative about, and that directory caches may or may not cache.
+ *
+ * Note that the granularity here is based on authority granularity and on
+ * cache capabilities. Thus, one particular bit may correspond in practice to
+ * a few types of directory info, so long as every authority that pronounces
+ * officially about one of the types prounounces officially about all of them,
+ * and so long as every cache that caches one of them caches all of them.
+ */
typedef enum {
- NO_AUTHORITY = 0,
+ NO_DIRINFO = 0,
/** Serves/signs v1 directory information: Big lists of routers, and short
* routerstatus documents. */
- V1_AUTHORITY = 1 << 0,
+ V1_DIRINFO = 1 << 0,
/** Serves/signs v2 directory information: i.e. v2 networkstatus documents */
- V2_AUTHORITY = 1 << 1,
+ V2_DIRINFO = 1 << 1,
/** Serves/signs v3 directory information: votes, consensuses, certs */
- V3_AUTHORITY = 1 << 2,
+ V3_DIRINFO = 1 << 2,
/** Serves hidden service descriptors. */
- HIDSERV_AUTHORITY = 1 << 3,
+ HIDSERV_DIRINFO = 1 << 3,
/** Serves bridge descriptors. */
- BRIDGE_AUTHORITY = 1 << 4,
- /** Serves extrainfo documents. (XXX Not precisely an authority type)*/
- EXTRAINFO_CACHE = 1 << 5,
-} authority_type_t;
+ BRIDGE_DIRINFO = 1 << 4,
+ /** Serves extrainfo documents. */
+ EXTRAINFO_DIRINFO=1 << 5,
+ /** Serves microdescriptors. */
+ MICRODESC_DIRINFO=1 << 6,
+} dirinfo_type_t;
#define CRYPT_PATH_MAGIC 0x70127012u
@@ -2038,15 +2408,15 @@ typedef struct {
/** How to extend to the planned exit node. */
extend_info_t *chosen_exit;
/** Whether every node in the circ must have adequate uptime. */
- int need_uptime;
+ unsigned int need_uptime : 1;
/** Whether every node in the circ must have adequate capacity. */
- int need_capacity;
+ unsigned int need_capacity : 1;
/** Whether the last hop was picked with exiting in mind. */
- int is_internal;
- /** Did we pick this as a one-hop tunnel (not safe for other conns)?
- * These are for encrypted connections that exit to this router, not
+ unsigned int is_internal : 1;
+ /** Did we pick this as a one-hop tunnel (not safe for other streams)?
+ * These are for encrypted dir conns that exit to this router, not
* for arbitrary exits from the circuit. */
- int onehop_tunnel;
+ unsigned int onehop_tunnel : 1;
/** The crypt_path_t to append after rendezvous: used for rendezvous. */
crypt_path_t *pending_final_cpath;
/** How many times has building a circuit for this task failed? */
@@ -2144,7 +2514,10 @@ typedef struct circuit_t {
* length ONIONSKIN_CHALLENGE_LEN. */
char *n_conn_onionskin;
- struct timeval timestamp_created; /**< When was the circuit created? */
+ /** When was this circuit created? We keep this timestamp with a higher
+ * resolution than most so that the circuit-build-time tracking code can
+ * get millisecond resolution. */
+ struct timeval timestamp_created;
/** When the circuit was first used, or 0 if the circuit is clean.
*
* XXXX023 Note that some code will artifically adjust this value backward
@@ -2242,6 +2615,55 @@ typedef struct origin_circuit_t {
/* XXXX NM This can get re-used after 2**32 circuits. */
uint32_t global_identifier;
+ /** True if we have associated one stream to this circuit, thereby setting
+ * the isolation paramaters for this circuit. Note that this doesn't
+ * necessarily mean that we've <em>attached</em> any streams to the circuit:
+ * we may only have marked up this circuit during the launch process.
+ */
+ unsigned int isolation_values_set : 1;
+ /** True iff any stream has <em>ever</em> been attached to this circuit.
+ *
+ * In a better world we could use timestamp_dirty for this, but
+ * timestamp_dirty is far too overloaded at the moment.
+ */
+ unsigned int isolation_any_streams_attached : 1;
+
+ /** A bitfield of ISO_* flags for every isolation field such that this
+ * circuit has had streams with more than one value for that field
+ * attached to it. */
+ uint8_t isolation_flags_mixed;
+
+ /** @name Isolation parameters
+ *
+ * If any streams have been associated with this circ (isolation_values_set
+ * == 1), and all streams associated with the circuit have had the same
+ * value for some field ((isolation_flags_mixed & ISO_FOO) == 0), then these
+ * elements hold the value for that field.
+ *
+ * Note again that "associated" is not the same as "attached": we
+ * preliminarily associate streams with a circuit while the circuit is being
+ * launched, so that we can tell whether we need to launch more circuits.
+ *
+ * @{
+ */
+ uint8_t client_proto_type;
+ uint8_t client_proto_socksver;
+ uint16_t dest_port;
+ tor_addr_t client_addr;
+ char *dest_address;
+ int session_group;
+ unsigned nym_epoch;
+ size_t socks_username_len;
+ uint8_t socks_password_len;
+ /* Note that the next two values are NOT NUL-terminated; see
+ socks_username_len and socks_password_len for their lengths. */
+ char *socks_username;
+ char *socks_password;
+ /** Global identifier for the first stream attached here; used by
+ * ISO_STREAM. */
+ uint64_t associated_isolated_stream_global_id;
+ /**@}*/
+
} origin_circuit_t;
/** An or_circuit_t holds information needed to implement a circuit at an
@@ -2360,6 +2782,60 @@ typedef enum invalid_router_usage_t {
#define MIN_CONSTRAINED_TCP_BUFFER 2048
#define MAX_CONSTRAINED_TCP_BUFFER 262144 /* 256k */
+/** @name Isolation flags
+
+ Ways to isolate client streams
+
+ @{
+*/
+/** Isolate based on destination port */
+#define ISO_DESTPORT (1u<<0)
+/** Isolate based on destination address */
+#define ISO_DESTADDR (1u<<1)
+/** Isolate based on SOCKS authentication */
+#define ISO_SOCKSAUTH (1u<<2)
+/** Isolate based on client protocol choice */
+#define ISO_CLIENTPROTO (1u<<3)
+/** Isolate based on client address */
+#define ISO_CLIENTADDR (1u<<4)
+/** Isolate based on session group (always on). */
+#define ISO_SESSIONGRP (1u<<5)
+/** Isolate based on newnym epoch (always on). */
+#define ISO_NYM_EPOCH (1u<<6)
+/** Isolate all streams (Internal only). */
+#define ISO_STREAM (1u<<7)
+/**@}*/
+
+/** Default isolation level for ports. */
+#define ISO_DEFAULT (ISO_CLIENTADDR|ISO_SOCKSAUTH|ISO_SESSIONGRP|ISO_NYM_EPOCH)
+
+/** Indicates that we haven't yet set a session group on a port_cfg_t. */
+#define SESSION_GROUP_UNSET -1
+/** Session group reserved for directory connections */
+#define SESSION_GROUP_DIRCONN -2
+/** Session group reserved for resolve requests launched by a controller */
+#define SESSION_GROUP_CONTROL_RESOLVE -3
+/** First automatically allocated session group number */
+#define SESSION_GROUP_FIRST_AUTO -4
+
+/** Configuration for a single port that we're listening on. */
+typedef struct port_cfg_t {
+ tor_addr_t addr; /**< The actual IP to listen on, if !is_unix_addr. */
+ int port; /**< The configured port, or CFG_AUTO_PORT to tell Tor to pick its
+ * own port. */
+ uint8_t type; /**< One of CONN_TYPE_*_LISTENER */
+ unsigned is_unix_addr : 1; /**< True iff this is an AF_UNIX address. */
+
+ /* Client port types (socks, dns, trans, natd) only: */
+ uint8_t isolation_flags; /**< Zero or more isolation flags */
+ int session_group; /**< A session group, or -1 if this port is not in a
+ * session group. */
+
+ /* Unix sockets only: */
+ /** Path for an AF_UNIX address */
+ char unix_addr[FLEXIBLE_ARRAY_MEMBER];
+} port_cfg_t;
+
/** A linked list of lines in a config file. */
typedef struct config_line_t {
char *key;
@@ -2386,6 +2862,7 @@ typedef struct {
config_line_t *Logs; /**< New-style list of configuration lines
* for logs */
+ int LogTimeGranularity; /**< Log resolution in milliseconds. */
int LogMessageDomains; /**< Boolean: Should we log the domain(s) in which
* each log message occurs? */
@@ -2454,16 +2931,17 @@ typedef struct {
char *User; /**< Name of user to run Tor as. */
char *Group; /**< Name of group to run Tor as. */
int ORPort; /**< Port to listen on for OR connections. */
- int SocksPort; /**< Port to listen on for SOCKS connections. */
- /** Port to listen on for transparent pf/netfilter connections. */
- int TransPort;
- int NATDPort; /**< Port to listen on for transparent natd connections. */
+ config_line_t *SocksPort; /**< Ports to listen on for SOCKS connections. */
+ /** Ports to listen on for transparent pf/netfilter connections. */
+ config_line_t *TransPort;
+ config_line_t *NATDPort; /**< Ports to listen on for transparent natd
+ * connections. */
int ControlPort; /**< Port to listen on for control connections. */
config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
* for control connections. */
int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */
int DirPort; /**< Port to listen on for directory connections. */
- int DNSPort; /**< Port to listen on for DNS requests. */
+ config_line_t *DNSPort; /**< Port to listen on for DNS requests. */
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */
int V1AuthoritativeDir; /**< Boolean: is this an authoritative directory
@@ -2491,6 +2969,12 @@ typedef struct {
int UseBridges; /**< Boolean: should we start all circuits with a bridge? */
config_line_t *Bridges; /**< List of bootstrap bridge addresses. */
+ config_line_t *ClientTransportPlugin; /**< List of client
+ transport plugins. */
+
+ config_line_t *ServerTransportPlugin; /**< List of client
+ transport plugins. */
+
int BridgeRelay; /**< Boolean: are we acting as a bridge relay? We make
* this explicit so we can change how we behave in the
* future. */
@@ -2505,8 +2989,8 @@ typedef struct {
/** To what authority types do we publish our descriptor? Choices are
* "v1", "v2", "v3", "bridge", or "". */
smartlist_t *PublishServerDescriptor;
- /** An authority type, derived from PublishServerDescriptor. */
- authority_type_t _PublishServerDescriptor;
+ /** A bitfield of authority types, derived from PublishServerDescriptor. */
+ dirinfo_type_t _PublishServerDescriptor;
/** Boolean: do we publish hidden service descriptors to the HS auths? */
int PublishHidServDescriptors;
int FetchServerDescriptors; /**< Do we fetch server descriptors as normal? */
@@ -2538,12 +3022,10 @@ typedef struct {
uint64_t ConstrainedSockSize; /**< Size of constrained buffers. */
/** Whether we should drop exit streams from Tors that we don't know are
- * relays. One of "0" (never refuse), "1" (always refuse), or "auto" (do
+ * relays. One of "0" (never refuse), "1" (always refuse), or "-1" (do
* what the consensus says, defaulting to 'refuse' if the consensus says
* nothing). */
- char *RefuseUnknownExits;
- /** Parsed version of RefuseUnknownExits. -1 for auto. */
- int RefuseUnknownExits_;
+ int RefuseUnknownExits;
/** Application ports that require all nodes in circ to have sufficient
* uptime. */
@@ -2614,6 +3096,9 @@ typedef struct {
* authorizations for hidden services */
char *ContactInfo; /**< Contact info to be published in the directory. */
+ int HeartbeatPeriod; /**< Log heartbeat messages after this many seconds
+ * have passed. */
+
char *HTTPProxy; /**< hostname[:port] to use as http proxy, if any. */
tor_addr_t HTTPProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */
uint16_t HTTPProxyPort; /**< Parsed port for http proxy, if any. */
@@ -2651,7 +3136,8 @@ typedef struct {
char *MyFamily; /**< Declared family for this OR. */
config_line_t *NodeFamilies; /**< List of config lines for
- * node families */
+ * node families */
+ smartlist_t *NodeFamilySets; /**< List of parsed NodeFamilies values. */
config_line_t *AuthDirBadDir; /**< Address policy for descriptors to
* mark as bad dir mirrors. */
config_line_t *AuthDirBadExit; /**< Address policy for descriptors to
@@ -2721,6 +3207,8 @@ typedef struct {
* log whether it was DNS-leaking or not? */
int HardwareAccel; /**< Boolean: Should we enable OpenSSL hardware
* acceleration where available? */
+ /** Token Bucket Refill resolution in milliseconds. */
+ int TokenBucketRefillInterval;
char *AccelName; /**< Optional hardware acceleration engine name. */
char *AccelDir; /**< Optional hardware acceleration engine search dir. */
int UseEntryGuards; /**< Boolean: Do we try to enter from a smallish number
@@ -2755,7 +3243,9 @@ typedef struct {
/** Boolean: if set, we start even if our resolv.conf file is missing
* or broken. */
int ServerDNSAllowBrokenConfig;
-
+ /** Boolean: if set, then even connections to private addresses will get
+ * rate-limited. */
+ int CountPrivateBandwidth;
smartlist_t *ServerDNSTestAddresses; /**< A list of addresses that definitely
* should be resolvable. Used for
* testing our DNS server. */
@@ -2765,6 +3255,10 @@ typedef struct {
* possible. */
int PreferTunneledDirConns; /**< If true, avoid dirservers that don't
* support BEGIN_DIR, when possible. */
+ int PortForwarding; /**< If true, use NAT-PMP or UPnP to automatically
+ * forward the DirPort and ORPort on the NAT device */
+ char *PortForwardingHelper; /** < Filename or full path of the port
+ forwarding helper executable */
int AllowNonRFC953Hostnames; /**< If true, we allow connections to hostnames
* with weird characters. */
/** If true, we try resolving hostnames with weird characters. */
@@ -2802,6 +3296,9 @@ typedef struct {
/** If true, the user wants us to collect statistics on port usage. */
int ExitPortStatistics;
+ /** If true, the user wants us to collect connection statistics. */
+ int ConnDirectionStatistics;
+
/** If true, the user wants us to collect cell statistics. */
int CellStatistics;
@@ -2898,16 +3395,35 @@ typedef struct {
*/
double CircuitPriorityHalflife;
+ /** If true, do not enable IOCP on windows with bufferevents, even if
+ * we think we could. */
+ int DisableIOCP;
+ /** For testing only: will go away in 0.2.3.x. */
+ int _UseFilteringSSLBufferevents;
+
/** Set to true if the TestingTorNetwork configuration option is set.
* This is used so that options_validate() has a chance to realize that
* the defaults have changed. */
int _UsingTestNetworkDefaults;
+ /** If 1, we try to use microdescriptors to build circuits. If 0, we don't.
+ * If -1, Tor decides. */
+ int UseMicrodescriptors;
+
/** File where we should write the ControlPort. */
char *ControlPortWriteToFile;
/** Should that file be group-readable? */
int ControlPortFileGroupReadable;
+#define MAX_MAX_CLIENT_CIRCUITS_PENDING 1024
+ /** Maximum number of non-open general-purpose origin circuits to allow at
+ * once. */
+ int MaxClientCircuitsPending;
+
+ /** If 1, we always send optimistic data when it's supported. If 0, we
+ * never use it. If -1, we do what the consensus says. */
+ int OptimisticData;
+
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
@@ -2934,6 +3450,8 @@ typedef struct {
/** A list of Entry Guard-related configuration lines. */
config_line_t *EntryGuards;
+ config_line_t *TransportProxies;
+
/** These fields hold information on the history of bandwidth usage for
* servers. The "Ends" fields hold the time when we last updated the
* bandwidth usage. The "Interval" fields hold the granularity, in seconds,
@@ -2986,6 +3504,8 @@ static INLINE void or_state_mark_dirty(or_state_t *state, time_t when)
#define MAX_SOCKS_REPLY_LEN 1024
#define MAX_SOCKS_ADDR_LEN 256
+#define SOCKS_NO_AUTH 0x00
+#define SOCKS_USER_PASS 0x02
/** Please open a TCP connection to this addr:port. */
#define SOCKS_COMMAND_CONNECT 0x01
@@ -3005,10 +3525,17 @@ struct socks_request_t {
/** Which version of SOCKS did the client use? One of "0, 4, 5" -- where
* 0 means that no socks handshake ever took place, and this is just a
* stub connection (e.g. see connection_ap_make_link()). */
- char socks_version;
- int command; /**< What is this stream's goal? One from the above list. */
+ uint8_t socks_version;
+ /** If using socks5 authentication, which authentication type did we
+ * negotiate? currently we support 0 (no authentication) and 2
+ * (username/password). */
+ uint8_t auth_type;
+ /** What is this stream's goal? One of the SOCKS_COMMAND_* values */
+ uint8_t command;
+ /** Which kind of listener created this stream? */
+ uint8_t listener_type;
size_t replylen; /**< Length of <b>reply</b>. */
- char reply[MAX_SOCKS_REPLY_LEN]; /**< Write an entry into this string if
+ uint8_t reply[MAX_SOCKS_REPLY_LEN]; /**< Write an entry into this string if
* we want to specify our own socks reply,
* rather than using the default socks4 or
* socks5 socks reply. We use this for the
@@ -3020,10 +3547,21 @@ struct socks_request_t {
unsigned int has_finished : 1; /**< Has the SOCKS handshake finished? Used to
* make sure we send back a socks reply for
* every connection. */
+ unsigned int got_auth : 1; /**< Have we received any authentication data? */
+
+ /** Number of bytes in username; 0 if username is NULL */
+ size_t usernamelen;
+ /** Number of bytes in password; 0 if password is NULL */
+ uint8_t passwordlen;
+ /** The negotiated username value if any (for socks5), or the entire
+ * authentication string (for socks4). This value is NOT nul-terminated;
+ * see usernamelen for its length. */
+ char *username;
+ /** The negotiated password value if any (for socks5). This value is NOT
+ * nul-terminated; see passwordlen for its length. */
+ char *password;
};
-/* all the function prototypes go here */
-
/********************************* circuitbuild.c **********************/
/** How many hops does a general-purpose circuit have by default? */
@@ -3436,7 +3974,7 @@ typedef enum {
ADDR_POLICY_PROBABLY_ACCEPTED=1,
/** Part of the address was unknown, but as far as we can tell, it was
* rejected. */
- ADDR_POLICY_PROBABLY_REJECTED=2
+ ADDR_POLICY_PROBABLY_REJECTED=2,
} addr_policy_result_t;
/********************************* rephist.c ***************************/
@@ -3466,6 +4004,11 @@ typedef struct rend_encoded_v2_service_descriptor_t {
char *desc_str; /**< Descriptor string. */
} rend_encoded_v2_service_descriptor_t;
+/** The maximum number of non-circuit-build-timeout failures a hidden
+ * service client will tolerate while trying to build a circuit to an
+ * introduction point. See also rend_intro_point_t.unreachable_count. */
+#define MAX_INTRO_POINT_REACHABILITY_FAILURES 5
+
/** Introduction point information. Used both in rend_service_t (on
* the service side) and in rend_service_descriptor_t (on both the
* client and service side). */
@@ -3473,6 +4016,18 @@ typedef struct rend_intro_point_t {
extend_info_t *extend_info; /**< Extend info of this introduction point. */
crypto_pk_env_t *intro_key; /**< Introduction key that replaces the service
* key, if this descriptor is V2. */
+
+ /** (Client side only) Flag indicating that a timeout has occurred
+ * after sending an INTRODUCE cell to this intro point. After a
+ * timeout, an intro point should not be tried again during the same
+ * hidden service connection attempt, but it may be tried again
+ * during a future connection attempt. */
+ unsigned int timed_out : 1;
+
+ /** (Client side only) The number of times we have failed to build a
+ * circuit to this intro point for some reason other than our
+ * circuit-build timeout. See also MAX_INTRO_POINT_REACHABILITY_FAILURES. */
+ unsigned int unreachable_count : 3;
} rend_intro_point_t;
/** Information used to connect to a hidden service. Used on both the
@@ -3524,7 +4079,7 @@ typedef struct trusted_dir_server_t {
unsigned int has_accepted_serverdesc:1;
/** What kind of authority is this? (Bitfield.) */
- authority_type_t type;
+ dirinfo_type_t type;
download_status_t v2_ns_dl_status; /**< Status of downloading this server's
* v2 network status. */
@@ -3570,6 +4125,8 @@ typedef struct trusted_dir_server_t {
* fetches to _any_ single directory server.]
*/
#define PDS_NO_EXISTING_SERVERDESC_FETCH (1<<3)
+#define PDS_NO_EXISTING_MICRODESC_FETCH (1<<4)
+
#define _PDS_PREFER_TUNNELED_DIR_CONNS (1<<16)
/** Possible ways to weight routers when choosing one randomly. See
@@ -3587,7 +4144,8 @@ typedef enum {
CRN_NEED_GUARD = 1<<2,
CRN_ALLOW_INVALID = 1<<3,
/* XXXX not used, apparently. */
- CRN_WEIGHT_AS_EXIT = 1<<5
+ CRN_WEIGHT_AS_EXIT = 1<<5,
+ CRN_NEED_DESC = 1<<6
} router_crn_flags_t;
/** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */
diff --git a/src/or/policies.c b/src/or/policies.c
index c87036013..40e527747 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -11,6 +11,7 @@
#include "or.h"
#include "config.h"
#include "dirserv.h"
+#include "nodelist.h"
#include "policies.h"
#include "routerparse.h"
#include "ht.h"
@@ -82,15 +83,15 @@ policy_expand_private(smartlist_t **policy)
continue;
}
for (i = 0; private_nets[i]; ++i) {
- addr_policy_t policy;
- memcpy(&policy, p, sizeof(addr_policy_t));
- policy.is_private = 0;
- policy.is_canonical = 0;
- if (tor_addr_parse_mask_ports(private_nets[i], &policy.addr,
- &policy.maskbits, &port_min, &port_max)<0) {
+ addr_policy_t newpolicy;
+ memcpy(&newpolicy, p, sizeof(addr_policy_t));
+ newpolicy.is_private = 0;
+ newpolicy.is_canonical = 0;
+ if (tor_addr_parse_mask_ports(private_nets[i], &newpolicy.addr,
+ &newpolicy.maskbits, &port_min, &port_max)<0) {
tor_assert(0);
}
- smartlist_add(tmp, addr_policy_get_canonical_entry(&policy));
+ smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy));
}
addr_policy_free(p);
});
@@ -163,7 +164,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
static int
parse_reachable_addresses(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int ret = 0;
if (options->ReachableDirAddresses &&
@@ -261,7 +262,7 @@ fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port)
/** Return true iff we think our firewall will let us make an OR connection to
* <b>ri</b>. */
int
-fascist_firewall_allows_or(routerinfo_t *ri)
+fascist_firewall_allows_or(const routerinfo_t *ri)
{
/* XXXX proposal 118 */
tor_addr_t addr;
@@ -269,6 +270,22 @@ fascist_firewall_allows_or(routerinfo_t *ri)
return fascist_firewall_allows_address_or(&addr, ri->or_port);
}
+/** Return true iff we think our firewall will let us make an OR connection to
+ * <b>node</b>. */
+int
+fascist_firewall_allows_node(const node_t *node)
+{
+ if (node->ri) {
+ return fascist_firewall_allows_or(node->ri);
+ } else if (node->rs) {
+ tor_addr_t addr;
+ tor_addr_from_ipv4h(&addr, node->rs->addr);
+ return fascist_firewall_allows_address_or(&addr, node->rs->or_port);
+ } else {
+ return 1;
+ }
+}
+
/** Return true iff we think our firewall will let us make a directory
* connection to addr:port. */
int
@@ -339,7 +356,7 @@ authdir_policy_badexit_address(uint32_t addr, uint16_t port)
* options in <b>options</b>, return -1 and set <b>msg</b> to a newly
* allocated description of the error. Else return 0. */
int
-validate_addr_policies(or_options_t *options, char **msg)
+validate_addr_policies(const or_options_t *options, char **msg)
{
/* XXXX Maybe merge this into parse_policies_from_options, to make sure
* that the two can't go out of sync. */
@@ -423,7 +440,7 @@ load_policy_from_option(config_line_t *config, smartlist_t **policy,
/** Set all policies based on <b>options</b>, which should have been validated
* first by validate_addr_policies. */
int
-policies_parse_from_options(or_options_t *options)
+policies_parse_from_options(const or_options_t *options)
{
int ret = 0;
if (load_policy_from_option(options->SocksPolicy, &socks_policy, -1) < 0)
@@ -553,18 +570,6 @@ addr_policy_get_canonical_entry(addr_policy_t *e)
return found->policy;
}
-/** As compare_tor_addr_to_addr_policy, but instead of a tor_addr_t, takes
- * in host order. */
-addr_policy_result_t
-compare_addr_to_addr_policy(uint32_t addr, uint16_t port,
- const smartlist_t *policy)
-{
- /*XXXX deprecate this function when possible. */
- tor_addr_t a;
- tor_addr_from_ipv4h(&a, addr);
- return compare_tor_addr_to_addr_policy(&a, port, policy);
-}
-
/** Helper for compare_tor_addr_to_addr_policy. Implements the case where
* addr and port are both known. */
static addr_policy_result_t
@@ -684,7 +689,7 @@ compare_tor_addr_to_addr_policy(const tor_addr_t *addr, uint16_t port,
if (!policy) {
/* no policy? accept all. */
return ADDR_POLICY_ACCEPTED;
- } else if (tor_addr_is_null(addr)) {
+ } else if (addr == NULL || tor_addr_is_null(addr)) {
tor_assert(port != 0);
return compare_unknown_tor_addr_to_addr_policy(port, policy);
} else if (port == 0) {
@@ -866,15 +871,11 @@ policies_exit_policy_append_reject_star(smartlist_t **dest)
append_exit_policy_string(dest, "reject *:*");
}
-/** Replace the exit policy of <b>r</b> with reject *:*. */
+/** Replace the exit policy of <b>node</b> with reject *:* */
void
-policies_set_router_exitpolicy_to_reject_all(routerinfo_t *r)
+policies_set_node_exitpolicy_to_reject_all(node_t *node)
{
- addr_policy_t *item;
- addr_policy_list_free(r->exit_policy);
- r->exit_policy = smartlist_create();
- item = router_parse_addr_policy_item_from_string("reject *:*", -1);
- smartlist_add(r->exit_policy, item);
+ node->rejects_all = 1;
}
/** Return 1 if there is at least one /8 subnet in <b>policy</b> that
@@ -1085,7 +1086,7 @@ policy_summary_split(smartlist_t *summary,
int start_at_index;
int i = 0;
- /* XXXX Do a binary search if run time matters */
+
while (AT(i)->prt_max < prt_min)
i++;
if (AT(i)->prt_min != prt_min) {
@@ -1298,6 +1299,186 @@ policy_summarize(smartlist_t *policy)
return result;
}
+/** Convert a summarized policy string into a short_policy_t. Return NULL
+ * if the string is not well-formed. */
+short_policy_t *
+parse_short_policy(const char *summary)
+{
+ const char *orig_summary = summary;
+ short_policy_t *result;
+ int is_accept;
+ int n_entries;
+ short_policy_entry_t entries[MAX_EXITPOLICY_SUMMARY_LEN]; /* overkill */
+ const char *next;
+
+ if (!strcmpstart(summary, "accept ")) {
+ is_accept = 1;
+ summary += strlen("accept ");
+ } else if (!strcmpstart(summary, "reject ")) {
+ is_accept = 0;
+ summary += strlen("reject ");
+ } else {
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Unrecognized policy summary keyword");
+ return NULL;
+ }
+
+ n_entries = 0;
+ for ( ; *summary; summary = next) {
+ const char *comma = strchr(summary, ',');
+ unsigned low, high;
+ char dummy;
+ char ent_buf[32];
+
+ next = comma ? comma+1 : strchr(summary, '\0');
+
+ if (n_entries == MAX_EXITPOLICY_SUMMARY_LEN) {
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Impossibly long policy summary %s",
+ escaped(orig_summary));
+ return NULL;
+ }
+
+ if (! TOR_ISDIGIT(*summary) || next-summary > (int)(sizeof(ent_buf)-1)) {
+ /* unrecognized entry format. skip it. */
+ continue;
+ }
+ if (next-summary < 2) {
+ /* empty; skip it. */
+ continue;
+ }
+
+ memcpy(ent_buf, summary, next-summary-1);
+ ent_buf[next-summary-1] = '\0';
+
+ if (tor_sscanf(ent_buf, "%u-%u%c", &low, &high, &dummy) == 2) {
+ if (low<1 || low>65535 || high<1 || high>65535) {
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR,
+ "Found bad entry in policy summary %s", escaped(orig_summary));
+ return NULL;
+ }
+ } else if (tor_sscanf(ent_buf, "%u%c", &low, &dummy) == 1) {
+ if (low<1 || low>65535) {
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR,
+ "Found bad entry in policy summary %s", escaped(orig_summary));
+ return NULL;
+ }
+ high = low;
+ } else {
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR,"Found bad entry in policy summary %s",
+ escaped(orig_summary));
+ return NULL;
+ }
+
+ entries[n_entries].min_port = low;
+ entries[n_entries].max_port = high;
+ n_entries++;
+ }
+
+ if (n_entries == 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR,
+ "Found no port-range entries in summary %s", escaped(orig_summary));
+ return NULL;
+ }
+
+ {
+ size_t size = STRUCT_OFFSET(short_policy_t, entries) +
+ sizeof(short_policy_entry_t)*(n_entries);
+ result = tor_malloc_zero(size);
+
+ tor_assert( (char*)&result->entries[n_entries-1] < ((char*)result)+size);
+ }
+
+ result->is_accept = is_accept;
+ result->n_entries = n_entries;
+ memcpy(result->entries, entries, sizeof(short_policy_entry_t)*n_entries);
+ return result;
+}
+
+/** Release all storage held in <b>policy</b>. */
+void
+short_policy_free(short_policy_t *policy)
+{
+ tor_free(policy);
+}
+
+/** See whether the <b>addr</b>:<b>port</b> address is likely to be accepted
+ * or rejected by the summarized policy <b>policy</b>. Return values are as
+ * for compare_tor_addr_to_addr_policy. Unlike the regular addr_policy
+ * functions, requires the <b>port</b> be specified. */
+addr_policy_result_t
+compare_tor_addr_to_short_policy(const tor_addr_t *addr, uint16_t port,
+ const short_policy_t *policy)
+{
+ int i;
+ int found_match = 0;
+ int accept;
+ (void)addr;
+
+ tor_assert(port != 0);
+
+ if (addr && tor_addr_is_null(addr))
+ addr = NULL; /* Unspec means 'no address at all,' in this context. */
+
+ if (addr && (tor_addr_is_internal(addr, 0) ||
+ tor_addr_is_loopback(addr)))
+ return ADDR_POLICY_REJECTED;
+
+ for (i=0; i < policy->n_entries; ++i) {
+ const short_policy_entry_t *e = &policy->entries[i];
+ if (e->min_port <= port && port <= e->max_port) {
+ found_match = 1;
+ break;
+ }
+ }
+
+ if (found_match)
+ accept = policy->is_accept;
+ else
+ accept = ! policy->is_accept;
+
+ /* ???? are these right? */
+ if (accept)
+ return ADDR_POLICY_PROBABLY_ACCEPTED;
+ else
+ return ADDR_POLICY_REJECTED;
+}
+
+/** Return true iff <b>policy</b> seems reject all ports */
+int
+short_policy_is_reject_star(const short_policy_t *policy)
+{
+ /* This doesn't need to be as much on the lookout as policy_is_reject_star,
+ * since policy summaries are from the consensus or from consensus
+ * microdescs.
+ */
+ tor_assert(policy);
+ /* Check for an exact match of "reject 1-65535". */
+ return (policy->is_accept == 0 && policy->n_entries == 1 &&
+ policy->entries[0].min_port == 1 &&
+ policy->entries[0].max_port == 65535);
+}
+
+/** Decides whether addr:port is probably or definitely accepted or rejcted by
+ * <b>node</b>. See compare_tor_addr_to_addr_policy for details on addr/port
+ * interpretation. */
+addr_policy_result_t
+compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port,
+ const node_t *node)
+{
+ if (node->rejects_all)
+ return ADDR_POLICY_REJECTED;
+
+ if (node->ri)
+ return compare_tor_addr_to_addr_policy(addr, port, node->ri->exit_policy);
+ else if (node->md) {
+ if (node->md->exit_policy == NULL)
+ return ADDR_POLICY_REJECTED;
+ else
+ return compare_tor_addr_to_short_policy(addr, port,
+ node->md->exit_policy);
+ } else
+ return ADDR_POLICY_PROBABLY_REJECTED;
+}
+
/** Implementation for GETINFO control command: knows the answer for questions
* about "exit-policy/..." */
int
diff --git a/src/or/policies.h b/src/or/policies.h
index b2947c67e..51716ab0a 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -19,7 +19,8 @@
int firewall_is_fascist_or(void);
int fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port);
-int fascist_firewall_allows_or(routerinfo_t *ri);
+int fascist_firewall_allows_or(const routerinfo_t *ri);
+int fascist_firewall_allows_node(const node_t *node);
int fascist_firewall_allows_address_dir(const tor_addr_t *addr, uint16_t port);
int dir_policy_permits_address(const tor_addr_t *addr);
int socks_policy_permits_address(const tor_addr_t *addr);
@@ -28,21 +29,23 @@ int authdir_policy_valid_address(uint32_t addr, uint16_t port);
int authdir_policy_baddir_address(uint32_t addr, uint16_t port);
int authdir_policy_badexit_address(uint32_t addr, uint16_t port);
-int validate_addr_policies(or_options_t *options, char **msg);
+int validate_addr_policies(const or_options_t *options, char **msg);
void policy_expand_private(smartlist_t **policy);
-int policies_parse_from_options(or_options_t *options);
+int policies_parse_from_options(const or_options_t *options);
addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent);
int cmp_addr_policies(smartlist_t *a, smartlist_t *b);
addr_policy_result_t compare_tor_addr_to_addr_policy(const tor_addr_t *addr,
uint16_t port, const smartlist_t *policy);
-addr_policy_result_t compare_addr_to_addr_policy(uint32_t addr,
- uint16_t port, const smartlist_t *policy);
+
+addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
+ uint16_t port, const node_t *node);
+
int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int rejectprivate, const char *local_address,
int add_default_policy);
void policies_exit_policy_append_reject_star(smartlist_t **dest);
-void policies_set_router_exitpolicy_to_reject_all(routerinfo_t *exitrouter);
+void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter);
int exit_policy_is_general_exit(smartlist_t *policy);
int policy_is_reject_star(const smartlist_t *policy);
int getinfo_helper_policies(control_connection_t *conn,
@@ -57,5 +60,12 @@ void policies_free_all(void);
char *policy_summarize(smartlist_t *policy);
+short_policy_t *parse_short_policy(const char *summary);
+void short_policy_free(short_policy_t *policy);
+int short_policy_is_reject_star(const short_policy_t *policy);
+addr_policy_result_t compare_tor_addr_to_short_policy(
+ const tor_addr_t *addr, uint16_t port,
+ const short_policy_t *policy);
+
#endif
diff --git a/src/or/relay.c b/src/or/relay.c
index d9b9d0c48..51a29a20e 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -24,6 +24,7 @@
#include "main.h"
#include "mempool.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
#include "relay.h"
@@ -648,6 +649,7 @@ connection_edge_send_command(edge_connection_t *fromconn,
{
/* XXXX NM Split this function into a separate versions per circuit type? */
circuit_t *circ;
+ crypt_path_t *cpath_layer = fromconn->cpath_layer;
tor_assert(fromconn);
circ = fromconn->on_circuit;
@@ -662,7 +664,8 @@ connection_edge_send_command(edge_connection_t *fromconn,
if (!circ) {
if (fromconn->_base.type == CONN_TYPE_AP) {
log_info(LD_APP,"no circ. Closing conn.");
- connection_mark_unattached_ap(fromconn, END_STREAM_REASON_INTERNAL);
+ connection_mark_unattached_ap(EDGE_TO_ENTRY_CONN(fromconn),
+ END_STREAM_REASON_INTERNAL);
} else {
log_info(LD_EXIT,"no circ. Closing conn.");
fromconn->edge_has_sent_end = 1; /* no circ to send to */
@@ -674,7 +677,7 @@ connection_edge_send_command(edge_connection_t *fromconn,
return relay_send_command_from_edge(fromconn->stream_id, circ,
relay_command, payload,
- payload_len, fromconn->cpath_layer);
+ payload_len, cpath_layer);
}
/** How many times will I retry a stream that fails due to DNS
@@ -702,22 +705,24 @@ edge_reason_is_retriable(int reason)
static int
connection_ap_process_end_not_open(
relay_header_t *rh, cell_t *cell, origin_circuit_t *circ,
- edge_connection_t *conn, crypt_path_t *layer_hint)
+ entry_connection_t *conn, crypt_path_t *layer_hint)
{
struct in_addr in;
- routerinfo_t *exitrouter;
+ node_t *exitrouter;
int reason = *(cell->payload+RELAY_HEADER_SIZE);
int control_reason = reason | END_STREAM_REASON_FLAG_REMOTE;
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
(void) layer_hint; /* unused */
if (rh->length > 0 && edge_reason_is_retriable(reason) &&
- !connection_edge_is_rendezvous_stream(conn) /* avoid retry if rend */
- ) {
+ /* avoid retry if rend */
+ !connection_edge_is_rendezvous_stream(edge_conn)) {
+ const char *chosen_exit_digest =
+ circ->build_state->chosen_exit->identity_digest;
log_info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
safe_str(conn->socks_request->address),
stream_end_reason_to_string(reason));
- exitrouter =
- router_get_by_digest(circ->build_state->chosen_exit->identity_digest);
+ exitrouter = node_get_mutable_by_id(chosen_exit_digest);
switch (reason) {
case END_STREAM_REASON_EXITPOLICY:
if (rh->length >= 5) {
@@ -752,8 +757,8 @@ connection_ap_process_end_not_open(
log_info(LD_APP,
"Exitrouter %s seems to be more restrictive than its exit "
"policy. Not using this router as exit for now.",
- router_describe(exitrouter));
- policies_set_router_exitpolicy_to_reject_all(exitrouter);
+ node_describe(exitrouter));
+ policies_set_node_exitpolicy_to_reject_all(exitrouter);
}
/* rewrite it to an IP if we learned one. */
if (addressmap_rewrite(conn->socks_request->address,
@@ -818,7 +823,7 @@ connection_ap_process_end_not_open(
case END_STREAM_REASON_HIBERNATING:
case END_STREAM_REASON_RESOURCELIMIT:
if (exitrouter) {
- policies_set_router_exitpolicy_to_reject_all(exitrouter);
+ policies_set_node_exitpolicy_to_reject_all(exitrouter);
}
if (conn->chosen_exit_optional) {
/* stop wanting a specific exit */
@@ -838,7 +843,7 @@ connection_ap_process_end_not_open(
stream_end_reason_to_string(rh->length > 0 ? reason : -1));
circuit_log_path(LOG_INFO,LD_APP,circ);
/* need to test because of detach_retriable */
- if (!conn->_base.marked_for_close)
+ if (!ENTRY_TO_CONN(conn)->marked_for_close)
connection_mark_unattached_ap(conn, control_reason);
return 0;
}
@@ -847,7 +852,7 @@ connection_ap_process_end_not_open(
* dotted-quad representation of <b>new_addr</b> (given in host order),
* and send an appropriate REMAP event. */
static void
-remap_event_helper(edge_connection_t *conn, uint32_t new_addr)
+remap_event_helper(entry_connection_t *conn, uint32_t new_addr)
{
struct in_addr in;
@@ -873,7 +878,8 @@ connection_edge_process_relay_cell_not_open(
if (rh->command == RELAY_COMMAND_END) {
if (CIRCUIT_IS_ORIGIN(circ) && conn->_base.type == CONN_TYPE_AP) {
return connection_ap_process_end_not_open(rh, cell,
- TO_ORIGIN_CIRCUIT(circ), conn,
+ TO_ORIGIN_CIRCUIT(circ),
+ EDGE_TO_ENTRY_CONN(conn),
layer_hint);
} else {
/* we just got an 'end', don't need to send one */
@@ -887,6 +893,7 @@ connection_edge_process_relay_cell_not_open(
if (conn->_base.type == CONN_TYPE_AP &&
rh->command == RELAY_COMMAND_CONNECTED) {
+ entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
tor_assert(CIRCUIT_IS_ORIGIN(circ));
if (conn->_base.state != AP_CONN_STATE_CONNECT_WAIT) {
log_fn(LOG_PROTOCOL_WARN, LD_APP,
@@ -901,29 +908,26 @@ connection_edge_process_relay_cell_not_open(
int ttl;
if (!addr || (get_options()->ClientDNSRejectInternalAddresses &&
is_internal_IP(addr, 0))) {
- char buf[INET_NTOA_BUF_LEN];
- struct in_addr a;
- a.s_addr = htonl(addr);
- tor_inet_ntoa(&a, buf, sizeof(buf));
- log_info(LD_APP,
- "...but it claims the IP address was %s. Closing.", buf);
+ log_info(LD_APP, "...but it claims the IP address was %s. Closing.",
+ fmt_addr32(addr));
connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ connection_mark_unattached_ap(entry_conn,
+ END_STREAM_REASON_TORPROTOCOL);
return 0;
}
if (rh->length >= 8)
ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+4));
else
ttl = -1;
- client_dns_set_addressmap(conn->socks_request->address, addr,
- conn->chosen_exit_name, ttl);
+ client_dns_set_addressmap(entry_conn->socks_request->address, addr,
+ entry_conn->chosen_exit_name, ttl);
- remap_event_helper(conn, addr);
+ remap_event_helper(entry_conn, addr);
}
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
/* don't send a socks reply to transparent conns */
- if (!conn->socks_request->has_finished)
- connection_ap_handshake_socks_reply(conn, NULL, 0, 0);
+ if (!entry_conn->socks_request->has_finished)
+ connection_ap_handshake_socks_reply(entry_conn, NULL, 0, 0);
/* Was it a linked dir conn? If so, a dir request just started to
* fetch something; this could be a bootstrap status milestone. */
@@ -946,6 +950,12 @@ connection_edge_process_relay_cell_not_open(
break;
}
}
+ /* This is definitely a success, so forget about any pending data we
+ * had sent. */
+ if (entry_conn->pending_optimistic_data) {
+ generic_buffer_free(entry_conn->pending_optimistic_data);
+ entry_conn->pending_optimistic_data = NULL;
+ }
/* handle anything that might have queued */
if (connection_edge_package_raw_inbuf(conn, 1, NULL) < 0) {
@@ -960,17 +970,18 @@ connection_edge_process_relay_cell_not_open(
int ttl;
int answer_len;
uint8_t answer_type;
+ entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
if (conn->_base.state != AP_CONN_STATE_RESOLVE_WAIT) {
log_fn(LOG_PROTOCOL_WARN, LD_APP, "Got a 'resolved' cell while "
"not in state resolve_wait. Dropping.");
return 0;
}
- tor_assert(SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command));
+ tor_assert(SOCKS_COMMAND_IS_RESOLVE(entry_conn->socks_request->command));
answer_len = cell->payload[RELAY_HEADER_SIZE+1];
if (rh->length < 2 || answer_len+2>rh->length) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Dropping malformed 'resolved' cell");
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TORPROTOCOL);
return 0;
}
answer_type = cell->payload[RELAY_HEADER_SIZE];
@@ -983,19 +994,17 @@ connection_edge_process_relay_cell_not_open(
uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
if (get_options()->ClientDNSRejectInternalAddresses &&
is_internal_IP(addr, 0)) {
- char buf[INET_NTOA_BUF_LEN];
- struct in_addr a;
- a.s_addr = htonl(addr);
- tor_inet_ntoa(&a, buf, sizeof(buf));
- log_info(LD_APP,"Got a resolve with answer %s. Rejecting.", buf);
- connection_ap_handshake_socks_resolved(conn,
+ log_info(LD_APP,"Got a resolve with answer %s. Rejecting.",
+ fmt_addr32(addr));
+ connection_ap_handshake_socks_resolved(entry_conn,
RESOLVED_TYPE_ERROR_TRANSIENT,
0, NULL, 0, TIME_MAX);
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ connection_mark_unattached_ap(entry_conn,
+ END_STREAM_REASON_TORPROTOCOL);
return 0;
}
}
- connection_ap_handshake_socks_resolved(conn,
+ connection_ap_handshake_socks_resolved(entry_conn,
answer_type,
cell->payload[RELAY_HEADER_SIZE+1], /*answer_len*/
cell->payload+RELAY_HEADER_SIZE+2, /*answer*/
@@ -1003,9 +1012,9 @@ connection_edge_process_relay_cell_not_open(
-1);
if (answer_type == RESOLVED_TYPE_IPV4 && answer_len == 4) {
uint32_t addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+2));
- remap_event_helper(conn, addr);
+ remap_event_helper(entry_conn, addr);
}
- connection_mark_unattached_ap(conn,
+ connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_DONE |
END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
return 0;
@@ -1039,6 +1048,9 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
relay_header_t rh;
unsigned domain = layer_hint?LD_APP:LD_EXIT;
int reason;
+ int optimistic_data = 0; /* Set to 1 if we receive data on a stream
+ * that's in the EXIT_CONN_STATE_RESOLVING
+ * or EXIT_CONN_STATE_CONNECTING states. */
tor_assert(cell);
tor_assert(circ);
@@ -1058,9 +1070,20 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
/* either conn is NULL, in which case we've got a control cell, or else
* conn points to the recognized stream. */
- if (conn && !connection_state_is_open(TO_CONN(conn)))
- return connection_edge_process_relay_cell_not_open(
- &rh, cell, circ, conn, layer_hint);
+ if (conn && !connection_state_is_open(TO_CONN(conn))) {
+ if (conn->_base.type == CONN_TYPE_EXIT &&
+ (conn->_base.state == EXIT_CONN_STATE_CONNECTING ||
+ conn->_base.state == EXIT_CONN_STATE_RESOLVING) &&
+ rh.command == RELAY_COMMAND_DATA) {
+ /* Allow DATA cells to be delivered to an exit node in state
+ * EXIT_CONN_STATE_CONNECTING or EXIT_CONN_STATE_RESOLVING.
+ * This speeds up HTTP, for example. */
+ optimistic_data = 1;
+ } else {
+ return connection_edge_process_relay_cell_not_open(
+ &rh, cell, circ, conn, layer_hint);
+ }
+ }
switch (rh.command) {
case RELAY_COMMAND_DROP:
@@ -1127,7 +1150,14 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
stats_n_data_bytes_received += rh.length;
connection_write_to_buf((char*)(cell->payload + RELAY_HEADER_SIZE),
rh.length, TO_CONN(conn));
- connection_edge_consider_sending_sendme(conn);
+
+ if (!optimistic_data) {
+ /* Only send a SENDME if we're not getting optimistic data; otherwise
+ * a SENDME could arrive before the CONNECTED.
+ */
+ connection_edge_consider_sending_sendme(conn);
+ }
+
return 0;
case RELAY_COMMAND_END:
reason = rh.length > 0 ?
@@ -1142,9 +1172,13 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
conn->_base.s,
stream_end_reason_to_string(reason),
conn->stream_id);
- if (conn->socks_request && !conn->socks_request->has_finished)
- log_warn(LD_BUG,
- "open stream hasn't sent socks answer yet? Closing.");
+ if (conn->_base.type == CONN_TYPE_AP) {
+ entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
+ if (entry_conn->socks_request &&
+ !entry_conn->socks_request->has_finished)
+ log_warn(LD_BUG,
+ "open stream hasn't sent socks answer yet? Closing.");
+ }
/* We just *got* an end; no reason to send one. */
conn->edge_has_sent_end = 1;
if (!conn->end_reason)
@@ -1152,8 +1186,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
if (!conn->_base.marked_for_close) {
/* only mark it if not already marked. it's possible to
* get the 'end' right around when the client hangs up on us. */
- connection_mark_for_close(TO_CONN(conn));
- conn->_base.hold_open_until_flushed = 1;
+ connection_mark_and_flush(TO_CONN(conn));
}
return 0;
case RELAY_COMMAND_EXTEND:
@@ -1324,10 +1357,17 @@ int
connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
int *max_cells)
{
- size_t amount_to_process, length;
+ size_t bytes_to_process, length;
char payload[CELL_PAYLOAD_SIZE];
circuit_t *circ;
- unsigned domain = conn->cpath_layer ? LD_APP : LD_EXIT;
+ const unsigned domain = conn->_base.type == CONN_TYPE_AP ? LD_APP : LD_EXIT;
+ int sending_from_optimistic = 0;
+ const int sending_optimistically =
+ conn->_base.type == CONN_TYPE_AP &&
+ conn->_base.state != AP_CONN_STATE_OPEN;
+ entry_connection_t *entry_conn =
+ conn->_base.type == CONN_TYPE_AP ? EDGE_TO_ENTRY_CONN(conn) : NULL;
+ crypt_path_t *cpath_layer = conn->cpath_layer;
tor_assert(conn);
@@ -1350,7 +1390,7 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
return -1;
}
- if (circuit_consider_stop_edge_reading(circ, conn->cpath_layer))
+ if (circuit_consider_stop_edge_reading(circ, cpath_layer))
return 0;
if (conn->package_window <= 0) {
@@ -1360,44 +1400,75 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
return 0;
}
- amount_to_process = buf_datalen(conn->_base.inbuf);
+ sending_from_optimistic = entry_conn &&
+ entry_conn->sending_optimistic_data != NULL;
- if (!amount_to_process)
+ if (PREDICT_UNLIKELY(sending_from_optimistic)) {
+ bytes_to_process = generic_buffer_len(entry_conn->sending_optimistic_data);
+ if (PREDICT_UNLIKELY(!bytes_to_process)) {
+ log_warn(LD_BUG, "sending_optimistic_data was non-NULL but empty");
+ bytes_to_process = connection_get_inbuf_len(TO_CONN(conn));
+ sending_from_optimistic = 0;
+ }
+ } else {
+ bytes_to_process = connection_get_inbuf_len(TO_CONN(conn));
+ }
+
+ if (!bytes_to_process)
return 0;
- if (!package_partial && amount_to_process < RELAY_PAYLOAD_SIZE)
+ if (!package_partial && bytes_to_process < RELAY_PAYLOAD_SIZE)
return 0;
- if (amount_to_process > RELAY_PAYLOAD_SIZE) {
+ if (bytes_to_process > RELAY_PAYLOAD_SIZE) {
length = RELAY_PAYLOAD_SIZE;
} else {
- length = amount_to_process;
+ length = bytes_to_process;
}
stats_n_data_bytes_packaged += length;
stats_n_data_cells_packaged += 1;
- connection_fetch_from_buf(payload, length, TO_CONN(conn));
+ if (PREDICT_UNLIKELY(sending_from_optimistic)) {
+ /* XXX023 We could be more efficient here by sometimes packing
+ * previously-sent optimistic data in the same cell with data
+ * from the inbuf. */
+ generic_buffer_get(entry_conn->sending_optimistic_data, payload, length);
+ if (!generic_buffer_len(entry_conn->sending_optimistic_data)) {
+ generic_buffer_free(entry_conn->sending_optimistic_data);
+ entry_conn->sending_optimistic_data = NULL;
+ }
+ } else {
+ connection_fetch_from_buf(payload, length, TO_CONN(conn));
+ }
log_debug(domain,"(%d) Packaging %d bytes (%d waiting).", conn->_base.s,
- (int)length, (int)buf_datalen(conn->_base.inbuf));
+ (int)length, (int)connection_get_inbuf_len(TO_CONN(conn)));
+
+ if (sending_optimistically && !sending_from_optimistic) {
+ /* This is new optimistic data; remember it in case we need to detach and
+ retry */
+ if (!entry_conn->pending_optimistic_data)
+ entry_conn->pending_optimistic_data = generic_buffer_new();
+ generic_buffer_add(entry_conn->pending_optimistic_data, payload, length);
+ }
if (connection_edge_send_command(conn, RELAY_COMMAND_DATA,
payload, length) < 0 )
/* circuit got marked for close, don't continue, don't need to mark conn */
return 0;
- if (!conn->cpath_layer) { /* non-rendezvous exit */
+ if (!cpath_layer) { /* non-rendezvous exit */
tor_assert(circ->package_window > 0);
circ->package_window--;
} else { /* we're an AP, or an exit on a rendezvous circ */
- tor_assert(conn->cpath_layer->package_window > 0);
- conn->cpath_layer->package_window--;
+ tor_assert(cpath_layer->package_window > 0);
+ cpath_layer->package_window--;
}
if (--conn->package_window <= 0) { /* is it 0 after decrement? */
connection_stop_reading(TO_CONN(conn));
log_debug(domain,"conn->package_window reached 0.");
- circuit_consider_stop_edge_reading(circ, conn->cpath_layer);
+ circuit_consider_stop_edge_reading(circ, cpath_layer);
return 0; /* don't process the inbuf any more */
}
log_debug(domain,"conn->package_window is now %d",conn->package_window);
@@ -1436,7 +1507,7 @@ connection_edge_consider_sending_sendme(edge_connection_t *conn)
}
while (conn->deliver_window <= STREAMWINDOW_START - STREAMWINDOW_INCREMENT) {
- log_debug(conn->cpath_layer?LD_APP:LD_EXIT,
+ log_debug(conn->_base.type == CONN_TYPE_AP ?LD_APP:LD_EXIT,
"Outbuf %d, Queuing stream sendme.",
(int)conn->_base.outbuf_flushlen);
conn->deliver_window += STREAMWINDOW_INCREMENT;
@@ -1532,7 +1603,7 @@ circuit_resume_edge_reading_helper(edge_connection_t *first_conn,
if (!layer_hint || conn->cpath_layer == layer_hint) {
connection_start_reading(TO_CONN(conn));
- if (buf_datalen(conn->_base.inbuf) > 0)
+ if (connection_get_inbuf_len(TO_CONN(conn)) > 0)
++n_packaging_streams;
}
}
@@ -1543,7 +1614,7 @@ circuit_resume_edge_reading_helper(edge_connection_t *first_conn,
if (!layer_hint || conn->cpath_layer == layer_hint) {
connection_start_reading(TO_CONN(conn));
- if (buf_datalen(conn->_base.inbuf) > 0)
+ if (connection_get_inbuf_len(TO_CONN(conn)) > 0)
++n_packaging_streams;
}
}
@@ -1582,7 +1653,7 @@ circuit_resume_edge_reading_helper(edge_connection_t *first_conn,
}
/* If there's still data to read, we'll be coming back to this stream. */
- if (buf_datalen(conn->_base.inbuf))
+ if (connection_get_inbuf_len(TO_CONN(conn)))
++n_streams_left;
/* If the circuit won't accept any more data, return without looking
@@ -1638,9 +1709,10 @@ circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
if (layer_hint->package_window <= 0) {
log_debug(domain,"yes, at-origin. stopped.");
for (conn = TO_ORIGIN_CIRCUIT(circ)->p_streams; conn;
- conn=conn->next_stream)
+ conn=conn->next_stream) {
if (conn->cpath_layer == layer_hint)
connection_stop_reading(TO_CONN(conn));
+ }
return 1;
}
return 0;
@@ -1995,7 +2067,8 @@ static int ewma_enabled = 0;
/** Adjust the global cell scale factor based on <b>options</b> */
void
-cell_ewma_set_scale_factor(or_options_t *options, networkstatus_t *consensus)
+cell_ewma_set_scale_factor(const or_options_t *options,
+ const networkstatus_t *consensus)
{
int32_t halflife_ms;
double halflife;
@@ -2246,7 +2319,7 @@ set_streams_blocked_on_circ(circuit_t *circ, or_connection_t *orconn,
edge->edge_blocked_on_circ = block;
}
- if (!conn->read_event) {
+ if (!conn->read_event && !HAS_BUFFEREVENT(conn)) {
/* This connection is a placeholder for something; probably a DNS
* request. It can't actually stop or start reading.*/
continue;
@@ -2321,13 +2394,13 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max,
/* Calculate the exact time that this cell has spent in the queue. */
if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) {
- struct timeval now;
+ struct timeval tvnow;
uint32_t flushed;
uint32_t cell_waiting_time;
insertion_time_queue_t *it_queue = queue->insertion_times;
- tor_gettimeofday_cached(&now);
- flushed = (uint32_t)((now.tv_sec % SECONDS_IN_A_DAY) * 100L +
- (uint32_t)now.tv_usec / (uint32_t)10000L);
+ tor_gettimeofday_cached(&tvnow);
+ flushed = (uint32_t)((tvnow.tv_sec % SECONDS_IN_A_DAY) * 100L +
+ (uint32_t)tvnow.tv_usec / (uint32_t)10000L);
if (!it_queue || !it_queue->first) {
log_info(LD_GENERAL, "Cannot determine insertion time of cell. "
"Looks like the CellStatistics option was "
@@ -2447,7 +2520,7 @@ append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn,
make_circuit_active_on_conn(circ, orconn);
}
- if (! buf_datalen(orconn->_base.outbuf)) {
+ if (! connection_get_outbuf_len(TO_CONN(orconn))) {
/* There is no data at all waiting to be sent on the outbuf. Add a
* cell, so that we can notice when it gets flushed, flushed_some can
* get called, and we can start putting more data onto the buffer then.
diff --git a/src/or/relay.h b/src/or/relay.h
index f64752da5..7fce8edca 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -60,8 +60,8 @@ const uint8_t *decode_address_from_payload(tor_addr_t *addr_out,
const uint8_t *payload,
int payload_len);
unsigned cell_ewma_get_tick(void);
-void cell_ewma_set_scale_factor(or_options_t *options,
- networkstatus_t *consensus);
+void cell_ewma_set_scale_factor(const or_options_t *options,
+ const networkstatus_t *consensus);
void circuit_clear_cell_queue(circuit_t *circ, or_connection_t *orconn);
void tor_gettimeofday_cache_clear(void);
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index f84475acb..1038378de 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -16,6 +16,7 @@
#include "connection_edge.h"
#include "directory.h"
#include "main.h"
+#include "nodelist.h"
#include "relay.h"
#include "rendclient.h"
#include "rendcommon.h"
@@ -138,6 +139,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
tor_assert(rendcirc->rend_data);
tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address,
rendcirc->rend_data->onion_address));
+ tor_assert(!(introcirc->build_state->onehop_tunnel));
+ tor_assert(!(rendcirc->build_state->onehop_tunnel));
if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
&entry) < 1) {
@@ -328,6 +331,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
}
tor_assert(circ->build_state->chosen_exit);
+ tor_assert(!(circ->build_state->onehop_tunnel));
tor_assert(circ->rend_data);
if (request_len == 0) {
@@ -339,6 +343,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
rendcirc = circuit_get_by_rend_query_and_purpose(
circ->rend_data->onion_address, CIRCUIT_PURPOSE_C_REND_READY);
if (rendcirc) { /* remember the ack */
+ tor_assert(!(rendcirc->build_state->onehop_tunnel));
rendcirc->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
/* Set timestamp_dirty, because circuit_expire_building expects
* it to specify when a circuit entered the
@@ -360,8 +365,9 @@ rend_client_introduction_acked(origin_circuit_t *circ,
log_info(LD_REND, "Got nack for %s from %s...",
safe_str_client(circ->rend_data->onion_address),
safe_str_client(extend_info_describe(circ->build_state->chosen_exit)));
- if (rend_client_remove_intro_point(circ->build_state->chosen_exit,
- circ->rend_data) > 0) {
+ if (rend_client_report_intro_point_failure(circ->build_state->chosen_exit,
+ circ->rend_data,
+ INTRO_POINT_FAILURE_GENERIC)>0) {
/* There are introduction points left. Re-extend the circuit to
* another intro point and try again. */
int result = rend_client_reextend_intro_circuit(circ);
@@ -378,9 +384,12 @@ rend_client_introduction_acked(origin_circuit_t *circ,
#define REND_HID_SERV_DIR_REQUERY_PERIOD (15 * 60)
/** Contains the last request times to hidden service directories for
- * certain queries; keys are strings consisting of base32-encoded
- * hidden service directory identities and base32-encoded descriptor IDs;
- * values are pointers to timestamps of the last requests. */
+ * certain queries; each key is a string consisting of the
+ * concatenation of a base32-encoded HS directory identity digest, a
+ * base32-encoded HS descriptor ID, and a hidden service address
+ * (without the ".onion" part); each value is a pointer to a time_t
+ * holding the time of the last request for that descriptor ID to that
+ * HS directory. */
static strmap_t *last_hid_serv_requests_ = NULL;
/** Returns last_hid_serv_requests_, initializing it to a new strmap if
@@ -393,23 +402,34 @@ get_last_hid_serv_requests(void)
return last_hid_serv_requests_;
}
+#define LAST_HID_SERV_REQUEST_KEY_LEN (REND_DESC_ID_V2_LEN_BASE32 + \
+ REND_DESC_ID_V2_LEN_BASE32 + \
+ REND_SERVICE_ID_LEN_BASE32)
+
/** Look up the last request time to hidden service directory <b>hs_dir</b>
- * for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero,
+ * for descriptor ID <b>desc_id_base32</b> for the service specified in
+ * <b>rend_query</b>. If <b>set</b> is non-zero,
* assign the current time <b>now</b> and return that. Otherwise, return
* the most recent request time, or 0 if no such request has been sent
* before. */
static time_t
lookup_last_hid_serv_request(routerstatus_t *hs_dir,
- const char *desc_id_base32, time_t now, int set)
+ const char *desc_id_base32,
+ const rend_data_t *rend_query,
+ time_t now, int set)
{
char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
- char hsdir_desc_comb_id[2 * REND_DESC_ID_V2_LEN_BASE32 + 1];
+ char hsdir_desc_comb_id[LAST_HID_SERV_REQUEST_KEY_LEN + 1];
time_t *last_request_ptr;
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32),
hs_dir->identity_digest, DIGEST_LEN);
- tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s",
- hsdir_id_base32, desc_id_base32);
+ tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s%s",
+ hsdir_id_base32,
+ desc_id_base32,
+ rend_query->onion_address);
+ /* XXX023 tor_assert(strlen(hsdir_desc_comb_id) ==
+ LAST_HID_SERV_REQUEST_KEY_LEN); */
if (set) {
time_t *oldptr;
last_request_ptr = tor_malloc_zero(sizeof(time_t));
@@ -427,10 +447,10 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir,
* it does not contain requests older than REND_HID_SERV_DIR_REQUERY_PERIOD
* seconds any more. */
static void
-directory_clean_last_hid_serv_requests(void)
+directory_clean_last_hid_serv_requests(time_t now)
{
strmap_iter_t *iter;
- time_t cutoff = time(NULL) - REND_HID_SERV_DIR_REQUERY_PERIOD;
+ time_t cutoff = now - REND_HID_SERV_DIR_REQUERY_PERIOD;
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
for (iter = strmap_iter_init(last_hid_serv_requests);
!strmap_iter_done(iter); ) {
@@ -448,6 +468,33 @@ directory_clean_last_hid_serv_requests(void)
}
}
+/** Remove all requests related to the hidden service named
+ * <b>onion_address</b> from the history of times of requests to
+ * hidden service directories. */
+static void
+purge_hid_serv_from_last_hid_serv_requests(const char *onion_address)
+{
+ strmap_iter_t *iter;
+ strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
+ /* XXX023 tor_assert(strlen(onion_address) == REND_SERVICE_ID_LEN_BASE32); */
+ for (iter = strmap_iter_init(last_hid_serv_requests);
+ !strmap_iter_done(iter); ) {
+ const char *key;
+ void *val;
+ strmap_iter_get(iter, &key, &val);
+ /* XXX023 tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */
+ if (tor_memeq(key + LAST_HID_SERV_REQUEST_KEY_LEN -
+ REND_SERVICE_ID_LEN_BASE32,
+ onion_address,
+ REND_SERVICE_ID_LEN_BASE32)) {
+ iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
+ tor_free(val);
+ } else {
+ iter = strmap_iter_next(last_hid_serv_requests, iter);
+ }
+ }
+}
+
/** Purge the history of request times to hidden service directories,
* so that future lookups of an HS descriptor will not fail because we
* accessed all of the HSDir relays responsible for the descriptor
@@ -469,12 +516,11 @@ rend_client_purge_last_hid_serv_requests(void)
}
/** Determine the responsible hidden service directories for <b>desc_id</b>
- * and fetch the descriptor belonging to that ID from one of them. Only
- * send a request to hidden service directories that we did not try within
- * the last REND_HID_SERV_DIR_REQUERY_PERIOD seconds; on success, return 1,
+ * and fetch the descriptor with that ID from one of them. Only
+ * send a request to a hidden service directory that we have not yet tried
+ * during this attempt to connect to this hidden service; on success, return 1,
* in the case that no hidden service directory is left to ask for the
- * descriptor, return 0, and in case of a failure -1. <b>query</b> is only
- * passed for pretty log statements. */
+ * descriptor, return 0, and in case of a failure -1. */
static int
directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
{
@@ -494,12 +540,16 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
/* Only select those hidden service directories to which we did not send
* a request recently and for which we have a router descriptor here. */
- directory_clean_last_hid_serv_requests(); /* Clean request history first. */
+
+ /* Clean request history first. */
+ directory_clean_last_hid_serv_requests(now);
SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, {
- if (lookup_last_hid_serv_request(dir, desc_id_base32, 0, 0) +
- REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
- !router_get_by_digest(dir->identity_digest))
+ time_t last = lookup_last_hid_serv_request(
+ dir, desc_id_base32, rend_query, 0, 0);
+ const node_t *node = node_get_by_id(dir->identity_digest);
+ if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
+ !node || !node_has_descriptor(node))
SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
});
@@ -512,9 +562,9 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
return 0;
}
- /* Remember, that we are requesting a descriptor from this hidden service
+ /* Remember that we are requesting a descriptor from this hidden service
* directory now. */
- lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
+ lookup_last_hid_serv_request(hs_dir, desc_id_base32, rend_query, now, 1);
/* Encode descriptor cookie for logging purposes. */
if (rend_query->auth_type != REND_NO_AUTH) {
@@ -568,10 +618,11 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
"service descriptor, but are not fetching service descriptors.");
return;
}
- /* Before fetching, check if we already have the descriptor here. */
- if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0) {
+ /* Before fetching, check if we already have a usable descriptor here. */
+ if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0 &&
+ rend_client_any_intro_points_usable(e)) {
log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we "
- "already have that descriptor here. Not fetching.");
+ "already have a usable descriptor here. Not fetching.");
return;
}
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
@@ -641,16 +692,31 @@ rend_client_cancel_descriptor_fetches(void)
} SMARTLIST_FOREACH_END(conn);
}
-/** Remove failed_intro from ent. If ent now has no intro points, or
- * service is unrecognized, then launch a new renddesc fetch.
-
+/** Mark <b>failed_intro</b> as a failed introduction point for the
+ * hidden service specified by <b>rend_query</b>. If the HS now has no
+ * usable intro points, or we do not have an HS descriptor for it,
+ * then launch a new renddesc fetch.
*
- * Return -1 if error, 0 if no intro points remain or service
+ * If <b>failure_type</b> is INTRO_POINT_FAILURE_GENERIC, remove the
+ * intro point from (our parsed copy of) the HS descriptor.
+ *
+ * If <b>failure_type</b> is INTRO_POINT_FAILURE_TIMEOUT, mark the
+ * intro point as 'timed out'; it will not be retried until the
+ * current hidden service connection attempt has ended or it has
+ * appeared in a newly fetched rendezvous descriptor.
+ *
+ * If <b>failure_type</b> is INTRO_POINT_FAILURE_UNREACHABLE,
+ * increment the intro point's reachability-failure count; if it has
+ * now failed MAX_INTRO_POINT_REACHABILITY_FAILURES or more times,
+ * remove the intro point from (our parsed copy of) the HS descriptor.
+ *
+ * Return -1 if error, 0 if no usable intro points remain or service
* unrecognized, 1 if recognized and some intro points remain.
*/
int
-rend_client_remove_intro_point(extend_info_t *failed_intro,
- const rend_data_t *rend_query)
+rend_client_report_intro_point_failure(extend_info_t *failed_intro,
+ const rend_data_t *rend_query,
+ unsigned int failure_type)
{
int i, r;
rend_cache_entry_t *ent;
@@ -673,8 +739,34 @@ rend_client_remove_intro_point(extend_info_t *failed_intro,
rend_intro_point_t *intro = smartlist_get(ent->parsed->intro_nodes, i);
if (tor_memeq(failed_intro->identity_digest,
intro->extend_info->identity_digest, DIGEST_LEN)) {
- rend_intro_point_free(intro);
- smartlist_del(ent->parsed->intro_nodes, i);
+ switch (failure_type) {
+ default:
+ log_warn(LD_BUG, "Unknown failure type %u. Removing intro point.",
+ failure_type);
+ tor_fragile_assert();
+ /* fall through */
+ case INTRO_POINT_FAILURE_GENERIC:
+ rend_intro_point_free(intro);
+ smartlist_del(ent->parsed->intro_nodes, i);
+ break;
+ case INTRO_POINT_FAILURE_TIMEOUT:
+ intro->timed_out = 1;
+ break;
+ case INTRO_POINT_FAILURE_UNREACHABLE:
+ ++(intro->unreachable_count);
+ {
+ int zap_intro_point =
+ intro->unreachable_count >= MAX_INTRO_POINT_REACHABILITY_FAILURES;
+ log_info(LD_REND, "Failed to reach this intro point %u times.%s",
+ intro->unreachable_count,
+ zap_intro_point ? " Removing from descriptor.": "");
+ if (zap_intro_point) {
+ rend_intro_point_free(intro);
+ smartlist_del(ent->parsed->intro_nodes, i);
+ }
+ }
+ break;
+ }
break;
}
}
@@ -810,40 +902,42 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
void
rend_client_desc_trynow(const char *query)
{
- edge_connection_t *conn;
+ entry_connection_t *conn;
rend_cache_entry_t *entry;
+ const rend_data_t *rend_data;
time_t now = time(NULL);
smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, _conn) {
- if (_conn->type != CONN_TYPE_AP ||
- _conn->state != AP_CONN_STATE_RENDDESC_WAIT ||
- _conn->marked_for_close)
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
+ if (base_conn->type != CONN_TYPE_AP ||
+ base_conn->state != AP_CONN_STATE_RENDDESC_WAIT ||
+ base_conn->marked_for_close)
continue;
- conn = TO_EDGE_CONN(_conn);
- if (!conn->rend_data)
+ conn = TO_ENTRY_CONN(base_conn);
+ rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data;
+ if (!rend_data)
continue;
- if (rend_cmp_service_ids(query, conn->rend_data->onion_address))
+ if (rend_cmp_service_ids(query, rend_data->onion_address))
continue;
- assert_connection_ok(TO_CONN(conn), now);
- if (rend_cache_lookup_entry(conn->rend_data->onion_address, -1,
+ assert_connection_ok(base_conn, now);
+ if (rend_cache_lookup_entry(rend_data->onion_address, -1,
&entry) == 1 &&
rend_client_any_intro_points_usable(entry)) {
/* either this fetch worked, or it failed but there was a
* valid entry from before which we should reuse */
log_info(LD_REND,"Rend desc is usable. Launching circuits.");
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
/* restart their timeout values, so they get a fair shake at
* connecting to the hidden service. */
- conn->_base.timestamp_created = now;
- conn->_base.timestamp_lastread = now;
- conn->_base.timestamp_lastwritten = now;
+ base_conn->timestamp_created = now;
+ base_conn->timestamp_lastread = now;
+ base_conn->timestamp_lastwritten = now;
if (connection_ap_handshake_attach_circuit(conn) < 0) {
/* it will never work */
log_warn(LD_REND,"Rendezvous attempt failed. Closing.");
- if (!conn->_base.marked_for_close)
+ if (!base_conn->marked_for_close)
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
}
} else { /* 404, or fetch didn't get that far */
@@ -851,8 +945,34 @@ rend_client_desc_trynow(const char *query)
"unavailable (try again later).",
safe_str_client(query));
connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED);
+ rend_client_note_connection_attempt_ended(query);
}
- } SMARTLIST_FOREACH_END(_conn);
+ } SMARTLIST_FOREACH_END(base_conn);
+}
+
+/** Clear temporary state used only during an attempt to connect to
+ * the hidden service named <b>onion_address</b>. Called when a
+ * connection attempt has ended; may be called occasionally at other
+ * times, and should be reasonably harmless. */
+void
+rend_client_note_connection_attempt_ended(const char *onion_address)
+{
+ rend_cache_entry_t *cache_entry = NULL;
+ rend_cache_lookup_entry(onion_address, -1, &cache_entry);
+
+ log_info(LD_REND, "Connection attempt for %s has ended; "
+ "cleaning up temporary state.",
+ safe_str_client(onion_address));
+
+ /* Clear the timed_out flag on all remaining intro points for this HS. */
+ if (cache_entry != NULL) {
+ SMARTLIST_FOREACH(cache_entry->parsed->intro_nodes,
+ rend_intro_point_t *, ip,
+ ip->timed_out = 0; );
+ }
+
+ /* Remove the HS's entries in last_hid_serv_requests. */
+ purge_hid_serv_from_last_hid_serv_requests(onion_address);
}
/** Return a newly allocated extend_info_t* for a randomly chosen introduction
@@ -894,8 +1014,7 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
int i;
rend_intro_point_t *intro;
- routerinfo_t *router;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
smartlist_t *usable_nodes;
int n_excluded = 0;
@@ -904,6 +1023,13 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
usable_nodes = smartlist_create();
smartlist_add_all(usable_nodes, entry->parsed->intro_nodes);
+ /* Remove the intro points that have timed out during this HS
+ * connection attempt from our list of usable nodes. */
+ SMARTLIST_FOREACH(usable_nodes, rend_intro_point_t *, ip,
+ if (ip->timed_out) {
+ SMARTLIST_DEL_CURRENT(usable_nodes, ip);
+ });
+
again:
if (smartlist_len(usable_nodes) == 0) {
if (n_excluded && get_options()->StrictNodes && warnings) {
@@ -921,21 +1047,22 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
intro = smartlist_get(usable_nodes, i);
/* Do we need to look up the router or is the extend info complete? */
if (!intro->extend_info->onion_key) {
+ const node_t *node;
if (tor_digest_is_zero(intro->extend_info->identity_digest))
- router = router_get_by_hexdigest(intro->extend_info->nickname);
+ node = node_get_by_hex_id(intro->extend_info->nickname);
else
- router = router_get_by_digest(intro->extend_info->identity_digest);
- if (!router) {
+ node = node_get_by_id(intro->extend_info->identity_digest);
+ if (!node) {
log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
intro->extend_info->nickname);
smartlist_del(usable_nodes, i);
goto again;
}
extend_info_free(intro->extend_info);
- intro->extend_info = extend_info_from_router(router);
+ intro->extend_info = extend_info_from_node(node);
}
/* Check if we should refuse to talk to this router. */
- if (options->ExcludeNodes && strict &&
+ if (strict &&
routerset_contains_extendinfo(options->ExcludeNodes,
intro->extend_info)) {
n_excluded++;
@@ -1001,7 +1128,8 @@ rend_service_authorization_free_all(void)
* service and add it to the local map of hidden service authorizations.
* Return 0 for success and -1 for failure. */
int
-rend_parse_service_authorization(or_options_t *options, int validate_only)
+rend_parse_service_authorization(const or_options_t *options,
+ int validate_only)
{
config_line_t *line;
int res = -1;
diff --git a/src/or/rendclient.h b/src/or/rendclient.h
index c6cf82b3d..89da47789 100644
--- a/src/or/rendclient.h
+++ b/src/or/rendclient.h
@@ -22,8 +22,15 @@ int rend_client_introduction_acked(origin_circuit_t *circ,
void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query);
void rend_client_cancel_descriptor_fetches(void);
void rend_client_purge_last_hid_serv_requests(void);
-int rend_client_remove_intro_point(extend_info_t *failed_intro,
- const rend_data_t *rend_query);
+
+#define INTRO_POINT_FAILURE_GENERIC 0
+#define INTRO_POINT_FAILURE_TIMEOUT 1
+#define INTRO_POINT_FAILURE_UNREACHABLE 2
+
+int rend_client_report_intro_point_failure(extend_info_t *failed_intro,
+ const rend_data_t *rend_query,
+ unsigned int failure_type);
+
int rend_client_rendezvous_acked(origin_circuit_t *circ,
const uint8_t *request,
size_t request_len);
@@ -32,12 +39,14 @@ int rend_client_receive_rendezvous(origin_circuit_t *circ,
size_t request_len);
void rend_client_desc_trynow(const char *query);
+void rend_client_note_connection_attempt_ended(const char *onion_address);
+
extend_info_t *rend_client_get_random_intro(const rend_data_t *rend_query);
int rend_client_any_intro_points_usable(const rend_cache_entry_t *entry);
int rend_client_send_introduction(origin_circuit_t *introcirc,
origin_circuit_t *rendcirc);
-int rend_parse_service_authorization(or_options_t *options,
+int rend_parse_service_authorization(const or_options_t *options,
int validate_only);
rend_service_authorization_t *rend_client_lookup_service_authorization(
const char *onion_address);
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index c5bf88163..94bb00221 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -814,14 +814,13 @@ rend_cache_free_all(void)
/** Removes all old entries from the service descriptor cache.
*/
void
-rend_cache_clean(void)
+rend_cache_clean(time_t now)
{
strmap_iter_t *iter;
const char *key;
void *val;
rend_cache_entry_t *ent;
- time_t cutoff;
- cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
+ time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) {
strmap_iter_get(iter, &key, &val);
ent = (rend_cache_entry_t*)val;
@@ -849,10 +848,10 @@ rend_cache_purge(void)
/** Remove all old v2 descriptors and those for which this hidden service
* directory is not responsible for any more. */
void
-rend_cache_clean_v2_descs_as_dir(void)
+rend_cache_clean_v2_descs_as_dir(time_t now)
{
digestmap_iter_t *iter;
- time_t cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
+ time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
for (iter = digestmap_iter_init(rend_cache_v2_dir);
!digestmap_iter_done(iter); ) {
const char *key;
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index c51039f1f..0d64466db 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -34,8 +34,8 @@ void rend_encoded_v2_service_descriptor_free(
void rend_intro_point_free(rend_intro_point_t *intro);
void rend_cache_init(void);
-void rend_cache_clean(void);
-void rend_cache_clean_v2_descs_as_dir(void);
+void rend_cache_clean(time_t now);
+void rend_cache_clean_v2_descs_as_dir(time_t now);
void rend_cache_purge(void);
void rend_cache_free_all(void);
int rend_valid_service_id(const char *query);
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 0f5731912..d582d71f6 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -14,6 +14,7 @@
#include "config.h"
#include "directory.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendservice.h"
@@ -257,7 +258,7 @@ parse_port_config(const char *string)
} else {
addrport = smartlist_get(sl,1);
if (strchr(addrport, ':') || strchr(addrport, '.')) {
- if (tor_addr_port_parse(addrport, &addr, &p)<0) {
+ if (tor_addr_port_lookup(addrport, &addr, &p)<0) {
log_warn(LD_CONFIG,"Unparseable address in hidden service port "
"configuration.");
goto err;
@@ -291,7 +292,7 @@ parse_port_config(const char *string)
* normal, but don't actually change the configured services.)
*/
int
-rend_config_services(or_options_t *options, int validate_only)
+rend_config_services(const or_options_t *options, int validate_only)
{
config_line_t *line;
rend_service_t *service = NULL;
@@ -904,8 +905,9 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
time_t now = time(NULL);
char diffie_hellman_hash[DIGEST_LEN];
time_t *access_time;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
+ tor_assert(!(circuit->build_state->onehop_tunnel));
tor_assert(circuit->rend_data);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
@@ -1058,7 +1060,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
} else {
char *rp_nickname;
size_t nickname_field_len;
- routerinfo_t *router;
+ const node_t *node;
int version;
if (*buf == 1) {
rp_nickname = buf+1;
@@ -1085,8 +1087,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
len -= nickname_field_len;
len -= rp_nickname - buf; /* also remove header space used by version, if
* any */
- router = router_get_by_nickname(rp_nickname, 0);
- if (!router) {
+ node = node_get_by_nickname(rp_nickname, 0);
+ if (!node) {
log_info(LD_REND, "Couldn't find router %s named in introduce2 cell.",
escaped_safe_str_client(rp_nickname));
/* XXXX Add a no-such-router reason? */
@@ -1094,7 +1096,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
goto err;
}
- extend_info = extend_info_from_router(router);
+ extend_info = extend_info_from_node(node);
}
if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
@@ -1104,7 +1106,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
}
/* Check if we'd refuse to talk to this router */
- if (options->ExcludeNodes && options->StrictNodes &&
+ if (options->StrictNodes &&
routerset_contains_extendinfo(options->ExcludeNodes, extend_info)) {
log_warn(LD_REND, "Client asked to rendezvous at a relay that we "
"exclude, and StrictNodes is set. Refusing service.");
@@ -1389,6 +1391,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
crypto_pk_env_t *intro_key;
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
+ tor_assert(!(circuit->build_state->onehop_tunnel));
tor_assert(circuit->cpath);
tor_assert(circuit->rend_data);
@@ -1407,7 +1410,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
/* If we already have enough introduction circuits for this service,
* redefine this one as a general circuit or close it, depending. */
if (count_established_intro_points(serviceid) > NUM_INTRO_POINTS) {
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (options->ExcludeNodes) {
/* XXXX in some future version, we can test whether the transition is
allowed or not given the actual nodes in the circuit. But for now,
@@ -1544,6 +1547,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
tor_assert(circuit->cpath);
tor_assert(circuit->build_state);
+ tor_assert(!(circuit->build_state->onehop_tunnel));
tor_assert(circuit->rend_data);
hop = circuit->build_state->pending_final_cpath;
tor_assert(hop);
@@ -1672,12 +1676,14 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
for (j = 0; j < smartlist_len(responsible_dirs); j++) {
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
char *hs_dir_ip;
+ const node_t *node;
hs_dir = smartlist_get(responsible_dirs, j);
if (smartlist_digest_isin(renddesc->successful_uploads,
hs_dir->identity_digest))
/* Don't upload descriptor if we succeeded in doing so last time. */
continue;
- if (!router_get_by_digest(hs_dir->identity_digest)) {
+ node = node_get_by_id(hs_dir->identity_digest);
+ if (!node || !node_has_descriptor(node)) {
log_info(LD_REND, "Not sending publish request for v2 descriptor to "
"hidden service directory %s; we don't have its "
"router descriptor. Queuing for later upload.",
@@ -1854,19 +1860,19 @@ void
rend_services_introduce(void)
{
int i,j,r;
- routerinfo_t *router;
+ const node_t *node;
rend_service_t *service;
rend_intro_point_t *intro;
int changed, prev_intro_nodes;
- smartlist_t *intro_routers;
+ smartlist_t *intro_nodes;
time_t now;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
- intro_routers = smartlist_create();
+ intro_nodes = smartlist_create();
now = time(NULL);
for (i=0; i < smartlist_len(rend_service_list); ++i) {
- smartlist_clear(intro_routers);
+ smartlist_clear(intro_nodes);
service = smartlist_get(rend_service_list, i);
tor_assert(service);
@@ -1886,8 +1892,8 @@ rend_services_introduce(void)
service. */
for (j=0; j < smartlist_len(service->intro_nodes); ++j) {
intro = smartlist_get(service->intro_nodes, j);
- router = router_get_by_digest(intro->extend_info->identity_digest);
- if (!router || !find_intro_circuit(intro, service->pk_digest)) {
+ node = node_get_by_id(intro->extend_info->identity_digest);
+ if (!node || !find_intro_circuit(intro, service->pk_digest)) {
log_info(LD_REND,"Giving up on %s as intro point for %s.",
safe_str_client(extend_info_describe(intro->extend_info)),
safe_str_client(service->service_id));
@@ -1907,8 +1913,8 @@ rend_services_introduce(void)
smartlist_del(service->intro_nodes,j--);
changed = 1;
}
- if (router)
- smartlist_add(intro_routers, router);
+ if (node)
+ smartlist_add(intro_nodes, (void*)node);
}
/* We have enough intro points, and the intro points we thought we had were
@@ -1937,26 +1943,26 @@ rend_services_introduce(void)
#define NUM_INTRO_POINTS_INIT (NUM_INTRO_POINTS + 2)
for (j=prev_intro_nodes; j < (prev_intro_nodes == 0 ?
NUM_INTRO_POINTS_INIT : NUM_INTRO_POINTS); ++j) {
- router_crn_flags_t flags = CRN_NEED_UPTIME;
+ router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC;
if (get_options()->_AllowInvalid & ALLOW_INVALID_INTRODUCTION)
flags |= CRN_ALLOW_INVALID;
- router = router_choose_random_node(intro_routers,
- options->ExcludeNodes, flags);
- if (!router) {
+ node = router_choose_random_node(intro_nodes,
+ options->ExcludeNodes, flags);
+ if (!node) {
log_warn(LD_REND,
"Could only establish %d introduction points for %s.",
smartlist_len(service->intro_nodes), service->service_id);
break;
}
changed = 1;
- smartlist_add(intro_routers, router);
+ smartlist_add(intro_nodes, (void*)node);
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
- intro->extend_info = extend_info_from_router(router);
+ intro->extend_info = extend_info_from_node(node);
intro->intro_key = crypto_new_pk_env();
tor_assert(!crypto_pk_generate_key(intro->intro_key));
smartlist_add(service->intro_nodes, intro);
log_info(LD_REND, "Picked router %s as an intro point for %s.",
- safe_str_client(router_describe(router)),
+ safe_str_client(node_describe(node)),
safe_str_client(service->service_id));
}
@@ -1975,7 +1981,7 @@ rend_services_introduce(void)
}
}
}
- smartlist_free(intro_routers);
+ smartlist_free(intro_nodes);
}
/** Regenerate and upload rendezvous service descriptors for all
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 70389afe9..8a2994c4c 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -13,7 +13,7 @@
#define _TOR_RENDSERVICE_H
int num_rend_services(void);
-int rend_config_services(or_options_t *options, int validate_only);
+int rend_config_services(const or_options_t *options, int validate_only);
int rend_service_load_keys(void);
void rend_services_introduce(void);
void rend_consider_services_upload(time_t now);
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 1dd3d94fd..6bbb93b82 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -7,7 +7,7 @@
* \brief Basic history and "reputation" functionality to remember
* which servers have worked in the past, how much bandwidth we've
* been using, which ports we tend to want, and so on; further,
- * exit port statistics and cell statistics.
+ * exit port statistics, cell statistics, and connection statistics.
**/
#include "or.h"
@@ -15,6 +15,7 @@
#include "circuituse.h"
#include "config.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
@@ -643,7 +644,7 @@ rep_hist_dump_stats(time_t now, int severity)
size_t len;
int ret;
unsigned long upt, downt;
- routerinfo_t *r;
+ const node_t *node;
rep_history_clean(now - get_options()->RephistTrackTime);
@@ -657,8 +658,8 @@ rep_hist_dump_stats(time_t now, int severity)
digestmap_iter_get(orhist_it, &digest1, &or_history_p);
or_history = (or_history_t*) or_history_p;
- if ((r = router_get_by_digest(digest1)))
- name1 = r->nickname;
+ if ((node = node_get_by_id(digest1)) && node_get_nickname(node))
+ name1 = node_get_nickname(node);
else
name1 = "(unknown)";
base16_encode(hexdigest1, sizeof(hexdigest1), digest1, DIGEST_LEN);
@@ -688,8 +689,8 @@ rep_hist_dump_stats(time_t now, int severity)
lhist_it = digestmap_iter_next(or_history->link_history_map,
lhist_it)) {
digestmap_iter_get(lhist_it, &digest2, &link_history_p);
- if ((r = router_get_by_digest(digest2)))
- name2 = r->nickname;
+ if ((node = node_get_by_id(digest2)) && node_get_nickname(node))
+ name2 = node_get_nickname(node);
else
name2 = "(unknown)";
@@ -823,7 +824,7 @@ rep_hist_record_mtbf_data(time_t now, int missing_means_down)
base16_encode(dbuf, sizeof(dbuf), digest, DIGEST_LEN);
if (missing_means_down && hist->start_of_run &&
- !router_get_by_digest(digest)) {
+ !router_get_by_id_digest(digest)) {
/* We think this relay is running, but it's not listed in our
* routerlist. Somehow it fell out without telling us it went
* down. Complain and also correct it. */
@@ -938,28 +939,32 @@ rep_hist_get_router_stability_doc(time_t now)
}
DIGESTMAP_FOREACH(history_map, id, or_history_t *, hist) {
- routerinfo_t *ri;
+ const node_t *node;
char dbuf[BASE64_DIGEST_LEN+1];
char header_buf[512];
char *info;
digest_to_base64(dbuf, id);
- ri = router_get_by_digest(id);
- if (ri) {
- char *ip = tor_dup_ip(ri->addr);
+ node = node_get_by_id(id);
+ if (node) {
+ char ip[INET_NTOA_BUF_LEN+1];
char tbuf[ISO_TIME_LEN+1];
- format_iso_time(tbuf, ri->cache_info.published_on);
+ time_t published = node_get_published_on(node);
+ node_get_address_string(node,ip,sizeof(ip));
+ if (published > 0)
+ format_iso_time(tbuf, published);
+ else
+ strlcpy(tbuf, "???", sizeof(tbuf));
tor_snprintf(header_buf, sizeof(header_buf),
"router %s %s %s\n"
"published %s\n"
"relevant-flags %s%s%s\n"
"declared-uptime %ld\n",
- dbuf, ri->nickname, ip,
+ dbuf, node_get_nickname(node), ip,
tbuf,
- ri->is_running ? "Running " : "",
- ri->is_valid ? "Valid " : "",
- ri->is_hibernating ? "Hibernating " : "",
- ri->uptime);
- tor_free(ip);
+ node->is_running ? "Running " : "",
+ node->is_valid ? "Valid " : "",
+ node->ri && node->ri->is_hibernating ? "Hibernating " : "",
+ node_get_declared_uptime(node));
} else {
tor_snprintf(header_buf, sizeof(header_buf),
"router %s {no descriptor}\n", dbuf);
@@ -1474,7 +1479,7 @@ rep_hist_fill_bandwidth_history(char *buf, size_t len, const bw_array_t *b)
{
char *cp = buf;
int i, n;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
uint64_t cutoff;
if (b->num_maxes_set <= b->next_max_idx) {
@@ -1770,10 +1775,13 @@ rep_hist_load_state(or_state_t *state, char **err)
/*********************************************************************/
+typedef struct predicted_port_t {
+ uint16_t port;
+ time_t time;
+} predicted_port_t;
+
/** A list of port numbers that have been used recently. */
static smartlist_t *predicted_ports_list=NULL;
-/** The corresponding most recently used time for each port. */
-static smartlist_t *predicted_ports_times=NULL;
/** We just got an application request for a connection with
* port <b>port</b>. Remember it for the future, so we can keep
@@ -1782,14 +1790,11 @@ static smartlist_t *predicted_ports_times=NULL;
static void
add_predicted_port(time_t now, uint16_t port)
{
- /* XXXX we could just use uintptr_t here, I think. -NM */
- uint16_t *tmp_port = tor_malloc(sizeof(uint16_t));
- time_t *tmp_time = tor_malloc(sizeof(time_t));
- *tmp_port = port;
- *tmp_time = now;
- rephist_total_alloc += sizeof(uint16_t) + sizeof(time_t);
- smartlist_add(predicted_ports_list, tmp_port);
- smartlist_add(predicted_ports_times, tmp_time);
+ predicted_port_t *pp = tor_malloc(sizeof(predicted_port_t));
+ pp->port = port;
+ pp->time = now;
+ rephist_total_alloc += sizeof(*pp);
+ smartlist_add(predicted_ports_list, pp);
}
/** Initialize whatever memory and structs are needed for predicting
@@ -1800,7 +1805,6 @@ static void
predicted_ports_init(void)
{
predicted_ports_list = smartlist_create();
- predicted_ports_times = smartlist_create();
add_predicted_port(time(NULL), 80); /* add one to kickstart us */
}
@@ -1810,12 +1814,11 @@ predicted_ports_init(void)
static void
predicted_ports_free(void)
{
- rephist_total_alloc -= smartlist_len(predicted_ports_list)*sizeof(uint16_t);
- SMARTLIST_FOREACH(predicted_ports_list, char *, cp, tor_free(cp));
+ rephist_total_alloc -=
+ smartlist_len(predicted_ports_list)*sizeof(predicted_port_t);
+ SMARTLIST_FOREACH(predicted_ports_list, predicted_port_t *,
+ pp, tor_free(pp));
smartlist_free(predicted_ports_list);
- rephist_total_alloc -= smartlist_len(predicted_ports_times)*sizeof(time_t);
- SMARTLIST_FOREACH(predicted_ports_times, char *, cp, tor_free(cp));
- smartlist_free(predicted_ports_times);
}
/** Remember that <b>port</b> has been asked for as of time <b>now</b>.
@@ -1825,24 +1828,17 @@ predicted_ports_free(void)
void
rep_hist_note_used_port(time_t now, uint16_t port)
{
- int i;
- uint16_t *tmp_port;
- time_t *tmp_time;
-
tor_assert(predicted_ports_list);
- tor_assert(predicted_ports_times);
if (!port) /* record nothing */
return;
- for (i = 0; i < smartlist_len(predicted_ports_list); ++i) {
- tmp_port = smartlist_get(predicted_ports_list, i);
- tmp_time = smartlist_get(predicted_ports_times, i);
- if (*tmp_port == port) {
- *tmp_time = now;
+ SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
+ if (pp->port == port) {
+ pp->time = now;
return;
}
- }
+ } SMARTLIST_FOREACH_END(pp);
/* it's not there yet; we need to add it */
add_predicted_port(now, port);
}
@@ -1851,36 +1847,28 @@ rep_hist_note_used_port(time_t now, uint16_t port)
* we'll want to make connections to the same port in the future. */
#define PREDICTED_CIRCS_RELEVANCE_TIME (60*60)
-/** Return a pointer to the list of port numbers that
+/** Return a newly allocated pointer to a list of uint16_t * for ports that
* are likely to be asked for in the near future.
- *
- * The caller promises not to mess with it.
*/
smartlist_t *
rep_hist_get_predicted_ports(time_t now)
{
- int i;
- uint16_t *tmp_port;
- time_t *tmp_time;
-
+ smartlist_t *out = smartlist_create();
tor_assert(predicted_ports_list);
- tor_assert(predicted_ports_times);
/* clean out obsolete entries */
- for (i = 0; i < smartlist_len(predicted_ports_list); ++i) {
- tmp_time = smartlist_get(predicted_ports_times, i);
- if (*tmp_time + PREDICTED_CIRCS_RELEVANCE_TIME < now) {
- tmp_port = smartlist_get(predicted_ports_list, i);
- log_debug(LD_CIRC, "Expiring predicted port %d", *tmp_port);
- smartlist_del(predicted_ports_list, i);
- smartlist_del(predicted_ports_times, i);
- rephist_total_alloc -= sizeof(uint16_t)+sizeof(time_t);
- tor_free(tmp_port);
- tor_free(tmp_time);
- i--;
+ SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) {
+ if (pp->time + PREDICTED_CIRCS_RELEVANCE_TIME < now) {
+ log_debug(LD_CIRC, "Expiring predicted port %d", pp->port);
+
+ rephist_total_alloc -= sizeof(predicted_port_t);
+ tor_free(pp);
+ SMARTLIST_DEL_CURRENT(predicted_ports_list, pp);
+ } else {
+ smartlist_add(out, tor_memdup(&pp->port, sizeof(uint16_t)));
}
- }
- return predicted_ports_list;
+ } SMARTLIST_FOREACH_END(pp);
+ return out;
}
/** The user asked us to do a resolve. Rather than keeping track of
@@ -2116,7 +2104,9 @@ rep_hist_exit_stats_term(void)
tor_free(exit_streams);
}
-/** Helper for qsort: compare two ints. */
+/** Helper for qsort: compare two ints. Does not handle overflow properly,
+ * but works fine for sorting an array of port numbers, which is what we use
+ * it for. */
static int
_compare_int(const void *x, const void *y)
{
@@ -2124,7 +2114,8 @@ _compare_int(const void *x, const void *y)
}
/** Return a newly allocated string containing the exit port statistics
- * until <b>now</b>, or NULL if we're not collecting exit stats. */
+ * until <b>now</b>, or NULL if we're not collecting exit stats. Caller
+ * must ensure start_of_exit_stats_interval is in the past. */
char *
rep_hist_format_exit_stats(time_t now)
{
@@ -2143,6 +2134,8 @@ rep_hist_format_exit_stats(time_t now)
if (!start_of_exit_stats_interval)
return NULL; /* Not initialized. */
+ tor_assert(now >= start_of_exit_stats_interval);
+
/* Go through all ports to find the n ports that saw most written and
* read bytes.
*
@@ -2374,41 +2367,60 @@ typedef struct circ_buffer_stats_t {
/** List of circ_buffer_stats_t. */
static smartlist_t *circuits_for_buffer_stats = NULL;
+/** Remember cell statistics <b>mean_num_cells_in_queue</b>,
+ * <b>mean_time_cells_in_queue</b>, and <b>processed_cells</b> of a
+ * circuit. */
+void
+rep_hist_add_buffer_stats(double mean_num_cells_in_queue,
+ double mean_time_cells_in_queue, uint32_t processed_cells)
+{
+ circ_buffer_stats_t *stat;
+ if (!start_of_buffer_stats_interval)
+ return; /* Not initialized. */
+ stat = tor_malloc_zero(sizeof(circ_buffer_stats_t));
+ stat->mean_num_cells_in_queue = mean_num_cells_in_queue;
+ stat->mean_time_cells_in_queue = mean_time_cells_in_queue;
+ stat->processed_cells = processed_cells;
+ if (!circuits_for_buffer_stats)
+ circuits_for_buffer_stats = smartlist_create();
+ smartlist_add(circuits_for_buffer_stats, stat);
+}
+
/** Remember cell statistics for circuit <b>circ</b> at time
* <b>end_of_interval</b> and reset cell counters in case the circuit
* remains open in the next measurement interval. */
void
rep_hist_buffer_stats_add_circ(circuit_t *circ, time_t end_of_interval)
{
- circ_buffer_stats_t *stat;
time_t start_of_interval;
int interval_length;
or_circuit_t *orcirc;
+ double mean_num_cells_in_queue, mean_time_cells_in_queue;
+ uint32_t processed_cells;
if (CIRCUIT_IS_ORIGIN(circ))
return;
orcirc = TO_OR_CIRCUIT(circ);
if (!orcirc->processed_cells)
return;
- if (!circuits_for_buffer_stats)
- circuits_for_buffer_stats = smartlist_create();
- start_of_interval = circ->timestamp_created.tv_sec >
- start_of_buffer_stats_interval ?
+ start_of_interval = (circ->timestamp_created.tv_sec >
+ start_of_buffer_stats_interval) ?
circ->timestamp_created.tv_sec :
start_of_buffer_stats_interval;
interval_length = (int) (end_of_interval - start_of_interval);
if (interval_length <= 0)
return;
- stat = tor_malloc_zero(sizeof(circ_buffer_stats_t));
- stat->processed_cells = orcirc->processed_cells;
+ processed_cells = orcirc->processed_cells;
/* 1000.0 for s -> ms; 2.0 because of app-ward and exit-ward queues */
- stat->mean_num_cells_in_queue = (double) orcirc->total_cell_waiting_time /
+ mean_num_cells_in_queue = (double) orcirc->total_cell_waiting_time /
(double) interval_length / 1000.0 / 2.0;
- stat->mean_time_cells_in_queue =
+ mean_time_cells_in_queue =
(double) orcirc->total_cell_waiting_time /
(double) orcirc->processed_cells;
- smartlist_add(circuits_for_buffer_stats, stat);
orcirc->total_cell_waiting_time = 0;
orcirc->processed_cells = 0;
+ rep_hist_add_buffer_stats(mean_num_cells_in_queue,
+ mean_time_cells_in_queue,
+ processed_cells);
}
/** Sorting helper: return -1, 1, or 0 based on comparison of two
@@ -2430,136 +2442,536 @@ _buffer_stats_compare_entries(const void **_a, const void **_b)
void
rep_hist_buffer_stats_term(void)
{
- start_of_buffer_stats_interval = 0;
+ rep_hist_reset_buffer_stats(0);
+}
+
+/** Clear history of circuit statistics and set the measurement interval
+ * start to <b>now</b>. */
+void
+rep_hist_reset_buffer_stats(time_t now)
+{
if (!circuits_for_buffer_stats)
circuits_for_buffer_stats = smartlist_create();
SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *,
stat, tor_free(stat));
smartlist_clear(circuits_for_buffer_stats);
+ start_of_buffer_stats_interval = now;
}
-/** Write buffer statistics to $DATADIR/stats/buffer-stats and return when
- * we would next want to write exit stats. */
-time_t
-rep_hist_buffer_stats_write(time_t now)
+/** Return a newly allocated string containing the buffer statistics until
+ * <b>now</b>, or NULL if we're not collecting buffer stats. Caller must
+ * ensure start_of_buffer_stats_interval is in the past. */
+char *
+rep_hist_format_buffer_stats(time_t now)
{
- char *statsdir = NULL, *filename = NULL;
- char written[ISO_TIME_LEN+1];
- open_file_t *open_file = NULL;
- FILE *out;
#define SHARES 10
int processed_cells[SHARES], circs_in_share[SHARES],
number_of_circuits, i;
double queued_cells[SHARES], time_in_queue[SHARES];
- smartlist_t *str_build = NULL;
- char *str = NULL, *buf = NULL;
- circuit_t *circ;
+ char *buf = NULL;
+ smartlist_t *processed_cells_strings, *queued_cells_strings,
+ *time_in_queue_strings;
+ char *processed_cells_string, *queued_cells_string,
+ *time_in_queue_string;
+ char t[ISO_TIME_LEN+1];
+ char *result;
if (!start_of_buffer_stats_interval)
- return 0; /* Not initialized. */
- if (start_of_buffer_stats_interval + WRITE_STATS_INTERVAL > now)
- goto done; /* Not ready to write */
+ return NULL; /* Not initialized. */
- str_build = smartlist_create();
+ tor_assert(now >= start_of_buffer_stats_interval);
- /* add current circuits to stats */
- for (circ = _circuit_get_global_list(); circ; circ = circ->next)
- rep_hist_buffer_stats_add_circ(circ, now);
- /* calculate deciles */
+ /* Calculate deciles if we saw at least one circuit. */
memset(processed_cells, 0, SHARES * sizeof(int));
memset(circs_in_share, 0, SHARES * sizeof(int));
memset(queued_cells, 0, SHARES * sizeof(double));
memset(time_in_queue, 0, SHARES * sizeof(double));
if (!circuits_for_buffer_stats)
circuits_for_buffer_stats = smartlist_create();
- smartlist_sort(circuits_for_buffer_stats,
- _buffer_stats_compare_entries);
number_of_circuits = smartlist_len(circuits_for_buffer_stats);
- if (number_of_circuits < 1) {
- log_info(LD_HIST, "Attempt to write cell statistics to disk failed. "
- "We haven't seen a single circuit to report about.");
- goto done;
+ if (number_of_circuits > 0) {
+ smartlist_sort(circuits_for_buffer_stats,
+ _buffer_stats_compare_entries);
+ i = 0;
+ SMARTLIST_FOREACH_BEGIN(circuits_for_buffer_stats,
+ circ_buffer_stats_t *, stat)
+ {
+ int share = i++ * SHARES / number_of_circuits;
+ processed_cells[share] += stat->processed_cells;
+ queued_cells[share] += stat->mean_num_cells_in_queue;
+ time_in_queue[share] += stat->mean_time_cells_in_queue;
+ circs_in_share[share]++;
+ }
+ SMARTLIST_FOREACH_END(stat);
}
- i = 0;
- SMARTLIST_FOREACH_BEGIN(circuits_for_buffer_stats,
- circ_buffer_stats_t *, stat)
- {
- int share = i++ * SHARES / number_of_circuits;
- processed_cells[share] += stat->processed_cells;
- queued_cells[share] += stat->mean_num_cells_in_queue;
- time_in_queue[share] += stat->mean_time_cells_in_queue;
- circs_in_share[share]++;
- }
- SMARTLIST_FOREACH_END(stat);
- /* clear buffer stats history */
- SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *,
- stat, tor_free(stat));
- smartlist_clear(circuits_for_buffer_stats);
- /* write to file */
- statsdir = get_datadir_fname("stats");
- if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0)
- goto done;
- filename = get_datadir_fname2("stats", "buffer-stats");
- out = start_writing_to_stdio_file(filename, OPEN_FLAGS_APPEND | O_TEXT,
- 0600, &open_file);
- if (!out)
- goto done;
- format_iso_time(written, now);
- if (fprintf(out, "cell-stats-end %s (%d s)\n", written,
- (unsigned) (now - start_of_buffer_stats_interval)) < 0)
- goto done;
+
+ /* Write deciles to strings. */
+ processed_cells_strings = smartlist_create();
+ queued_cells_strings = smartlist_create();
+ time_in_queue_strings = smartlist_create();
for (i = 0; i < SHARES; i++) {
tor_asprintf(&buf,"%d", !circs_in_share[i] ? 0 :
processed_cells[i] / circs_in_share[i]);
- smartlist_add(str_build, buf);
+ smartlist_add(processed_cells_strings, buf);
}
- str = smartlist_join_strings(str_build, ",", 0, NULL);
- if (fprintf(out, "cell-processed-cells %s\n", str) < 0)
- goto done;
- tor_free(str);
- SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
- smartlist_clear(str_build);
for (i = 0; i < SHARES; i++) {
tor_asprintf(&buf, "%.2f", circs_in_share[i] == 0 ? 0.0 :
queued_cells[i] / (double) circs_in_share[i]);
- smartlist_add(str_build, buf);
+ smartlist_add(queued_cells_strings, buf);
}
- str = smartlist_join_strings(str_build, ",", 0, NULL);
- if (fprintf(out, "cell-queued-cells %s\n", str) < 0)
- goto done;
- tor_free(str);
- SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
- smartlist_clear(str_build);
for (i = 0; i < SHARES; i++) {
tor_asprintf(&buf, "%.0f", circs_in_share[i] == 0 ? 0.0 :
time_in_queue[i] / (double) circs_in_share[i]);
- smartlist_add(str_build, buf);
+ smartlist_add(time_in_queue_strings, buf);
}
- str = smartlist_join_strings(str_build, ",", 0, NULL);
- if (fprintf(out, "cell-time-in-queue %s\n", str) < 0)
- goto done;
- tor_free(str);
- SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
- smartlist_free(str_build);
- str_build = NULL;
- if (fprintf(out, "cell-circuits-per-decile %d\n",
- (number_of_circuits + SHARES - 1) / SHARES) < 0)
+
+ /* Join all observations in single strings. */
+ processed_cells_string = smartlist_join_strings(processed_cells_strings,
+ ",", 0, NULL);
+ queued_cells_string = smartlist_join_strings(queued_cells_strings,
+ ",", 0, NULL);
+ time_in_queue_string = smartlist_join_strings(time_in_queue_strings,
+ ",", 0, NULL);
+ SMARTLIST_FOREACH(processed_cells_strings, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(queued_cells_strings, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(time_in_queue_strings, char *, cp, tor_free(cp));
+ smartlist_free(processed_cells_strings);
+ smartlist_free(queued_cells_strings);
+ smartlist_free(time_in_queue_strings);
+
+ /* Put everything together. */
+ format_iso_time(t, now);
+ tor_asprintf(&result, "cell-stats-end %s (%d s)\n"
+ "cell-processed-cells %s\n"
+ "cell-queued-cells %s\n"
+ "cell-time-in-queue %s\n"
+ "cell-circuits-per-decile %d\n",
+ t, (unsigned) (now - start_of_buffer_stats_interval),
+ processed_cells_string,
+ queued_cells_string,
+ time_in_queue_string,
+ (number_of_circuits + SHARES - 1) / SHARES);
+ tor_free(processed_cells_string);
+ tor_free(queued_cells_string);
+ tor_free(time_in_queue_string);
+ return result;
+#undef SHARES
+}
+
+/** If 24 hours have passed since the beginning of the current buffer
+ * stats period, write buffer stats to $DATADIR/stats/buffer-stats
+ * (possibly overwriting an existing file) and reset counters. Return
+ * when we would next want to write buffer stats or 0 if we never want to
+ * write. */
+time_t
+rep_hist_buffer_stats_write(time_t now)
+{
+ circuit_t *circ;
+ char *statsdir = NULL, *filename = NULL, *str = NULL;
+
+ if (!start_of_buffer_stats_interval)
+ return 0; /* Not initialized. */
+ if (start_of_buffer_stats_interval + WRITE_STATS_INTERVAL > now)
+ goto done; /* Not ready to write */
+
+ /* Add open circuits to the history. */
+ for (circ = _circuit_get_global_list(); circ; circ = circ->next) {
+ rep_hist_buffer_stats_add_circ(circ, now);
+ }
+
+ /* Generate history string. */
+ str = rep_hist_format_buffer_stats(now);
+
+ /* Reset both buffer history and counters of open circuits. */
+ rep_hist_reset_buffer_stats(now);
+
+ /* Try to write to disk. */
+ statsdir = get_datadir_fname("stats");
+ if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+ log_warn(LD_HIST, "Unable to create stats/ directory!");
goto done;
- finish_writing_to_file(open_file);
- open_file = NULL;
- start_of_buffer_stats_interval = now;
+ }
+ filename = get_datadir_fname2("stats", "buffer-stats");
+ if (write_str_to_file(filename, str, 0) < 0)
+ log_warn(LD_HIST, "Unable to write buffer stats to disk!");
+
done:
- if (open_file)
- abort_writing_to_file(open_file);
+ tor_free(str);
tor_free(filename);
tor_free(statsdir);
- if (str_build) {
- SMARTLIST_FOREACH(str_build, char *, c, tor_free(c));
- smartlist_free(str_build);
+ return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL;
+}
+
+/*** Descriptor serving statistics ***/
+
+/** Digestmap to track which descriptors were downloaded this stats
+ * collection interval. It maps descriptor digest to pointers to 1,
+ * effectively turning this into a list. */
+static digestmap_t *served_descs = NULL;
+
+/** Number of how many descriptors were downloaded in total during this
+ * interval. */
+static unsigned long total_descriptor_downloads;
+
+/** Start time of served descs stats or 0 if we're not collecting those. */
+static time_t start_of_served_descs_stats_interval;
+
+/** Initialize descriptor stats. */
+void
+rep_hist_desc_stats_init(time_t now)
+{
+ if (served_descs) {
+ log_warn(LD_BUG, "Called rep_hist_desc_stats_init() when desc stats were "
+ "already initialized. This is probably harmless.");
+ return; // Already initialized
}
+ served_descs = digestmap_new();
+ total_descriptor_downloads = 0;
+ start_of_served_descs_stats_interval = now;
+}
+
+/** Reset served descs stats to empty, starting a new interval <b>now</b>. */
+static void
+rep_hist_reset_desc_stats(time_t now)
+{
+ rep_hist_desc_stats_term();
+ rep_hist_desc_stats_init(now);
+}
+
+/** Stop collecting served descs stats, so that rep_hist_desc_stats_init() is
+ * safe to be called again. */
+void
+rep_hist_desc_stats_term(void)
+{
+ digestmap_free(served_descs, NULL);
+ served_descs = NULL;
+ start_of_served_descs_stats_interval = 0;
+ total_descriptor_downloads = 0;
+}
+
+/** Helper for rep_hist_desc_stats_write(). Return a newly allocated string
+ * containing the served desc statistics until now, or NULL if we're not
+ * collecting served desc stats. Caller must ensure that now is not before
+ * start_of_served_descs_stats_interval. */
+static char *
+rep_hist_format_desc_stats(time_t now)
+{
+ char t[ISO_TIME_LEN+1];
+ char *result;
+
+ digestmap_iter_t *iter;
+ const char *key;
+ void *val;
+ unsigned size;
+ int *vals;
+ int n = 0;
+
+ if (!start_of_served_descs_stats_interval)
+ return NULL;
+
+ size = digestmap_size(served_descs);
+ vals = tor_malloc(size * sizeof(int));
+
+ for (iter = digestmap_iter_init(served_descs); !digestmap_iter_done(iter);
+ iter = digestmap_iter_next(served_descs, iter) ) {
+ uintptr_t count;
+ digestmap_iter_get(iter, &key, &val);
+ count = (uintptr_t)val;
+ vals[n++] = (int)count;
+ (void)key;
+ }
+
+ format_iso_time(t, now);
+
+ tor_asprintf(&result,
+ "served-descs-stats-end %s (%d s) total=%lu unique=%u "
+ "max=%d q3=%d md=%d q1=%d min=%d\n",
+ t,
+ (unsigned) (now - start_of_served_descs_stats_interval),
+ total_descriptor_downloads,
+ size,
+ find_nth_int(vals, size, size-1),
+ find_nth_int(vals, size, (3*size-1)/4),
+ find_nth_int(vals, size, (size-1)/2),
+ find_nth_int(vals, size, (size-1)/4),
+ find_nth_int(vals, size, 0));
+
+ tor_free(vals);
+ return result;
+}
+
+/** If WRITE_STATS_INTERVAL seconds have passed since the beginning of
+ * the current served desc stats interval, write the stats to
+ * $DATADIR/stats/served-desc-stats (possibly appending to an existing file)
+ * and reset the state for the next interval. Return when we would next want
+ * to write served desc stats or 0 if we won't want to write. */
+time_t
+rep_hist_desc_stats_write(time_t now)
+{
+ char *statsdir = NULL, *filename = NULL, *str = NULL;
+
+ if (!start_of_served_descs_stats_interval)
+ return 0; /* We're not collecting stats. */
+ if (start_of_served_descs_stats_interval + WRITE_STATS_INTERVAL > now)
+ return start_of_served_descs_stats_interval + WRITE_STATS_INTERVAL;
+
+ str = rep_hist_format_desc_stats(now);
+
+ statsdir = get_datadir_fname("stats");
+ if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+ log_warn(LD_HIST, "Unable to create stats/ directory!");
+ goto done;
+ }
+ filename = get_datadir_fname2("stats", "served-desc-stats");
+ if (append_bytes_to_file(filename, str, strlen(str), 0) < 0)
+ log_warn(LD_HIST, "Unable to write served descs statistics to disk!");
+
+ rep_hist_reset_desc_stats(now);
+
+ done:
+ tor_free(statsdir);
+ tor_free(filename);
tor_free(str);
-#undef SHARES
- return start_of_buffer_stats_interval + WRITE_STATS_INTERVAL;
+ return start_of_served_descs_stats_interval + WRITE_STATS_INTERVAL;
+}
+
+void
+rep_hist_note_desc_served(const char * desc)
+{
+ void *val;
+ uintptr_t count;
+ if (!served_descs)
+ return; // We're not collecting stats
+ val = digestmap_get(served_descs, desc);
+ count = (uintptr_t)val;
+ if (count != INT_MAX)
+ ++count;
+ digestmap_set(served_descs, desc, (void*)count);
+ total_descriptor_downloads++;
+}
+
+/*** Connection statistics ***/
+
+/** Start of the current connection stats interval or 0 if we're not
+ * collecting connection statistics. */
+static time_t start_of_conn_stats_interval;
+
+/** Initialize connection stats. */
+void
+rep_hist_conn_stats_init(time_t now)
+{
+ start_of_conn_stats_interval = now;
+}
+
+/* Count connections that we read and wrote less than these many bytes
+ * from/to as below threshold. */
+#define BIDI_THRESHOLD 20480
+
+/* Count connections that we read or wrote at least this factor as many
+ * bytes from/to than we wrote or read to/from as mostly reading or
+ * writing. */
+#define BIDI_FACTOR 10
+
+/* Interval length in seconds for considering read and written bytes for
+ * connection stats. */
+#define BIDI_INTERVAL 10
+
+/* Start of next BIDI_INTERVAL second interval. */
+static time_t bidi_next_interval = 0;
+
+/* Number of connections that we read and wrote less than BIDI_THRESHOLD
+ * bytes from/to in BIDI_INTERVAL seconds. */
+static uint32_t below_threshold = 0;
+
+/* Number of connections that we read at least BIDI_FACTOR times more
+ * bytes from than we wrote to in BIDI_INTERVAL seconds. */
+static uint32_t mostly_read = 0;
+
+/* Number of connections that we wrote at least BIDI_FACTOR times more
+ * bytes to than we read from in BIDI_INTERVAL seconds. */
+static uint32_t mostly_written = 0;
+
+/* Number of connections that we read and wrote at least BIDI_THRESHOLD
+ * bytes from/to, but not BIDI_FACTOR times more in either direction in
+ * BIDI_INTERVAL seconds. */
+static uint32_t both_read_and_written = 0;
+
+/* Entry in a map from connection ID to the number of read and written
+ * bytes on this connection in a BIDI_INTERVAL second interval. */
+typedef struct bidi_map_entry_t {
+ HT_ENTRY(bidi_map_entry_t) node;
+ uint64_t conn_id; /**< Connection ID */
+ size_t read; /**< Number of read bytes */
+ size_t written; /**< Number of written bytes */
+} bidi_map_entry_t;
+
+/** Map of OR connections together with the number of read and written
+ * bytes in the current BIDI_INTERVAL second interval. */
+static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map =
+ HT_INITIALIZER();
+
+static int
+bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b)
+{
+ return a->conn_id == b->conn_id;
+}
+
+static unsigned
+bidi_map_ent_hash(const bidi_map_entry_t *entry)
+{
+ return (unsigned) entry->conn_id;
+}
+
+HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
+ bidi_map_ent_eq);
+HT_GENERATE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
+ bidi_map_ent_eq, 0.6, malloc, realloc, free);
+
+static void
+bidi_map_free(void)
+{
+ bidi_map_entry_t **ptr, **next, *ent;
+ for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
+ ent = *ptr;
+ next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
+ tor_free(ent);
+ }
+ HT_CLEAR(bidimap, &bidi_map);
+}
+
+/** Reset counters for conn statistics. */
+void
+rep_hist_reset_conn_stats(time_t now)
+{
+ start_of_conn_stats_interval = now;
+ below_threshold = 0;
+ mostly_read = 0;
+ mostly_written = 0;
+ both_read_and_written = 0;
+ bidi_map_free();
+}
+
+/** Stop collecting connection stats in a way that we can re-start doing
+ * so in rep_hist_conn_stats_init(). */
+void
+rep_hist_conn_stats_term(void)
+{
+ rep_hist_reset_conn_stats(0);
+}
+
+/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR
+ * connection <b>conn_id</b> in second <b>when</b>. If this is the first
+ * observation in a new interval, sum up the last observations. Add bytes
+ * for this connection. */
+void
+rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
+ size_t num_written, time_t when)
+{
+ if (!start_of_conn_stats_interval)
+ return;
+ /* Initialize */
+ if (bidi_next_interval == 0)
+ bidi_next_interval = when + BIDI_INTERVAL;
+ /* Sum up last period's statistics */
+ if (when >= bidi_next_interval) {
+ bidi_map_entry_t **ptr, **next, *ent;
+ for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) {
+ ent = *ptr;
+ if (ent->read + ent->written < BIDI_THRESHOLD)
+ below_threshold++;
+ else if (ent->read >= ent->written * BIDI_FACTOR)
+ mostly_read++;
+ else if (ent->written >= ent->read * BIDI_FACTOR)
+ mostly_written++;
+ else
+ both_read_and_written++;
+ next = HT_NEXT_RMV(bidimap, &bidi_map, ptr);
+ tor_free(ent);
+ }
+ while (when >= bidi_next_interval)
+ bidi_next_interval += BIDI_INTERVAL;
+ log_info(LD_GENERAL, "%d below threshold, %d mostly read, "
+ "%d mostly written, %d both read and written.",
+ below_threshold, mostly_read, mostly_written,
+ both_read_and_written);
+ }
+ /* Add this connection's bytes. */
+ if (num_read > 0 || num_written > 0) {
+ bidi_map_entry_t *entry, lookup;
+ lookup.conn_id = conn_id;
+ entry = HT_FIND(bidimap, &bidi_map, &lookup);
+ if (entry) {
+ entry->written += num_written;
+ entry->read += num_read;
+ } else {
+ entry = tor_malloc_zero(sizeof(bidi_map_entry_t));
+ entry->conn_id = conn_id;
+ entry->written = num_written;
+ entry->read = num_read;
+ HT_INSERT(bidimap, &bidi_map, entry);
+ }
+ }
+}
+
+/** Return a newly allocated string containing the connection statistics
+ * until <b>now</b>, or NULL if we're not collecting conn stats. Caller must
+ * ensure start_of_conn_stats_interval is in the past. */
+char *
+rep_hist_format_conn_stats(time_t now)
+{
+ char *result, written[ISO_TIME_LEN+1];
+
+ if (!start_of_conn_stats_interval)
+ return NULL; /* Not initialized. */
+
+ tor_assert(now >= start_of_conn_stats_interval);
+
+ format_iso_time(written, now);
+ tor_asprintf(&result, "conn-bi-direct %s (%d s) %d,%d,%d,%d\n",
+ written,
+ (unsigned) (now - start_of_conn_stats_interval),
+ below_threshold,
+ mostly_read,
+ mostly_written,
+ both_read_and_written);
+ return result;
+}
+
+/** If 24 hours have passed since the beginning of the current conn stats
+ * period, write conn stats to $DATADIR/stats/conn-stats (possibly
+ * overwriting an existing file) and reset counters. Return when we would
+ * next want to write conn stats or 0 if we never want to write. */
+time_t
+rep_hist_conn_stats_write(time_t now)
+{
+ char *statsdir = NULL, *filename = NULL, *str = NULL;
+
+ if (!start_of_conn_stats_interval)
+ return 0; /* Not initialized. */
+ if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now)
+ goto done; /* Not ready to write */
+
+ /* Generate history string. */
+ str = rep_hist_format_conn_stats(now);
+
+ /* Reset counters. */
+ rep_hist_reset_conn_stats(now);
+
+ /* Try to write to disk. */
+ statsdir = get_datadir_fname("stats");
+ if (check_private_dir(statsdir, CPD_CREATE, get_options()->User) < 0) {
+ log_warn(LD_HIST, "Unable to create stats/ directory!");
+ goto done;
+ }
+ filename = get_datadir_fname2("stats", "conn-stats");
+ if (write_str_to_file(filename, str, 0) < 0)
+ log_warn(LD_HIST, "Unable to write conn stats to disk!");
+
+ done:
+ tor_free(str);
+ tor_free(filename);
+ tor_free(statsdir);
+ return start_of_conn_stats_interval + WRITE_STATS_INTERVAL;
}
/** Free all storage held by the OR/link history caches, by the
@@ -2576,11 +2988,15 @@ rep_hist_free_all(void)
tor_free(exit_streams);
built_last_stability_doc_at = 0;
predicted_ports_free();
+ bidi_map_free();
+
if (circuits_for_buffer_stats) {
SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s,
tor_free(s));
smartlist_free(circuits_for_buffer_stats);
circuits_for_buffer_stats = NULL;
}
+ rep_hist_desc_stats_term();
+ total_descriptor_downloads = 0;
}
diff --git a/src/or/rephist.h b/src/or/rephist.h
index b06a39ed5..0a3e46ae1 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -77,6 +77,23 @@ void rep_hist_buffer_stats_add_circ(circuit_t *circ,
time_t end_of_interval);
time_t rep_hist_buffer_stats_write(time_t now);
void rep_hist_buffer_stats_term(void);
+void rep_hist_add_buffer_stats(double mean_num_cells_in_queue,
+ double mean_time_cells_in_queue, uint32_t processed_cells);
+char *rep_hist_format_buffer_stats(time_t now);
+void rep_hist_reset_buffer_stats(time_t now);
+
+void rep_hist_desc_stats_init(time_t now);
+void rep_hist_note_desc_served(const char * desc);
+void rep_hist_desc_stats_term(void);
+time_t rep_hist_desc_stats_write(time_t now);
+
+void rep_hist_conn_stats_init(time_t now);
+void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read,
+ size_t num_written, time_t when);
+void rep_hist_reset_conn_stats(time_t now);
+char *rep_hist_format_conn_stats(time_t now);
+time_t rep_hist_conn_stats_write(time_t now);
+void rep_hist_conn_stats_term(void);
#endif
diff --git a/src/or/router.c b/src/or/router.c
index 365e888af..c9f141b7f 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -7,6 +7,7 @@
#define ROUTER_PRIVATE
#include "or.h"
+#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
@@ -19,6 +20,7 @@
#include "hibernate.h"
#include "main.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "policies.h"
#include "relay.h"
#include "rephist.h"
@@ -82,6 +84,11 @@ static authority_cert_t *legacy_key_certificate = NULL;
static void
set_onion_key(crypto_pk_env_t *k)
{
+ if (onionkey && !crypto_pk_cmp_keys(onionkey, k)) {
+ /* k is already our onion key; free it and return */
+ crypto_free_pk_env(k);
+ return;
+ }
tor_mutex_acquire(key_lock);
crypto_free_pk_env(onionkey);
onionkey = k;
@@ -149,8 +156,8 @@ assert_identity_keys_ok(void)
} else {
/* assert that we have set the client and server keys to be unequal */
if (server_identitykey)
- tor_assert(0!=crypto_pk_cmp_keys(client_identitykey,
- server_identitykey));
+ tor_assert(0!=crypto_pk_cmp_keys(client_identitykey,
+ server_identitykey));
}
}
@@ -493,8 +500,8 @@ init_keys(void)
char digest[DIGEST_LEN];
char v3_digest[DIGEST_LEN];
char *cp;
- or_options_t *options = get_options();
- authority_type_t type;
+ const or_options_t *options = get_options();
+ dirinfo_type_t type;
time_t now = time(NULL);
trusted_dir_server_t *ds;
int v3_digest_set = 0;
@@ -695,11 +702,12 @@ init_keys(void)
}
/* 6b. [authdirserver only] add own key to approved directories. */
crypto_pk_get_digest(get_server_identity_key(), digest);
- type = ((options->V1AuthoritativeDir ? V1_AUTHORITY : NO_AUTHORITY) |
- (options->V2AuthoritativeDir ? V2_AUTHORITY : NO_AUTHORITY) |
- (options->V3AuthoritativeDir ? V3_AUTHORITY : NO_AUTHORITY) |
- (options->BridgeAuthoritativeDir ? BRIDGE_AUTHORITY : NO_AUTHORITY) |
- (options->HSAuthoritativeDir ? HIDSERV_AUTHORITY : NO_AUTHORITY));
+ type = ((options->V1AuthoritativeDir ? V1_DIRINFO : NO_DIRINFO) |
+ (options->V2AuthoritativeDir ? V2_DIRINFO : NO_DIRINFO) |
+ (options->V3AuthoritativeDir ?
+ (V3_DIRINFO|MICRODESC_DIRINFO|EXTRAINFO_DIRINFO) : NO_DIRINFO) |
+ (options->BridgeAuthoritativeDir ? BRIDGE_DIRINFO : NO_DIRINFO) |
+ (options->HSAuthoritativeDir ? HIDSERV_DIRINFO : NO_DIRINFO));
ds = router_get_trusteddirserver_by_digest(digest);
if (!ds) {
@@ -721,7 +729,7 @@ init_keys(void)
type, ds->type);
ds->type = type;
}
- if (v3_digest_set && (ds->type & V3_AUTHORITY) &&
+ if (v3_digest_set && (ds->type & V3_DIRINFO) &&
tor_memneq(v3_digest, ds->v3_identity_digest, DIGEST_LEN)) {
log_warn(LD_DIR, "V3 identity key does not match identity declared in "
"DirServer line. Adjusting.");
@@ -760,7 +768,7 @@ router_reset_reachability(void)
int
check_whether_orport_reachable(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
return options->AssumeReachable ||
can_reach_or_port;
}
@@ -769,7 +777,7 @@ check_whether_orport_reachable(void)
int
check_whether_dirport_reachable(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
return !options->DirPort ||
options->AssumeReachable ||
we_are_hibernating() ||
@@ -784,7 +792,7 @@ check_whether_dirport_reachable(void)
* a DirPort.
*/
static int
-decide_to_advertise_dirport(or_options_t *options, uint16_t dir_port)
+decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
{
static int advertising=1; /* start out assuming we will advertise */
int new_choice=1;
@@ -849,14 +857,14 @@ decide_to_advertise_dirport(or_options_t *options, uint16_t dir_port)
void
consider_testing_reachability(int test_or, int test_dir)
{
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
int orport_reachable = check_whether_orport_reachable();
tor_addr_t addr;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (!me)
return;
- if (routerset_contains_router(options->ExcludeNodes, me) &&
+ if (routerset_contains_router(options->ExcludeNodes, me, -1) &&
options->StrictNodes) {
/* If we've excluded ourself, and StrictNodes is set, we can't test
* ourself. */
@@ -876,11 +884,14 @@ consider_testing_reachability(int test_or, int test_dir)
}
if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) {
+ extend_info_t *ei;
log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.",
!orport_reachable ? "reachability" : "bandwidth",
me->address, me->or_port);
- circuit_launch_by_router(CIRCUIT_PURPOSE_TESTING, me,
- CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
+ ei = extend_info_from_router(me);
+ circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei,
+ CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
+ extend_info_free(ei);
}
tor_addr_from_ipv4h(&addr, me->addr);
@@ -903,11 +914,11 @@ consider_testing_reachability(int test_or, int test_dir)
void
router_orport_found_reachable(void)
{
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
if (!can_reach_or_port && me) {
log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from "
"the outside. Excellent.%s",
- get_options()->_PublishServerDescriptor != NO_AUTHORITY ?
+ get_options()->_PublishServerDescriptor != NO_DIRINFO ?
" Publishing server descriptor." : "");
can_reach_or_port = 1;
mark_my_descriptor_dirty("ORPort found reachable");
@@ -921,7 +932,7 @@ router_orport_found_reachable(void)
void
router_dirport_found_reachable(void)
{
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
if (!can_reach_dir_port && me) {
log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable "
"from the outside. Excellent.");
@@ -967,7 +978,7 @@ router_perform_bandwidth_test(int num_circs, time_t now)
* directory server.
*/
int
-authdir_mode(or_options_t *options)
+authdir_mode(const or_options_t *options)
{
return options->AuthoritativeDir != 0;
}
@@ -975,7 +986,7 @@ authdir_mode(or_options_t *options)
* directory server.
*/
int
-authdir_mode_v1(or_options_t *options)
+authdir_mode_v1(const or_options_t *options)
{
return authdir_mode(options) && options->V1AuthoritativeDir != 0;
}
@@ -983,7 +994,7 @@ authdir_mode_v1(or_options_t *options)
* directory server.
*/
int
-authdir_mode_v2(or_options_t *options)
+authdir_mode_v2(const or_options_t *options)
{
return authdir_mode(options) && options->V2AuthoritativeDir != 0;
}
@@ -991,13 +1002,13 @@ authdir_mode_v2(or_options_t *options)
* directory server.
*/
int
-authdir_mode_v3(or_options_t *options)
+authdir_mode_v3(const or_options_t *options)
{
return authdir_mode(options) && options->V3AuthoritativeDir != 0;
}
/** Return true iff we are a v1, v2, or v3 directory authority. */
int
-authdir_mode_any_main(or_options_t *options)
+authdir_mode_any_main(const or_options_t *options)
{
return options->V1AuthoritativeDir ||
options->V2AuthoritativeDir ||
@@ -1006,7 +1017,7 @@ authdir_mode_any_main(or_options_t *options)
/** Return true if we believe ourselves to be any kind of
* authoritative directory beyond just a hidserv authority. */
int
-authdir_mode_any_nonhidserv(or_options_t *options)
+authdir_mode_any_nonhidserv(const or_options_t *options)
{
return options->BridgeAuthoritativeDir ||
authdir_mode_any_main(options);
@@ -1015,7 +1026,7 @@ authdir_mode_any_nonhidserv(or_options_t *options)
* authoritative about receiving and serving descriptors of type
* <b>purpose</b> its dirport. Use -1 for "any purpose". */
int
-authdir_mode_handles_descs(or_options_t *options, int purpose)
+authdir_mode_handles_descs(const or_options_t *options, int purpose)
{
if (purpose < 0)
return authdir_mode_any_nonhidserv(options);
@@ -1030,7 +1041,7 @@ authdir_mode_handles_descs(or_options_t *options, int purpose)
* publishes its own network statuses.
*/
int
-authdir_mode_publishes_statuses(or_options_t *options)
+authdir_mode_publishes_statuses(const or_options_t *options)
{
if (authdir_mode_bridge(options))
return 0;
@@ -1040,7 +1051,7 @@ authdir_mode_publishes_statuses(or_options_t *options)
* tests reachability of the descriptors it learns about.
*/
int
-authdir_mode_tests_reachability(or_options_t *options)
+authdir_mode_tests_reachability(const or_options_t *options)
{
return authdir_mode_handles_descs(options, -1);
}
@@ -1048,7 +1059,7 @@ authdir_mode_tests_reachability(or_options_t *options)
* directory server.
*/
int
-authdir_mode_bridge(or_options_t *options)
+authdir_mode_bridge(const or_options_t *options)
{
return authdir_mode(options) && options->BridgeAuthoritativeDir != 0;
}
@@ -1056,7 +1067,7 @@ authdir_mode_bridge(or_options_t *options)
/** Return true iff we are trying to be a server.
*/
int
-server_mode(or_options_t *options)
+server_mode(const or_options_t *options)
{
if (options->ClientOnly) return 0;
return (options->ORPort != 0 || options->ORListenAddress);
@@ -1065,7 +1076,7 @@ server_mode(or_options_t *options)
/** Return true iff we are trying to be a non-bridge server.
*/
int
-public_server_mode(or_options_t *options)
+public_server_mode(const or_options_t *options)
{
if (!server_mode(options)) return 0;
return (!options->BridgeRelay);
@@ -1075,10 +1086,10 @@ public_server_mode(or_options_t *options)
* in the consensus mean that we don't want to allow exits from circuits
* we got from addresses not known to be servers. */
int
-should_refuse_unknown_exits(or_options_t *options)
+should_refuse_unknown_exits(const or_options_t *options)
{
- if (options->RefuseUnknownExits_ != -1) {
- return options->RefuseUnknownExits_;
+ if (options->RefuseUnknownExits != -1) {
+ return options->RefuseUnknownExits;
} else {
return networkstatus_get_param(NULL, "refuseunknownexits", 1, 0, 1);
}
@@ -1105,14 +1116,12 @@ set_server_advertised(int s)
server_is_advertised = s;
}
-/** Return true iff we are trying to be a socks proxy. */
+/** Return true iff we are trying to proxy client connections. */
int
-proxy_mode(or_options_t *options)
+proxy_mode(const or_options_t *options)
{
- return (options->SocksPort != 0 ||
- options->TransPort != 0 ||
- options->NATDPort != 0 ||
- options->DNSPort != 0);
+ (void)options;
+ return smartlist_len(get_configured_client_ports()) > 0;
}
/** Decide if we're a publishable server. We are a publishable server if:
@@ -1128,11 +1137,11 @@ proxy_mode(or_options_t *options)
static int
decide_if_publishable_server(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (options->ClientOnly)
return 0;
- if (options->_PublishServerDescriptor == NO_AUTHORITY)
+ if (options->_PublishServerDescriptor == NO_DIRINFO)
return 0;
if (!server_mode(options))
return 0;
@@ -1173,7 +1182,7 @@ consider_publishable_server(int force)
* the one configured in the ORPort option, or the one we actually bound to
* if ORPort is "auto". */
uint16_t
-router_get_advertised_or_port(or_options_t *options)
+router_get_advertised_or_port(const or_options_t *options)
{
if (options->ORPort == CFG_AUTO_PORT) {
connection_t *c = connection_get_by_type(CONN_TYPE_OR_LISTENER);
@@ -1190,7 +1199,7 @@ router_get_advertised_or_port(or_options_t *options)
* the one configured in the DirPort option,
* or the one we actually bound to if DirPort is "auto". */
uint16_t
-router_get_advertised_dir_port(or_options_t *options, uint16_t dirport)
+router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport)
{
if (!options->DirPort)
return dirport;
@@ -1211,9 +1220,14 @@ router_get_advertised_dir_port(or_options_t *options, uint16_t dirport)
static routerinfo_t *desc_routerinfo = NULL;
/** My extrainfo */
static extrainfo_t *desc_extrainfo = NULL;
+/** Why did we most recently decide to regenerate our descriptor? Used to
+ * tell the authorities why we're sending it to them. */
+static const char *desc_gen_reason = NULL;
/** Since when has our descriptor been "clean"? 0 if we need to regenerate it
* now. */
static time_t desc_clean_since = 0;
+/** Why did we mark the descriptor dirty? */
+static const char *desc_dirty_reason = NULL;
/** Boolean: do we need to regenerate the above? */
static int desc_needs_upload = 0;
@@ -1224,11 +1238,11 @@ static int desc_needs_upload = 0;
void
router_upload_dir_desc_to_dirservers(int force)
{
- routerinfo_t *ri;
+ const routerinfo_t *ri;
extrainfo_t *ei;
char *msg;
size_t desc_len, extra_len = 0, total_len;
- authority_type_t auth = get_options()->_PublishServerDescriptor;
+ dirinfo_type_t auth = get_options()->_PublishServerDescriptor;
ri = router_get_my_routerinfo();
if (!ri) {
@@ -1236,7 +1250,7 @@ router_upload_dir_desc_to_dirservers(int force)
return;
}
ei = router_get_my_extrainfo();
- if (auth == NO_AUTHORITY)
+ if (auth == NO_DIRINFO)
return;
if (!force && !desc_needs_upload)
return;
@@ -1257,7 +1271,7 @@ router_upload_dir_desc_to_dirservers(int force)
msg[desc_len+extra_len] = 0;
directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_DIR,
- (auth & BRIDGE_AUTHORITY) ?
+ (auth & BRIDGE_DIRINFO) ?
ROUTER_PURPOSE_BRIDGE :
ROUTER_PURPOSE_GENERAL,
auth, msg, desc_len, extra_len);
@@ -1322,7 +1336,7 @@ router_extrainfo_digest_is_me(const char *digest)
/** A wrapper around router_digest_is_me(). */
int
-router_is_me(routerinfo_t *router)
+router_is_me(const routerinfo_t *router)
{
return router_digest_is_me(router->cache_info.identity_digest);
}
@@ -1341,7 +1355,7 @@ router_fingerprint_is_me(const char *fp)
/** Return a routerinfo for this OR, rebuilding a fresh one if
* necessary. Return NULL on error, or if called on an OP. */
-routerinfo_t *
+const routerinfo_t *
router_get_my_routerinfo(void)
{
if (!server_mode(get_options()))
@@ -1380,6 +1394,14 @@ router_get_my_extrainfo(void)
return desc_extrainfo;
}
+/** Return a human-readable string describing what triggered us to generate
+ * our current descriptor, or NULL if we don't know. */
+const char *
+router_get_descriptor_gen_reason(void)
+{
+ return desc_gen_reason;
+}
+
/** A list of nicknames that we've warned about including in our family
* declaration verbatim rather than as digests. */
static smartlist_t *warned_nonexistent_family = NULL;
@@ -1391,10 +1413,8 @@ static int router_guess_address_from_dir_headers(uint32_t *guess);
* dirserver headers. Place the answer in *<b>addr</b> and return
* 0 on success, else return -1 if we have no guess. */
int
-router_pick_published_address(or_options_t *options, uint32_t *addr)
+router_pick_published_address(const or_options_t *options, uint32_t *addr)
{
- char buf[INET_NTOA_BUF_LEN];
- struct in_addr a;
if (resolve_my_address(LOG_INFO, options, addr, NULL) < 0) {
log_info(LD_CONFIG, "Could not determine our address locally. "
"Checking if directory headers provide any hints.");
@@ -1404,9 +1424,7 @@ router_pick_published_address(or_options_t *options, uint32_t *addr)
return -1;
}
}
- a.s_addr = htonl(*addr);
- tor_inet_ntoa(&a, buf, sizeof(buf));
- log_info(LD_CONFIG,"Success: chose address '%s'.", buf);
+ log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr));
return 0;
}
@@ -1422,7 +1440,7 @@ router_rebuild_descriptor(int force)
uint32_t addr;
char platform[256];
int hibernating = we_are_hibernating();
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (desc_clean_since && !force)
return 0;
@@ -1476,13 +1494,12 @@ router_rebuild_descriptor(int force)
ri->policy_is_reject_star =
policy_is_reject_star(ri->exit_policy);
- if (desc_routerinfo) { /* inherit values */
- ri->is_valid = desc_routerinfo->is_valid;
- ri->is_running = desc_routerinfo->is_running;
- ri->is_named = desc_routerinfo->is_named;
- }
+#if 0
+ /* XXXX NM NM I belive this is safe to remove */
if (authdir_mode(options))
ri->is_valid = ri->is_named = 1; /* believe in yourself */
+#endif
+
if (options->MyFamily) {
smartlist_t *family;
if (!warned_nonexistent_family)
@@ -1491,13 +1508,12 @@ router_rebuild_descriptor(int force)
ri->declared_family = smartlist_create();
smartlist_split_string(family, options->MyFamily, ",",
SPLIT_SKIP_SPACE|SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH(family, char *, name,
- {
- routerinfo_t *member;
+ SMARTLIST_FOREACH_BEGIN(family, char *, name) {
+ const node_t *member;
if (!strcasecmp(name, options->Nickname))
- member = ri;
+ goto skip; /* Don't list ourself, that's redundant */
else
- member = router_get_by_nickname(name, 1);
+ member = node_get_by_nickname(name, 1);
if (!member) {
int is_legal = is_legal_nickname_or_hexdigest(name);
if (!smartlist_string_isin(warned_nonexistent_family, name) &&
@@ -1517,19 +1533,21 @@ router_rebuild_descriptor(int force)
smartlist_add(ri->declared_family, name);
name = NULL;
}
- } else if (router_is_me(member)) {
+ } else if (router_digest_is_me(member->identity)) {
/* Don't list ourself in our own family; that's redundant */
+ /* XXX shouldn't be possible */
} else {
char *fp = tor_malloc(HEX_DIGEST_LEN+2);
fp[0] = '$';
base16_encode(fp+1,HEX_DIGEST_LEN+1,
- member->cache_info.identity_digest, DIGEST_LEN);
+ member->identity, DIGEST_LEN);
smartlist_add(ri->declared_family, fp);
if (smartlist_string_isin(warned_nonexistent_family, name))
smartlist_string_remove(warned_nonexistent_family, name);
}
+ skip:
tor_free(name);
- });
+ } SMARTLIST_FOREACH_END(name);
/* remove duplicates from the list */
smartlist_sort_strings(ri->declared_family);
@@ -1590,8 +1608,6 @@ router_rebuild_descriptor(int force)
strlen(ri->cache_info.signed_descriptor_body),
ri->cache_info.signed_descriptor_digest);
- routerinfo_set_country(ri);
-
if (ei) {
tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL));
}
@@ -1603,16 +1619,56 @@ router_rebuild_descriptor(int force)
desc_clean_since = time(NULL);
desc_needs_upload = 1;
+ desc_gen_reason = desc_dirty_reason;
+ desc_dirty_reason = NULL;
control_event_my_descriptor_changed();
return 0;
}
-/** Mark descriptor out of date if it's older than <b>when</b> */
+/** If our router descriptor ever goes this long without being regenerated
+ * because something changed, we force an immediate regenerate-and-upload. */
+#define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (18*60*60)
+
+/** If our router descriptor seems to be missing or unacceptable according
+ * to the authorities, regenerate and reupload it _this_ often. */
+#define FAST_RETRY_DESCRIPTOR_INTERVAL (90*60)
+
+/** Mark descriptor out of date if it's been "too long" since we last tried
+ * to upload one. */
void
-mark_my_descriptor_dirty_if_older_than(time_t when)
+mark_my_descriptor_dirty_if_too_old(time_t now)
{
- if (desc_clean_since < when)
+ networkstatus_t *ns;
+ const routerstatus_t *rs;
+ const char *retry_fast_reason = NULL; /* Set if we should retry frequently */
+ const time_t slow_cutoff = now - FORCE_REGENERATE_DESCRIPTOR_INTERVAL;
+ const time_t fast_cutoff = now - FAST_RETRY_DESCRIPTOR_INTERVAL;
+
+ /* If it's already dirty, don't mark it. */
+ if (! desc_clean_since)
+ return;
+
+ /* If it's older than FORCE_REGENERATE_DESCRIPTOR_INTERVAL, it's always
+ * time to rebuild it. */
+ if (desc_clean_since < slow_cutoff) {
mark_my_descriptor_dirty("time for new descriptor");
+ return;
+ }
+ /* Now we see whether we want to be retrying frequently or no. The
+ * rule here is that we'll retry frequently if we aren't listed in the
+ * live consensus we have, or if the publication time of the
+ * descriptor listed for us in the consensus is very old. */
+ ns = networkstatus_get_live_consensus(now);
+ if (ns) {
+ rs = networkstatus_vote_find_entry(ns, server_identitykey_digest);
+ if (rs == NULL)
+ retry_fast_reason = "not listed in consensus";
+ else if (rs->published_on < slow_cutoff)
+ retry_fast_reason = "version listed in consensus is quite old";
+ }
+
+ if (retry_fast_reason && desc_clean_since < fast_cutoff)
+ mark_my_descriptor_dirty(retry_fast_reason);
}
/** Call when the current descriptor is out of date. */
@@ -1621,6 +1677,8 @@ mark_my_descriptor_dirty(const char *reason)
{
desc_clean_since = 0;
log_info(LD_OR, "Decided to publish new relay descriptor: %s", reason);
+ if (!desc_dirty_reason)
+ desc_dirty_reason = reason;
}
/** How frequently will we republish our descriptor because of large (factor
@@ -1686,7 +1744,7 @@ void
check_descriptor_ipaddress_changed(time_t now)
{
uint32_t prev, cur;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
(void) now;
if (!desc_routerinfo)
@@ -1718,7 +1776,7 @@ router_new_address_suggestion(const char *suggestion,
{
uint32_t addr, cur = 0;
struct in_addr in;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
/* first, learn what the IP address actually is */
if (!tor_inet_aton(suggestion, &in)) {
@@ -1817,7 +1875,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
int result=0;
addr_policy_t *tmpe;
char *family_line;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
/* Make sure the identity key matches the one in the routerinfo. */
if (crypto_pk_cmp_keys(ident_key, router->identity_pkey)) {
@@ -2059,7 +2117,7 @@ int
extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
crypto_pk_env_t *ident_key)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
char identity[HEX_DIGEST_LEN+1];
char published[ISO_TIME_LEN+1];
char digest[DIGEST_LEN];
@@ -2083,6 +2141,12 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
tor_free(bandwidth_usage);
smartlist_add(chunks, pre);
+ if (geoip_is_loaded()) {
+ char *chunk=NULL;
+ tor_asprintf(&chunk, "geoip-db-digest %s\n", geoip_db_digest());
+ smartlist_add(chunks, chunk);
+ }
+
if (options->ExtraInfoStatistics && write_stats_to_extrainfo) {
log_info(LD_GENERAL, "Adding stats to extra-info descriptor.");
if (options->DirReqStatistics &&
@@ -2105,6 +2169,11 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
"exit-stats-end", now, &contents) > 0) {
smartlist_add(chunks, contents);
}
+ if (options->ConnDirectionStatistics &&
+ load_stats_file("stats"PATH_SEPARATOR"conn-stats",
+ "conn-bi-direct", now, &contents) > 0) {
+ smartlist_add(chunks, contents);
+ }
}
if (should_record_bridge_info(options) && write_stats_to_extrainfo) {
@@ -2291,13 +2360,45 @@ router_get_description(char *buf, const routerinfo_t *ri)
return "<null>";
return format_node_description(buf,
ri->cache_info.identity_digest,
- ri->is_named,
+ router_is_named(ri),
ri->nickname,
NULL,
ri->addr);
}
/** Use <b>buf</b> (which must be at least NODE_DESC_BUF_LEN bytes long) to
+ * hold a human-readable description of <b>node</b>.
+ *
+ * Return a pointer to the front of <b>buf</b>.
+ */
+const char *
+node_get_description(char *buf, const node_t *node)
+{
+ const char *nickname = NULL;
+ uint32_t addr32h = 0;
+ int is_named = 0;
+
+ if (!node)
+ return "<null>";
+
+ if (node->rs) {
+ nickname = node->rs->nickname;
+ is_named = node->rs->is_named;
+ addr32h = node->rs->addr;
+ } else if (node->ri) {
+ nickname = node->ri->nickname;
+ addr32h = node->ri->addr;
+ }
+
+ return format_node_description(buf,
+ node->identity,
+ is_named,
+ nickname,
+ NULL,
+ addr32h);
+}
+
+/** Use <b>buf</b> (which must be at least NODE_DESC_BUF_LEN bytes long) to
* hold a human-readable description of <b>rs</b>.
*
* Return a pointer to the front of <b>buf</b>.
@@ -2345,6 +2446,18 @@ router_describe(const routerinfo_t *ri)
return router_get_description(buf, ri);
}
+/** Return a human-readable description of the node_t <b>node</b>.
+ *
+ * This function is not thread-safe. Each call to this function invalidates
+ * previous values returned by this function.
+ */
+const char *
+node_describe(const node_t *node)
+{
+ static char buf[NODE_DESC_BUF_LEN];
+ return node_get_description(buf, node);
+}
+
/** Return a human-readable description of the routerstatus_t <b>rs</b>.
*
* This function is not thread-safe. Each call to this function invalidates
@@ -2379,10 +2492,15 @@ extend_info_describe(const extend_info_t *ei)
void
router_get_verbose_nickname(char *buf, const routerinfo_t *router)
{
+ const char *good_digest = networkstatus_get_router_digest_by_nickname(
+ router->nickname);
+ int is_named = good_digest && tor_memeq(good_digest,
+ router->cache_info.identity_digest,
+ DIGEST_LEN);
buf[0] = '$';
base16_encode(buf+1, HEX_DIGEST_LEN+1, router->cache_info.identity_digest,
DIGEST_LEN);
- buf[1+HEX_DIGEST_LEN] = router->is_named ? '=' : '~';
+ buf[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
strlcpy(buf+1+HEX_DIGEST_LEN+1, router->nickname, MAX_NICKNAME_LEN+1);
}
diff --git a/src/or/router.h b/src/or/router.h
index 3733099f9..f9d156cb0 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -39,30 +39,30 @@ void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now);
-int authdir_mode(or_options_t *options);
-int authdir_mode_v1(or_options_t *options);
-int authdir_mode_v2(or_options_t *options);
-int authdir_mode_v3(or_options_t *options);
-int authdir_mode_any_main(or_options_t *options);
-int authdir_mode_any_nonhidserv(or_options_t *options);
-int authdir_mode_handles_descs(or_options_t *options, int purpose);
-int authdir_mode_publishes_statuses(or_options_t *options);
-int authdir_mode_tests_reachability(or_options_t *options);
-int authdir_mode_bridge(or_options_t *options);
+int authdir_mode(const or_options_t *options);
+int authdir_mode_v1(const or_options_t *options);
+int authdir_mode_v2(const or_options_t *options);
+int authdir_mode_v3(const or_options_t *options);
+int authdir_mode_any_main(const or_options_t *options);
+int authdir_mode_any_nonhidserv(const or_options_t *options);
+int authdir_mode_handles_descs(const or_options_t *options, int purpose);
+int authdir_mode_publishes_statuses(const or_options_t *options);
+int authdir_mode_tests_reachability(const or_options_t *options);
+int authdir_mode_bridge(const or_options_t *options);
-uint16_t router_get_advertised_or_port(or_options_t *options);
-uint16_t router_get_advertised_dir_port(or_options_t *options,
+uint16_t router_get_advertised_or_port(const or_options_t *options);
+uint16_t router_get_advertised_dir_port(const or_options_t *options,
uint16_t dirport);
-int server_mode(or_options_t *options);
-int public_server_mode(or_options_t *options);
+int server_mode(const or_options_t *options);
+int public_server_mode(const or_options_t *options);
int advertised_server_mode(void);
-int proxy_mode(or_options_t *options);
+int proxy_mode(const or_options_t *options);
void consider_publishable_server(int force);
-int should_refuse_unknown_exits(or_options_t *options);
+int should_refuse_unknown_exits(const or_options_t *options);
void router_upload_dir_desc_to_dirservers(int force);
-void mark_my_descriptor_dirty_if_older_than(time_t when);
+void mark_my_descriptor_dirty_if_too_old(time_t now);
void mark_my_descriptor_dirty(const char *reason);
void check_descriptor_bandwidth_changed(time_t now);
void check_descriptor_ipaddress_changed(time_t now);
@@ -70,14 +70,15 @@ void router_new_address_suggestion(const char *suggestion,
const dir_connection_t *d_conn);
int router_compare_to_my_exit_policy(edge_connection_t *conn);
int router_my_exit_policy_is_reject_star(void);
-routerinfo_t *router_get_my_routerinfo(void);
+const routerinfo_t *router_get_my_routerinfo(void);
extrainfo_t *router_get_my_extrainfo(void);
const char *router_get_my_descriptor(void);
+const char *router_get_descriptor_gen_reason(void);
int router_digest_is_me(const char *digest);
int router_extrainfo_digest_is_me(const char *digest);
-int router_is_me(routerinfo_t *router);
+int router_is_me(const routerinfo_t *router);
int router_fingerprint_is_me(const char *fp);
-int router_pick_published_address(or_options_t *options, uint32_t *addr);
+int router_pick_published_address(const or_options_t *options, uint32_t *addr);
int router_rebuild_descriptor(int force);
int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
crypto_pk_env_t *ident_key);
@@ -102,9 +103,11 @@ const char *format_node_description(char *buf,
const tor_addr_t *addr,
uint32_t addr32h);
const char *router_get_description(char *buf, const routerinfo_t *ri);
+const char *node_get_description(char *buf, const node_t *node);
const char *routerstatus_get_description(char *buf, const routerstatus_t *rs);
const char *extend_info_get_description(char *buf, const extend_info_t *ei);
const char *router_describe(const routerinfo_t *ri);
+const char *node_describe(const node_t *node);
const char *routerstatus_describe(const routerstatus_t *ri);
const char *extend_info_describe(const extend_info_t *ei);
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index f8df089a8..d97b978f4 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -22,7 +22,9 @@
#include "geoip.h"
#include "hibernate.h"
#include "main.h"
+#include "microdesc.h"
#include "networkstatus.h"
+#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
#include "rendcommon.h"
@@ -37,22 +39,25 @@
/****************************************************************************/
/* static function prototypes */
-static routerstatus_t *router_pick_directory_server_impl(
- authority_type_t auth, int flags);
-static routerstatus_t *router_pick_trusteddirserver_impl(
- authority_type_t auth, int flags, int *n_busy_out);
+static const routerstatus_t *router_pick_directory_server_impl(
+ dirinfo_type_t auth, int flags);
+static const routerstatus_t *router_pick_trusteddirserver_impl(
+ dirinfo_type_t auth, int flags, int *n_busy_out);
static void mark_all_trusteddirservers_up(void);
-static int router_nickname_matches(routerinfo_t *router, const char *nickname);
+static int router_nickname_matches(const routerinfo_t *router,
+ const char *nickname);
+static int node_nickname_matches(const node_t *router,
+ const char *nickname);
static void trusted_dir_server_free(trusted_dir_server_t *ds);
-static void launch_router_descriptor_downloads(smartlist_t *downloadable,
- routerstatus_t *source,
- time_t now);
static int signed_desc_digest_is_recognized(signed_descriptor_t *desc);
static void update_router_have_minimum_dir_info(void);
-static const char *signed_descriptor_get_body_impl(signed_descriptor_t *desc,
- int with_annotations);
+static const char *signed_descriptor_get_body_impl(
+ const signed_descriptor_t *desc,
+ int with_annotations);
static void list_pending_downloads(digestmap_t *result,
int purpose, const char *prefix);
+static void launch_dummy_descriptor_download_as_needed(time_t now,
+ const or_options_t *options);
DECLARE_TYPED_DIGESTMAP_FNS(sdmap_, digest_sd_map_t, signed_descriptor_t)
DECLARE_TYPED_DIGESTMAP_FNS(rimap_, digest_ri_map_t, routerinfo_t)
@@ -94,7 +99,7 @@ static smartlist_t *warned_nicknames = NULL;
/** The last time we tried to download any routerdesc, or 0 for "never". We
* use this to rate-limit download attempts when the number of routerdescs to
* download is low. */
-static time_t last_routerdesc_download_attempted = 0;
+static time_t last_descriptor_download_attempted = 0;
/** When we last computed the weights to use for bandwidths on directory
* requests, what were the total weighted bandwidth, and our share of that
@@ -106,7 +111,7 @@ static uint64_t sl_last_total_weighted_bw = 0,
/** Return the number of directory authorities whose type matches some bit set
* in <b>type</b> */
int
-get_n_authorities(authority_type_t type)
+get_n_authorities(dirinfo_type_t type)
{
int n = 0;
if (!trusted_dir_servers)
@@ -117,7 +122,7 @@ get_n_authorities(authority_type_t type)
return n;
}
-#define get_n_v2_authorities() get_n_authorities(V2_AUTHORITY)
+#define get_n_v2_authorities() get_n_authorities(V2_DIRINFO)
/** Helper: Return the cert_list_t for an authority whose authority ID is
* <b>id_digest</b>, allocating a new list if necessary. */
@@ -312,6 +317,7 @@ trusted_dirs_remove_old_certs(void)
time_t now = time(NULL);
#define DEAD_CERT_LIFETIME (2*24*60*60)
#define OLD_CERT_LIFETIME (7*24*60*60)
+#define CERT_EXPIRY_SKEW (60*60)
if (!trusted_dir_certs)
return;
@@ -514,7 +520,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
}
SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, trusted_dir_server_t *, ds) {
int found = 0;
- if (!(ds->type & V3_AUTHORITY))
+ if (!(ds->type & V3_DIRINFO))
continue;
if (smartlist_digest_isin(missing_digests, ds->v3_identity_digest))
continue;
@@ -600,7 +606,7 @@ router_should_rebuild_store(desc_store_t *store)
/** Return the desc_store_t in <b>rl</b> that should be used to store
* <b>sd</b>. */
static INLINE desc_store_t *
-desc_get_store(routerlist_t *rl, signed_descriptor_t *sd)
+desc_get_store(routerlist_t *rl, const signed_descriptor_t *sd)
{
if (sd->is_extrainfo)
return &rl->extrainfo_store;
@@ -926,10 +932,10 @@ router_get_trusted_dir_servers(void)
* Don't pick an authority if any non-authority is viable; try to avoid using
* servers that have returned 503 recently.
*/
-routerstatus_t *
-router_pick_directory_server(authority_type_t type, int flags)
+const routerstatus_t *
+router_pick_directory_server(dirinfo_type_t type, int flags)
{
- routerstatus_t *choice;
+ const routerstatus_t *choice;
if (get_options()->PreferTunneledDirConns)
flags |= _PDS_PREFER_TUNNELED_DIR_CONNS;
@@ -958,8 +964,8 @@ int
router_get_my_share_of_directory_requests(double *v2_share_out,
double *v3_share_out)
{
- routerinfo_t *me = router_get_my_routerinfo();
- routerstatus_t *rs;
+ const routerinfo_t *me = router_get_my_routerinfo();
+ const routerstatus_t *rs;
const int pds_flags = PDS_ALLOW_SELF|PDS_IGNORE_FASCISTFIREWALL;
*v2_share_out = *v3_share_out = 0.0;
if (!me)
@@ -972,7 +978,7 @@ router_get_my_share_of_directory_requests(double *v2_share_out,
/* XXXX This is a bit of a kludge */
if (rs->is_v2_dir) {
sl_last_total_weighted_bw = 0;
- router_pick_directory_server(V2_AUTHORITY, pds_flags);
+ router_pick_directory_server(V2_DIRINFO, pds_flags);
if (sl_last_total_weighted_bw != 0) {
*v2_share_out = U64_TO_DBL(sl_last_weighted_bw_of_me) /
U64_TO_DBL(sl_last_total_weighted_bw);
@@ -981,7 +987,7 @@ router_get_my_share_of_directory_requests(double *v2_share_out,
if (rs->version_supports_v3_dir) {
sl_last_total_weighted_bw = 0;
- router_pick_directory_server(V3_AUTHORITY, pds_flags);
+ router_pick_directory_server(V3_DIRINFO, pds_flags);
if (sl_last_total_weighted_bw != 0) {
*v3_share_out = U64_TO_DBL(sl_last_weighted_bw_of_me) /
U64_TO_DBL(sl_last_total_weighted_bw);
@@ -1022,7 +1028,7 @@ trusteddirserver_get_by_v3_auth_digest(const char *digest)
SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
{
if (tor_memeq(ds->v3_identity_digest, digest, DIGEST_LEN) &&
- (ds->type & V3_AUTHORITY))
+ (ds->type & V3_DIRINFO))
return ds;
});
@@ -1032,10 +1038,10 @@ trusteddirserver_get_by_v3_auth_digest(const char *digest)
/** Try to find a running trusted dirserver. Flags are as for
* router_pick_directory_server.
*/
-routerstatus_t *
-router_pick_trusteddirserver(authority_type_t type, int flags)
+const routerstatus_t *
+router_pick_trusteddirserver(dirinfo_type_t type, int flags)
{
- routerstatus_t *choice;
+ const routerstatus_t *choice;
int busy = 0;
if (get_options()->PreferTunneledDirConns)
flags |= _PDS_PREFER_TUNNELED_DIR_CONNS;
@@ -1047,7 +1053,8 @@ router_pick_trusteddirserver(authority_type_t type, int flags)
/* If the reason that we got no server is that servers are "busy",
* we must be excluding good servers because we already have serverdesc
* fetches with them. Do not mark down servers up because of this. */
- tor_assert((flags & PDS_NO_EXISTING_SERVERDESC_FETCH));
+ tor_assert((flags & (PDS_NO_EXISTING_SERVERDESC_FETCH|
+ PDS_NO_EXISTING_MICRODESC_FETCH)));
return NULL;
}
@@ -1067,11 +1074,11 @@ router_pick_trusteddirserver(authority_type_t type, int flags)
* If the _PDS_PREFER_TUNNELED_DIR_CONNS flag is set, prefer directory servers
* that we can use with BEGINDIR.
*/
-static routerstatus_t *
-router_pick_directory_server_impl(authority_type_t type, int flags)
+static const routerstatus_t *
+router_pick_directory_server_impl(dirinfo_type_t type, int flags)
{
- or_options_t *options = get_options();
- routerstatus_t *result;
+ const or_options_t *options = get_options();
+ const node_t *result;
smartlist_t *direct, *tunnel;
smartlist_t *trusted_direct, *trusted_tunnel;
smartlist_t *overloaded_direct, *overloaded_tunnel;
@@ -1095,54 +1102,64 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
overloaded_tunnel = smartlist_create();
/* Find all the running dirservers we know about. */
- SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *,
- status) {
+ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
int is_trusted;
- int is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
+ int is_overloaded;
tor_addr_t addr;
- if (!status->is_running || !status->dir_port || !status->is_valid)
+ const routerstatus_t *status = node->rs;
+ const country_t country = node->country;
+ if (!status)
continue;
- if (status->is_bad_directory)
+
+ if (!node->is_running || !status->dir_port || !node->is_valid)
+ continue;
+ if (node->is_bad_directory)
continue;
- if (requireother && router_digest_is_me(status->identity_digest))
+ if (requireother && router_digest_is_me(node->identity))
continue;
- if (type & V3_AUTHORITY) {
+ if (type & V3_DIRINFO) {
if (!(status->version_supports_v3_dir ||
- router_digest_is_trusted_dir_type(status->identity_digest,
- V3_AUTHORITY)))
+ router_digest_is_trusted_dir_type(node->identity,
+ V3_DIRINFO)))
continue;
}
- is_trusted = router_digest_is_trusted_dir(status->identity_digest);
- if ((type & V2_AUTHORITY) && !(status->is_v2_dir || is_trusted))
+ is_trusted = router_digest_is_trusted_dir(node->identity);
+ if ((type & V2_DIRINFO) && !(node->rs->is_v2_dir || is_trusted))
+ continue;
+ if ((type & EXTRAINFO_DIRINFO) &&
+ !router_supports_extrainfo(node->identity, 0))
continue;
- if ((type & EXTRAINFO_CACHE) &&
- !router_supports_extrainfo(status->identity_digest, 0))
+ if ((type & MICRODESC_DIRINFO) && !is_trusted &&
+ !node->rs->version_supports_microdesc_cache)
continue;
- if (try_excluding && options->ExcludeNodes &&
- routerset_contains_routerstatus(options->ExcludeNodes, status)) {
+ if (try_excluding &&
+ routerset_contains_routerstatus(options->ExcludeNodes, status,
+ country)) {
++n_excluded;
continue;
}
/* XXXX IP6 proposal 118 */
- tor_addr_from_ipv4h(&addr, status->addr);
+ tor_addr_from_ipv4h(&addr, node->rs->addr);
+
+ is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now;
if (prefer_tunnel &&
status->version_supports_begindir &&
(!fascistfirewall ||
fascist_firewall_allows_address_or(&addr, status->or_port)))
smartlist_add(is_trusted ? trusted_tunnel :
- is_overloaded ? overloaded_tunnel : tunnel, status);
+ is_overloaded ? overloaded_tunnel : tunnel, (void*)node);
else if (!fascistfirewall ||
fascist_firewall_allows_address_dir(&addr, status->dir_port))
smartlist_add(is_trusted ? trusted_direct :
- is_overloaded ? overloaded_direct : direct, status);
- } SMARTLIST_FOREACH_END(status);
+ is_overloaded ? overloaded_direct : direct, (void*)node);
+ } SMARTLIST_FOREACH_END(node);
if (smartlist_len(tunnel)) {
- result = routerstatus_sl_choose_by_bandwidth(tunnel, WEIGHT_FOR_DIR);
+ result = node_sl_choose_by_bandwidth(tunnel, WEIGHT_FOR_DIR);
} else if (smartlist_len(overloaded_tunnel)) {
- result = routerstatus_sl_choose_by_bandwidth(overloaded_tunnel,
+ result = node_sl_choose_by_bandwidth(overloaded_tunnel,
WEIGHT_FOR_DIR);
} else if (smartlist_len(trusted_tunnel)) {
/* FFFF We don't distinguish between trusteds and overloaded trusteds
@@ -1151,10 +1168,10 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
* is a feature, but it could easily be a bug. -RD */
result = smartlist_choose(trusted_tunnel);
} else if (smartlist_len(direct)) {
- result = routerstatus_sl_choose_by_bandwidth(direct, WEIGHT_FOR_DIR);
+ result = node_sl_choose_by_bandwidth(direct, WEIGHT_FOR_DIR);
} else if (smartlist_len(overloaded_direct)) {
- result = routerstatus_sl_choose_by_bandwidth(overloaded_direct,
- WEIGHT_FOR_DIR);
+ result = node_sl_choose_by_bandwidth(overloaded_direct,
+ WEIGHT_FOR_DIR);
} else {
result = smartlist_choose(trusted_direct);
}
@@ -1173,26 +1190,27 @@ router_pick_directory_server_impl(authority_type_t type, int flags)
goto retry_without_exclude;
}
- return result;
+ return result ? result->rs : NULL;
}
/** Choose randomly from among the trusted dirservers that are up. Flags
* are as for router_pick_directory_server_impl().
*/
-static routerstatus_t *
-router_pick_trusteddirserver_impl(authority_type_t type, int flags,
+static const routerstatus_t *
+router_pick_trusteddirserver_impl(dirinfo_type_t type, int flags,
int *n_busy_out)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
smartlist_t *direct, *tunnel;
smartlist_t *overloaded_direct, *overloaded_tunnel;
- routerinfo_t *me = router_get_my_routerinfo();
- routerstatus_t *result;
+ const routerinfo_t *me = router_get_my_routerinfo();
+ const routerstatus_t *result;
time_t now = time(NULL);
const int requireother = ! (flags & PDS_ALLOW_SELF);
const int fascistfirewall = ! (flags & PDS_IGNORE_FASCISTFIREWALL);
const int prefer_tunnel = (flags & _PDS_PREFER_TUNNELED_DIR_CONNS);
const int no_serverdesc_fetching =(flags & PDS_NO_EXISTING_SERVERDESC_FETCH);
+ const int no_microdesc_fetching =(flags & PDS_NO_EXISTING_MICRODESC_FETCH);
int n_busy = 0;
int try_excluding = 1, n_excluded = 0;
@@ -1214,14 +1232,14 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags,
if (!d->is_running) continue;
if ((type & d->type) == 0)
continue;
- if ((type & EXTRAINFO_CACHE) &&
+ if ((type & EXTRAINFO_DIRINFO) &&
!router_supports_extrainfo(d->digest, 1))
continue;
if (requireother && me && router_digest_is_me(d->digest))
continue;
- if (try_excluding && options->ExcludeNodes &&
+ if (try_excluding &&
routerset_contains_routerstatus(options->ExcludeNodes,
- &d->fake_status)) {
+ &d->fake_status, -1)) {
++n_excluded;
continue;
}
@@ -1240,6 +1258,13 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags,
continue;
}
}
+ if (no_microdesc_fetching) {
+ if (connection_get_by_type_addr_port_purpose(
+ CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_MICRODESC)) {
+ ++n_busy;
+ continue;
+ }
+ }
if (prefer_tunnel &&
d->or_port &&
@@ -1287,22 +1312,18 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags,
static void
mark_all_trusteddirservers_up(void)
{
- if (routerlist) {
- SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
- if (router_digest_is_trusted_dir(router->cache_info.identity_digest) &&
- router->dir_port > 0) {
- router->is_running = 1;
- });
- }
+ SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node, {
+ if (router_digest_is_trusted_dir(node->identity))
+ node->is_running = 1;
+ });
if (trusted_dir_servers) {
SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, dir,
{
routerstatus_t *rs;
dir->is_running = 1;
download_status_reset(&dir->v2_ns_dl_status);
- rs = router_get_consensus_status_by_id(dir->digest);
- if (rs && !rs->is_running) {
- rs->is_running = 1;
+ rs = router_get_mutable_consensus_status_by_id(dir->digest);
+ if (rs) {
rs->last_dir_503_at = 0;
control_event_networkstatus_changed_single(rs);
}
@@ -1326,148 +1347,160 @@ router_reset_status_download_failures(void)
mark_all_trusteddirservers_up();
}
-/** Return true iff router1 and router2 have the same /16 network. */
+/** Return true iff router1 and router2 have similar enough network addresses
+ * that we should treat them as being in the same family */
static INLINE int
-routers_in_same_network_family(routerinfo_t *r1, routerinfo_t *r2)
+addrs_in_same_network_family(const tor_addr_t *a1,
+ const tor_addr_t *a2)
{
- return (r1->addr & 0xffff0000) == (r2->addr & 0xffff0000);
+ /* XXXX MOVE ? */
+ return 0 == tor_addr_compare_masked(a1, a2, 16, CMP_SEMANTIC);
}
-/** Look through the routerlist and identify routers that
- * advertise the same /16 network address as <b>router</b>.
- * Add each of them to <b>sl</b>.
+/**
+ * Add all the family of <b>node</b>, including <b>node</b> itself, to
+ * the smartlist <b>sl</b>.
+ *
+ * This is used to make sure we don't pick siblings in a single path, or
+ * pick more than one relay from a family for our entry guard list.
+ * Note that a node may be added to <b>sl</b> more than once if it is
+ * part of <b>node</b>'s family for more than one reason.
*/
-static void
-routerlist_add_network_family(smartlist_t *sl, routerinfo_t *router)
+void
+nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
{
- SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, r,
+ /* XXXX MOVE */
+ const smartlist_t *all_nodes = nodelist_get_list();
+ const smartlist_t *declared_family;
+ const or_options_t *options = get_options();
+
+ tor_assert(node);
+
+ declared_family = node_get_declared_family(node);
+
+ /* Let's make sure that we have the node itself, if it's a real node. */
{
- if (router != r && routers_in_same_network_family(router, r))
- smartlist_add(sl, r);
- });
-}
+ const node_t *real_node = node_get_by_id(node->identity);
+ if (real_node)
+ smartlist_add(sl, (node_t*)real_node);
+ }
-/** Add all the family of <b>router</b> to the smartlist <b>sl</b>.
- * This is used to make sure we don't pick siblings in a single path,
- * or pick more than one relay from a family for our entry guard list.
- */
-void
-routerlist_add_family(smartlist_t *sl, routerinfo_t *router)
-{
- routerinfo_t *r;
- config_line_t *cl;
- or_options_t *options = get_options();
+ /* First, add any nodes with similar network addresses. */
+ if (options->EnforceDistinctSubnets) {
+ tor_addr_t node_addr;
+ node_get_addr(node, &node_addr);
- /* First, add any routers with similar network addresses. */
- if (options->EnforceDistinctSubnets)
- routerlist_add_network_family(sl, router);
+ SMARTLIST_FOREACH_BEGIN(all_nodes, const node_t *, node2) {
+ tor_addr_t a;
+ node_get_addr(node2, &a);
+ if (addrs_in_same_network_family(&a, &node_addr))
+ smartlist_add(sl, (void*)node2);
+ } SMARTLIST_FOREACH_END(node2);
+ }
- if (router->declared_family) {
- /* Add every r such that router declares familyness with r, and r
+ /* Now, add all nodes in the declared_family of this node, if they
+ * also declare this node to be in their family. */
+ if (declared_family) {
+ /* Add every r such that router declares familyness with node, and node
* declares familyhood with router. */
- SMARTLIST_FOREACH(router->declared_family, const char *, n,
- {
- if (!(r = router_get_by_nickname(n, 0)))
- continue;
- if (!r->declared_family)
- continue;
- SMARTLIST_FOREACH(r->declared_family, const char *, n2,
- {
- if (router_nickname_matches(router, n2))
- smartlist_add(sl, r);
- });
- });
+ SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) {
+ const node_t *node2;
+ const smartlist_t *family2;
+ if (!(node2 = node_get_by_nickname(name, 0)))
+ continue;
+ if (!(family2 = node_get_declared_family(node2)))
+ continue;
+ SMARTLIST_FOREACH_BEGIN(family2, const char *, name2) {
+ if (node_nickname_matches(node, name2)) {
+ smartlist_add(sl, (void*)node2);
+ break;
+ }
+ } SMARTLIST_FOREACH_END(name2);
+ } SMARTLIST_FOREACH_END(name);
}
/* If the user declared any families locally, honor those too. */
- for (cl = options->NodeFamilies; cl; cl = cl->next) {
- if (router_nickname_is_in_list(router, cl->value)) {
- add_nickname_list_to_smartlist(sl, cl->value, 0);
- }
+ if (options->NodeFamilySets) {
+ SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, {
+ if (routerset_contains_node(rs, node)) {
+ routerset_get_all_nodes(sl, rs, NULL, 0);
+ }
+ });
}
}
-/** Return true iff r is named by some nickname in <b>lst</b>. */
+/** Given a <b>router</b>, add every node_t in its family (including the
+ * node itself</b>) to <b>sl</b>.
+ *
+ * Note the type mismatch: This function takes a routerinfo, but adds nodes
+ * to the smartlist!
+ */
+static void
+routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router)
+{
+ /* XXXX MOVE ? */
+ node_t fake_node;
+ const node_t *node = node_get_by_id(router->cache_info.identity_digest);;
+ if (node == NULL) {
+ memset(&fake_node, 0, sizeof(fake_node));
+ fake_node.ri = (routerinfo_t *)router;
+ memcpy(fake_node.identity, router->cache_info.identity_digest, DIGEST_LEN);
+ node = &fake_node;
+ }
+ nodelist_add_node_and_family(sl, node);
+}
+
+/** Return true iff <b>node</b> is named by some nickname in <b>lst</b>. */
static INLINE int
-router_in_nickname_smartlist(smartlist_t *lst, routerinfo_t *r)
+node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node)
{
+ /* XXXX MOVE */
if (!lst) return 0;
- SMARTLIST_FOREACH(lst, const char *, name,
- if (router_nickname_matches(r, name))
- return 1;);
+ SMARTLIST_FOREACH(lst, const char *, name, {
+ if (node_nickname_matches(node, name))
+ return 1;
+ });
return 0;
}
/** Return true iff r1 and r2 are in the same family, but not the same
* router. */
int
-routers_in_same_family(routerinfo_t *r1, routerinfo_t *r2)
+nodes_in_same_family(const node_t *node1, const node_t *node2)
{
- or_options_t *options = get_options();
- config_line_t *cl;
-
- if (options->EnforceDistinctSubnets && routers_in_same_network_family(r1,r2))
- return 1;
-
- if (router_in_nickname_smartlist(r1->declared_family, r2) &&
- router_in_nickname_smartlist(r2->declared_family, r1))
- return 1;
+ /* XXXX MOVE */
+ const or_options_t *options = get_options();
- for (cl = options->NodeFamilies; cl; cl = cl->next) {
- if (router_nickname_is_in_list(r1, cl->value) &&
- router_nickname_is_in_list(r2, cl->value))
+ /* Are they in the same family because of their addresses? */
+ if (options->EnforceDistinctSubnets) {
+ tor_addr_t a1, a2;
+ node_get_addr(node1, &a1);
+ node_get_addr(node2, &a2);
+ if (addrs_in_same_network_family(&a1, &a2))
return 1;
}
- return 0;
-}
-
-/** Given a (possibly NULL) comma-and-whitespace separated list of nicknames,
- * see which nicknames in <b>list</b> name routers in our routerlist, and add
- * the routerinfos for those routers to <b>sl</b>. If <b>must_be_running</b>,
- * only include routers that we think are running.
- * Warn if any non-Named routers are specified by nickname.
- */
-void
-add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
- int must_be_running)
-{
- routerinfo_t *router;
- smartlist_t *nickname_list;
- int have_dir_info = router_have_minimum_dir_info();
- if (!list)
- return; /* nothing to do */
- tor_assert(sl);
-
- nickname_list = smartlist_create();
- if (!warned_nicknames)
- warned_nicknames = smartlist_create();
+ /* Are they in the same family because the agree they are? */
+ {
+ const smartlist_t *f1, *f2;
+ f1 = node_get_declared_family(node1);
+ f2 = node_get_declared_family(node2);
+ if (f1 && f2 &&
+ node_in_nickname_smartlist(f1, node2) &&
+ node_in_nickname_smartlist(f2, node1))
+ return 1;
+ }
- smartlist_split_string(nickname_list, list, ",",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ /* Are they in the same option because the user says they are? */
+ if (options->NodeFamilySets) {
+ SMARTLIST_FOREACH(options->NodeFamilySets, const routerset_t *, rs, {
+ if (routerset_contains_node(rs, node1) &&
+ routerset_contains_node(rs, node2))
+ return 1;
+ });
+ }
- SMARTLIST_FOREACH(nickname_list, const char *, nick, {
- int warned;
- if (!is_legal_nickname_or_hexdigest(nick)) {
- log_warn(LD_CONFIG, "Nickname '%s' is misformed; skipping", nick);
- continue;
- }
- router = router_get_by_nickname(nick, 1);
- warned = smartlist_string_isin(warned_nicknames, nick);
- if (router) {
- if (!must_be_running || router->is_running) {
- smartlist_add(sl,router);
- }
- } else if (!router_get_consensus_status_by_nickname(nick,1)) {
- if (!warned) {
- log_fn(have_dir_info ? LOG_WARN : LOG_INFO, LD_CONFIG,
- "Nickname list includes '%s' which isn't a known router.",nick);
- smartlist_add(warned_nicknames, tor_strdup(nick));
- }
- }
- });
- SMARTLIST_FOREACH(nickname_list, char *, nick, tor_free(nick));
- smartlist_free(nickname_list);
+ return 0;
}
/** Return 1 iff any member of the (possibly NULL) comma-separated list
@@ -1475,7 +1508,7 @@ add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
* return 0.
*/
int
-router_nickname_is_in_list(routerinfo_t *router, const char *list)
+router_nickname_is_in_list(const routerinfo_t *router, const char *list)
{
smartlist_t *nickname_list;
int v = 0;
@@ -1494,34 +1527,32 @@ router_nickname_is_in_list(routerinfo_t *router, const char *list)
return v;
}
-/** Add every suitable router from our routerlist to <b>sl</b>, so that
+/** Add every suitable node from our nodelist to <b>sl</b>, so that
* we can pick a node for a circuit.
*/
static void
-router_add_running_routers_to_smartlist(smartlist_t *sl, int allow_invalid,
- int need_uptime, int need_capacity,
- int need_guard)
-{
- if (!routerlist)
- return;
+router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
+ int need_uptime, int need_capacity,
+ int need_guard, int need_desc)
+{ /* XXXX MOVE */
+ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
+ if (!node->is_running ||
+ (!node->is_valid && !allow_invalid))
+ continue;
+ if (need_desc && !(node->ri || (node->rs && node->md)))
+ continue;
+ if (node->ri && node->ri->purpose != ROUTER_PURPOSE_GENERAL)
+ continue;
+ if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
+ continue;
- SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
- {
- if (router->is_running &&
- router->purpose == ROUTER_PURPOSE_GENERAL &&
- (router->is_valid || allow_invalid) &&
- !router_is_unreliable(router, need_uptime,
- need_capacity, need_guard)) {
- /* If it's running, and it's suitable according to the
- * other flags we had in mind */
- smartlist_add(sl, router);
- }
- });
+ smartlist_add(sl, (void *)node);
+ } SMARTLIST_FOREACH_END(node);
}
/** Look through the routerlist until we find a router that has my key.
Return it. */
-routerinfo_t *
+const routerinfo_t *
routerlist_find_my_routerinfo(void)
{
if (!routerlist)
@@ -1541,13 +1572,13 @@ routerlist_find_my_routerinfo(void)
* Don't exit enclave to excluded relays -- it wouldn't actually
* hurt anything, but this way there are fewer confused users.
*/
-routerinfo_t *
+const node_t *
router_find_exact_exit_enclave(const char *address, uint16_t port)
-{
+{/*XXXX MOVE*/
uint32_t addr;
struct in_addr in;
tor_addr_t a;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (!tor_inet_aton(address, &in))
return NULL; /* it's not an IP already */
@@ -1555,14 +1586,13 @@ router_find_exact_exit_enclave(const char *address, uint16_t port)
tor_addr_from_ipv4h(&a, addr);
- SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
- {
- if (router->addr == addr &&
- router->is_running &&
- compare_tor_addr_to_addr_policy(&a, port, router->exit_policy) ==
+ SMARTLIST_FOREACH(nodelist_get_list(), const node_t *, node, {
+ if (node_get_addr_ipv4h(node) == addr &&
+ node->is_running &&
+ compare_tor_addr_to_node_policy(&a, port, node) ==
ADDR_POLICY_ACCEPTED &&
- !routerset_contains_router(options->_ExcludeExitNodesUnion, router))
- return router;
+ !routerset_contains_node(options->_ExcludeExitNodesUnion, node))
+ return node;
});
return NULL;
}
@@ -1574,14 +1604,14 @@ router_find_exact_exit_enclave(const char *address, uint16_t port)
* If <b>need_guard</b>, we require that the router is a possible entry guard.
*/
int
-router_is_unreliable(routerinfo_t *router, int need_uptime,
- int need_capacity, int need_guard)
+node_is_unreliable(const node_t *node, int need_uptime,
+ int need_capacity, int need_guard)
{
- if (need_uptime && !router->is_stable)
+ if (need_uptime && !node->is_stable)
return 1;
- if (need_capacity && !router->is_fast)
+ if (need_capacity && !node->is_fast)
return 1;
- if (need_guard && !router->is_possible_guard)
+ if (need_guard && !node->is_possible_guard)
return 1;
return 0;
}
@@ -1589,7 +1619,7 @@ router_is_unreliable(routerinfo_t *router, int need_uptime,
/** Return the smaller of the router's configured BandwidthRate
* and its advertised capacity. */
uint32_t
-router_get_advertised_bandwidth(routerinfo_t *router)
+router_get_advertised_bandwidth(const routerinfo_t *router)
{
if (router->bandwidthcapacity < router->bandwidthrate)
return router->bandwidthcapacity;
@@ -1603,7 +1633,7 @@ router_get_advertised_bandwidth(routerinfo_t *router)
/** Return the smaller of the router's configured BandwidthRate
* and its advertised capacity, capped by max-believe-bw. */
uint32_t
-router_get_advertised_bandwidth_capped(routerinfo_t *router)
+router_get_advertised_bandwidth_capped(const routerinfo_t *router)
{
uint32_t result = router->bandwidthcapacity;
if (result > router->bandwidthrate)
@@ -1645,13 +1675,10 @@ kb_to_bytes(uint32_t bw)
}
/** Helper function:
- * choose a random element of smartlist <b>sl</b>, weighted by
+ * choose a random element of smartlist <b>sl</b> of nodes, weighted by
* the advertised bandwidth of each element using the consensus
* bandwidth weights.
*
- * If <b>statuses</b> is zero, then <b>sl</b> is a list of
- * routerinfo_t's. Otherwise it's a list of routerstatus_t's.
- *
* If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all
* nodes' bandwidth equally regardless of their Exit status, since there may
* be some in the list because they exit to obscure ports. If
@@ -1661,10 +1688,9 @@ kb_to_bytes(uint32_t bw)
* guard node: consider all guard's bandwidth equally. Otherwise, weight
* guards proportionally less.
*/
-static void *
-smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
- bandwidth_weight_rule_t rule,
- int statuses)
+static const node_t *
+smartlist_choose_node_by_bandwidth_weights(smartlist_t *sl,
+ bandwidth_weight_rule_t rule)
{
int64_t weight_scale;
int64_t rand_bw;
@@ -1758,15 +1784,14 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
bandwidths = tor_malloc_zero(sizeof(double)*smartlist_len(sl));
// Cycle through smartlist and total the bandwidth.
- for (i = 0; i < (unsigned)smartlist_len(sl); ++i) {
+ SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
int is_exit = 0, is_guard = 0, is_dir = 0, this_bw = 0, is_me = 0;
double weight = 1;
- if (statuses) {
- routerstatus_t *status = smartlist_get(sl, i);
- is_exit = status->is_exit && !status->is_bad_exit;
- is_guard = status->is_possible_guard;
- is_dir = (status->dir_port != 0);
- if (!status->has_bandwidth) {
+ is_exit = node->is_exit && ! node->is_bad_exit;
+ is_guard = node->is_possible_guard;
+ is_dir = node_is_dir(node);
+ if (node->rs) {
+ if (!node->rs->has_bandwidth) {
tor_free(bandwidths);
/* This should never happen, unless all the authorites downgrade
* to 0.2.0 or rogue routerstatuses get inserted into our consensus. */
@@ -1775,26 +1800,17 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
"old router selection algorithm.");
return NULL;
}
- this_bw = kb_to_bytes(status->bandwidth);
- if (router_digest_is_me(status->identity_digest))
- is_me = 1;
+ this_bw = kb_to_bytes(node->rs->bandwidth);
+ } else if (node->ri) {
+ /* bridge or other descriptor not in our consensus */
+ this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
+ have_unknown = 1;
} else {
- routerstatus_t *rs;
- routerinfo_t *router = smartlist_get(sl, i);
- rs = router_get_consensus_status_by_id(
- router->cache_info.identity_digest);
- is_exit = router->is_exit && !router->is_bad_exit;
- is_guard = router->is_possible_guard;
- is_dir = (router->dir_port != 0);
- if (rs && rs->has_bandwidth) {
- this_bw = kb_to_bytes(rs->bandwidth);
- } else { /* bridge or other descriptor not in our consensus */
- this_bw = bridge_get_advertised_bandwidth_bounded(router);
- have_unknown = 1;
- }
- if (router_digest_is_me(router->cache_info.identity_digest))
- is_me = 1;
+ /* We can't use this one. */
+ continue;
}
+ is_me = router_digest_is_me(node->identity);
+
if (is_guard && is_exit) {
weight = (is_dir ? Wdb*Wd : Wd);
} else if (is_guard) {
@@ -1805,11 +1821,11 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
weight = (is_dir ? Wmb*Wm : Wm);
}
- bandwidths[i] = weight*this_bw;
+ bandwidths[node_sl_idx] = weight*this_bw;
weighted_bw += weight*this_bw;
if (is_me)
sl_last_weighted_bw_of_me = weight*this_bw;
- }
+ } SMARTLIST_FOREACH_END(node);
/* XXXX023 this is a kludge to expose these values. */
sl_last_total_weighted_bw = weighted_bw;
@@ -1857,12 +1873,9 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
}
/** Helper function:
- * choose a random element of smartlist <b>sl</b>, weighted by
+ * choose a random node_t element of smartlist <b>sl</b>, weighted by
* the advertised bandwidth of each element.
*
- * If <b>statuses</b> is zero, then <b>sl</b> is a list of
- * routerinfo_t's. Otherwise it's a list of routerstatus_t's.
- *
* If <b>rule</b>==WEIGHT_FOR_EXIT. we're picking an exit node: consider all
* nodes' bandwidth equally regardless of their Exit status, since there may
* be some in the list because they exit to obscure ports. If
@@ -1872,13 +1885,11 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl,
* guard node: consider all guard's bandwidth equally. Otherwise, weight
* guards proportionally less.
*/
-static void *
-smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
- int statuses)
+static const node_t *
+smartlist_choose_node_by_bandwidth(smartlist_t *sl,
+ bandwidth_weight_rule_t rule)
{
- unsigned int i;
- routerinfo_t *router;
- routerstatus_t *status=NULL;
+ unsigned i;
int32_t *bandwidths;
int is_exit;
int is_guard;
@@ -1919,49 +1930,34 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
guard_bits = bitarray_init_zero(smartlist_len(sl));
/* Iterate over all the routerinfo_t or routerstatus_t, and */
- for (i = 0; i < (unsigned)smartlist_len(sl); ++i) {
+ SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
/* first, learn what bandwidth we think i has */
int is_known = 1;
int32_t flags = 0;
uint32_t this_bw = 0;
- if (statuses) {
- status = smartlist_get(sl, i);
- if (router_digest_is_me(status->identity_digest))
- me_idx = i;
- router = router_get_by_digest(status->identity_digest);
- is_exit = status->is_exit;
- is_guard = status->is_possible_guard;
- if (status->has_bandwidth) {
- this_bw = kb_to_bytes(status->bandwidth);
+ i = node_sl_idx;
+
+ if (router_digest_is_me(node->identity))
+ me_idx = node_sl_idx;
+
+ is_exit = node->is_exit;
+ is_guard = node->is_possible_guard;
+ if (node->rs) {
+ if (node->rs->has_bandwidth) {
+ this_bw = kb_to_bytes(node->rs->bandwidth);
} else { /* guess */
/* XXX023 once consensuses always list bandwidths, we can take
* this guessing business out. -RD */
is_known = 0;
- flags = status->is_fast ? 1 : 0;
+ flags = node->rs->is_fast ? 1 : 0;
flags |= is_exit ? 2 : 0;
flags |= is_guard ? 4 : 0;
}
- } else {
- routerstatus_t *rs;
- router = smartlist_get(sl, i);
- rs = router_get_consensus_status_by_id(
- router->cache_info.identity_digest);
- if (router_digest_is_me(router->cache_info.identity_digest))
- me_idx = i;
- is_exit = router->is_exit;
- is_guard = router->is_possible_guard;
- if (rs && rs->has_bandwidth) {
- this_bw = kb_to_bytes(rs->bandwidth);
- } else if (rs) { /* guess; don't trust the descriptor */
- /* XXX023 once consensuses always list bandwidths, we can take
- * this guessing business out. -RD */
- is_known = 0;
- flags = router->is_fast ? 1 : 0;
- flags |= is_exit ? 2 : 0;
- flags |= is_guard ? 4 : 0;
- } else /* bridge or other descriptor not in our consensus */
- this_bw = bridge_get_advertised_bandwidth_bounded(router);
+ } else if (node->ri) {
+ /* Must be a bridge if we're willing to use it */
+ this_bw = bridge_get_advertised_bandwidth_bounded(node->ri);
}
+
if (is_exit)
bitarray_set(exit_bits, i);
if (is_guard)
@@ -1981,9 +1977,9 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
total_nonexit_bw += this_bw;
} else {
++n_unknown;
- bandwidths[i] = -flags;
+ bandwidths[node_sl_idx] = -flags;
}
- }
+ } SMARTLIST_FOREACH_END(node);
/* Now, fill in the unknown values. */
if (n_unknown) {
@@ -2125,40 +2121,23 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule,
return smartlist_get(sl, i);
}
-/** Choose a random element of router list <b>sl</b>, weighted by
- * the advertised bandwidth of each router.
- */
-routerinfo_t *
-routerlist_sl_choose_by_bandwidth(smartlist_t *sl,
- bandwidth_weight_rule_t rule)
-{
- routerinfo_t *ret;
- if ((ret = smartlist_choose_by_bandwidth_weights(sl, rule, 0))) {
- return ret;
- } else {
- return smartlist_choose_by_bandwidth(sl, rule, 0);
- }
-}
-
/** Choose a random element of status list <b>sl</b>, weighted by
- * the advertised bandwidth of each status.
- */
-routerstatus_t *
-routerstatus_sl_choose_by_bandwidth(smartlist_t *sl,
- bandwidth_weight_rule_t rule)
-{
- /* We are choosing neither exit nor guard here. Weight accordingly. */
- routerstatus_t *ret;
- if ((ret = smartlist_choose_by_bandwidth_weights(sl, rule, 1))) {
+ * the advertised bandwidth of each node */
+const node_t *
+node_sl_choose_by_bandwidth(smartlist_t *sl,
+ bandwidth_weight_rule_t rule)
+{ /*XXXX MOVE */
+ const node_t *ret;
+ if ((ret = smartlist_choose_node_by_bandwidth_weights(sl, rule))) {
return ret;
} else {
- return smartlist_choose_by_bandwidth(sl, rule, 1);
+ return smartlist_choose_node_by_bandwidth(sl, rule);
}
}
-/** Return a random running router from the routerlist. Never
- * pick a node whose routerinfo is in
- * <b>excludedsmartlist</b>, or whose routerinfo matches <b>excludedset</b>,
+/** Return a random running node from the nodelist. Never
+ * pick a node that is in
+ * <b>excludedsmartlist</b>, or which matches <b>excludedset</b>,
* even if they are the only nodes available.
* If <b>CRN_NEED_UPTIME</b> is set in flags and any router has more than
* a minimum uptime, return one of those.
@@ -2170,21 +2149,26 @@ routerstatus_sl_choose_by_bandwidth(smartlist_t *sl,
* If <b>CRN_WEIGHT_AS_EXIT</b> is set in flags, we weight bandwidths as if
* picking an exit node, otherwise we weight bandwidths for picking a relay
* node (that is, possibly discounting exit nodes).
+ * If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that
+ * have a routerinfo or microdescriptor -- that is, enough info to be
+ * used to build a circuit.
*/
-routerinfo_t *
+const node_t *
router_choose_random_node(smartlist_t *excludedsmartlist,
routerset_t *excludedset,
router_crn_flags_t flags)
-{
+{ /* XXXX MOVE */
const int need_uptime = (flags & CRN_NEED_UPTIME) != 0;
const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0;
const int need_guard = (flags & CRN_NEED_GUARD) != 0;
const int allow_invalid = (flags & CRN_ALLOW_INVALID) != 0;
const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0;
+ const int need_desc = (flags & CRN_NEED_DESC) != 0;
smartlist_t *sl=smartlist_create(),
- *excludednodes=smartlist_create();
- routerinfo_t *choice = NULL, *r;
+ *excludednodes=smartlist_create();
+ const node_t *choice = NULL;
+ const routerinfo_t *r;
bandwidth_weight_rule_t rule;
tor_assert(!(weight_for_exit && need_guard));
@@ -2194,29 +2178,26 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
/* Exclude relays that allow single hop exit circuits, if the user
* wants to (such relays might be risky) */
if (get_options()->ExcludeSingleHopRelays) {
- routerlist_t *rl = router_get_routerlist();
- SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
- if (r->allow_single_hop_exits) {
- smartlist_add(excludednodes, r);
+ SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
+ if (node_allows_single_hop_exits(node)) {
+ smartlist_add(excludednodes, node);
});
}
- if ((r = routerlist_find_my_routerinfo())) {
- smartlist_add(excludednodes, r);
- routerlist_add_family(excludednodes, r);
- }
+ if ((r = routerlist_find_my_routerinfo()))
+ routerlist_add_node_and_family(excludednodes, r);
- router_add_running_routers_to_smartlist(sl, allow_invalid,
- need_uptime, need_capacity,
- need_guard);
+ router_add_running_nodes_to_smartlist(sl, allow_invalid,
+ need_uptime, need_capacity,
+ need_guard, need_desc);
smartlist_subtract(sl,excludednodes);
if (excludedsmartlist)
smartlist_subtract(sl,excludedsmartlist);
if (excludedset)
- routerset_subtract_routers(sl,excludedset);
+ routerset_subtract_nodes(sl,excludedset);
// Always weight by bandwidth
- choice = routerlist_sl_choose_by_bandwidth(sl, rule);
+ choice = node_sl_choose_by_bandwidth(sl, rule);
smartlist_free(sl);
if (!choice && (need_uptime || need_capacity || need_guard)) {
@@ -2239,35 +2220,90 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
return choice;
}
-/** Helper: Return true iff the <b>identity_digest</b> and <b>nickname</b>
- * combination of a router, encoded in hexadecimal, matches <b>hexdigest</b>
- * (which is optionally prefixed with a single dollar sign). Return false if
- * <b>hexdigest</b> is malformed, or it doesn't match. */
-static INLINE int
-hex_digest_matches(const char *hexdigest, const char *identity_digest,
- const char *nickname, int is_named)
+/** Helper: given an extended nickname in <b>hexdigest</b> try to decode it.
+ * Return 0 on success, -1 on failure. Store the result into the
+ * DIGEST_LEN-byte buffer at <b>digest_out</b>, the single character at
+ * <b>nickname_qualifier_char_out</b>, and the MAXNICKNAME_LEN+1-byte buffer
+ * at <b>nickname_out</b>.
+ *
+ * The recognized format is:
+ * HexName = Dollar? HexDigest NamePart?
+ * Dollar = '?'
+ * HexDigest = HexChar*20
+ * HexChar = 'a'..'f' | 'A'..'F' | '0'..'9'
+ * NamePart = QualChar Name
+ * QualChar = '=' | '~'
+ * Name = NameChar*(1..MAX_NICKNAME_LEN)
+ * NameChar = Any ASCII alphanumeric character
+ */
+int
+hex_digest_nickname_decode(const char *hexdigest,
+ char *digest_out,
+ char *nickname_qualifier_char_out,
+ char *nickname_out)
{
- char digest[DIGEST_LEN];
size_t len;
+
tor_assert(hexdigest);
if (hexdigest[0] == '$')
++hexdigest;
len = strlen(hexdigest);
- if (len < HEX_DIGEST_LEN)
+ if (len < HEX_DIGEST_LEN) {
+ return -1;
+ } else if (len > HEX_DIGEST_LEN && (hexdigest[HEX_DIGEST_LEN] == '=' ||
+ hexdigest[HEX_DIGEST_LEN] == '~') &&
+ len <= HEX_DIGEST_LEN+1+MAX_NICKNAME_LEN) {
+ *nickname_qualifier_char_out = hexdigest[HEX_DIGEST_LEN];
+ strlcpy(nickname_out, hexdigest+HEX_DIGEST_LEN+1 , MAX_NICKNAME_LEN+1);
+ } else if (len == HEX_DIGEST_LEN) {
+ ;
+ } else {
+ return -1;
+ }
+
+ if (base16_decode(digest_out, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
+ return -1;
+ return 0;
+}
+
+/** Helper: Return true iff the <b>identity_digest</b> and <b>nickname</b>
+ * combination of a router, encoded in hexadecimal, matches <b>hexdigest</b>
+ * (which is optionally prefixed with a single dollar sign). Return false if
+ * <b>hexdigest</b> is malformed, or it doesn't match. */
+static int
+hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest,
+ const char *nickname, int is_named)
+{
+ char digest[DIGEST_LEN];
+ char nn_char='\0';
+ char nn_buf[MAX_NICKNAME_LEN+1];
+
+ if (hex_digest_nickname_decode(hexdigest, digest, &nn_char, nn_buf) == -1)
return 0;
- else if (len > HEX_DIGEST_LEN &&
- (hexdigest[HEX_DIGEST_LEN] == '=' ||
- hexdigest[HEX_DIGEST_LEN] == '~')) {
- if (strcasecmp(hexdigest+HEX_DIGEST_LEN+1, nickname))
+
+ if (nn_char == '=' || nn_char == '~') {
+ if (!nickname)
return 0;
- if (hexdigest[HEX_DIGEST_LEN] == '=' && !is_named)
+ if (strcasecmp(nn_buf, nickname))
+ return 0;
+ if (nn_char == '=' && !is_named)
return 0;
}
- if (base16_decode(digest, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
- return 0;
- return (tor_memeq(digest, identity_digest, DIGEST_LEN));
+ return tor_memeq(digest, identity_digest, DIGEST_LEN);
+}
+
+/* Return true iff <b>router</b> is listed as named in the current
+ * consensus. */
+int
+router_is_named(const routerinfo_t *router)
+{
+ const char *digest =
+ networkstatus_get_router_digest_by_nickname(router->nickname);
+
+ return (digest &&
+ tor_memeq(digest, router->cache_info.identity_digest, DIGEST_LEN));
}
/** Return true iff the digest of <b>router</b>'s identity key,
@@ -2275,10 +2311,12 @@ hex_digest_matches(const char *hexdigest, const char *identity_digest,
* optionally prefixed with a single dollar sign). Return false if
* <b>hexdigest</b> is malformed, or it doesn't match. */
static INLINE int
-router_hex_digest_matches(routerinfo_t *router, const char *hexdigest)
+router_hex_digest_matches(const routerinfo_t *router, const char *hexdigest)
{
- return hex_digest_matches(hexdigest, router->cache_info.identity_digest,
- router->nickname, router->is_named);
+ return hex_digest_nickname_matches(hexdigest,
+ router->cache_info.identity_digest,
+ router->nickname,
+ router_is_named(router));
}
/** Return true if <b>router</b>'s nickname matches <b>nickname</b>
@@ -2286,20 +2324,43 @@ router_hex_digest_matches(routerinfo_t *router, const char *hexdigest)
* matches a hexadecimal value stored in <b>nickname</b>. Return
* false otherwise. */
static int
-router_nickname_matches(routerinfo_t *router, const char *nickname)
+router_nickname_matches(const routerinfo_t *router, const char *nickname)
{
if (nickname[0]!='$' && !strcasecmp(router->nickname, nickname))
return 1;
return router_hex_digest_matches(router, nickname);
}
+/** Return true if <b>node</b>'s nickname matches <b>nickname</b>
+ * (case-insensitive), or if <b>node's</b> identity key digest
+ * matches a hexadecimal value stored in <b>nickname</b>. Return
+ * false otherwise. */
+static int
+node_nickname_matches(const node_t *node, const char *nickname)
+{
+ const char *n = node_get_nickname(node);
+ if (n && nickname[0]!='$' && !strcasecmp(n, nickname))
+ return 1;
+ return hex_digest_nickname_matches(nickname,
+ node->identity,
+ n,
+ node_is_named(node));
+}
+
/** Return the router in our routerlist whose (case-insensitive)
* nickname or (case-sensitive) hexadecimal key digest is
* <b>nickname</b>. Return NULL if no such router is known.
*/
-routerinfo_t *
+const routerinfo_t *
router_get_by_nickname(const char *nickname, int warn_if_unnamed)
{
+#if 1
+ const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
+ if (node)
+ return node->ri;
+ else
+ return NULL;
+#else
int maybedigest;
char digest[DIGEST_LEN];
routerinfo_t *best_match=NULL;
@@ -2345,15 +2406,14 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
if (warn_if_unnamed && n_matches > 1) {
smartlist_t *fps = smartlist_create();
int any_unwarned = 0;
- SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
- {
+ SMARTLIST_FOREACH_BEGIN(routerlist->routers, routerinfo_t *, router) {
routerstatus_t *rs;
char *desc;
size_t dlen;
char fp[HEX_DIGEST_LEN+1];
if (strcasecmp(router->nickname, nickname))
continue;
- rs = router_get_consensus_status_by_id(
+ rs = router_get_mutable_consensus_status_by_id(
router->cache_info.identity_digest);
if (rs && !rs->name_lookup_warned) {
rs->name_lookup_warned = 1;
@@ -2366,7 +2426,7 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
tor_snprintf(desc, dlen, "\"$%s\" for the one at %s:%d",
fp, router->address, router->or_port);
smartlist_add(fps, desc);
- });
+ } SMARTLIST_FOREACH_END(router);
if (any_unwarned) {
char *alternatives = smartlist_join_strings(fps, "; ",0,NULL);
log_warn(LD_CONFIG,
@@ -2379,7 +2439,7 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
SMARTLIST_FOREACH(fps, char *, cp, tor_free(cp));
smartlist_free(fps);
} else if (warn_if_unnamed) {
- routerstatus_t *rs = router_get_consensus_status_by_id(
+ routerstatus_t *rs = router_get_mutable_consensus_status_by_id(
best_match->cache_info.identity_digest);
if (rs && !rs->name_lookup_warned) {
char fp[HEX_DIGEST_LEN+1];
@@ -2395,27 +2455,15 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed)
}
return best_match;
}
-
return NULL;
-}
-
-/** Try to find a routerinfo for <b>digest</b>. If we don't have one,
- * return 1. If we do, ask tor_version_as_new_as() for the answer.
- */
-int
-router_digest_version_as_new_as(const char *digest, const char *cutoff)
-{
- routerinfo_t *router = router_get_by_digest(digest);
- if (!router)
- return 1;
- return tor_version_as_new_as(router->platform, cutoff);
+#endif
}
/** Return true iff <b>digest</b> is the digest of the identity key of a
* trusted directory matching at least one bit of <b>type</b>. If <b>type</b>
* is zero, any authority is okay. */
int
-router_digest_is_trusted_dir_type(const char *digest, authority_type_t type)
+router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type)
{
if (!trusted_dir_servers)
return 0;
@@ -2459,44 +2507,20 @@ hexdigest_to_digest(const char *hexdigest, char *digest)
/** Return the router in our routerlist whose hexadecimal key digest
* is <b>hexdigest</b>. Return NULL if no such router is known. */
-routerinfo_t *
+const routerinfo_t *
router_get_by_hexdigest(const char *hexdigest)
{
- char digest[DIGEST_LEN];
- size_t len;
- routerinfo_t *ri;
-
- tor_assert(hexdigest);
- if (!routerlist)
- return NULL;
- if (hexdigest[0]=='$')
- ++hexdigest;
- len = strlen(hexdigest);
- if (hexdigest_to_digest(hexdigest, digest) < 0)
+ if (is_legal_nickname(hexdigest))
return NULL;
- ri = router_get_by_digest(digest);
-
- if (ri && len > HEX_DIGEST_LEN) {
- if (hexdigest[HEX_DIGEST_LEN] == '=') {
- if (strcasecmp(ri->nickname, hexdigest+HEX_DIGEST_LEN+1) ||
- !ri->is_named)
- return NULL;
- } else if (hexdigest[HEX_DIGEST_LEN] == '~') {
- if (strcasecmp(ri->nickname, hexdigest+HEX_DIGEST_LEN+1))
- return NULL;
- } else {
- return NULL;
- }
- }
-
- return ri;
+ /* It's not a legal nickname, so it must be a hexdigest or nothing. */
+ return router_get_by_nickname(hexdigest, 1);
}
-/** Return the router in our routerlist whose 20-byte key digest
- * is <b>digest</b>. Return NULL if no such router is known. */
+/** As router_get_by_id_digest,but return a pointer that you're allowed to
+ * modify */
routerinfo_t *
-router_get_by_digest(const char *digest)
+router_get_mutable_by_digest(const char *digest)
{
tor_assert(digest);
@@ -2507,6 +2531,14 @@ router_get_by_digest(const char *digest)
return rimap_get(routerlist->identity_map, digest);
}
+/** Return the router in our routerlist whose 20-byte key digest
+ * is <b>digest</b>. Return NULL if no such router is known. */
+const routerinfo_t *
+router_get_by_id_digest(const char *digest)
+{
+ return router_get_mutable_by_digest(digest);
+}
+
/** Return the router in our routerlist whose 20-byte descriptor
* is <b>digest</b>. Return NULL if no such router is known. */
signed_descriptor_t *
@@ -2557,7 +2589,7 @@ extrainfo_get_by_descriptor_digest(const char *digest)
* The caller must not free the string returned.
*/
static const char *
-signed_descriptor_get_body_impl(signed_descriptor_t *desc,
+signed_descriptor_get_body_impl(const signed_descriptor_t *desc,
int with_annotations)
{
const char *r = NULL;
@@ -2606,7 +2638,7 @@ signed_descriptor_get_body_impl(signed_descriptor_t *desc,
* The caller must not free the string returned.
*/
const char *
-signed_descriptor_get_body(signed_descriptor_t *desc)
+signed_descriptor_get_body(const signed_descriptor_t *desc)
{
return signed_descriptor_get_body_impl(desc, 0);
}
@@ -2614,7 +2646,7 @@ signed_descriptor_get_body(signed_descriptor_t *desc)
/** As signed_descriptor_get_body(), but points to the beginning of the
* annotations section rather than the beginning of the descriptor. */
const char *
-signed_descriptor_get_annotations(signed_descriptor_t *desc)
+signed_descriptor_get_annotations(const signed_descriptor_t *desc)
{
return signed_descriptor_get_body_impl(desc, 1);
}
@@ -2667,7 +2699,6 @@ routerinfo_free(routerinfo_t *router)
}
addr_policy_list_free(router->exit_policy);
- /* XXXX Remove if this turns out to affect performance. */
memset(router, 77, sizeof(routerinfo_t));
tor_free(router);
@@ -2682,7 +2713,6 @@ extrainfo_free(extrainfo_t *extrainfo)
tor_free(extrainfo->cache_info.signed_descriptor_body);
tor_free(extrainfo->pending_sig);
- /* XXXX remove this if it turns out to slow us down. */
memset(extrainfo, 88, sizeof(extrainfo_t)); /* debug bad memory usage */
tor_free(extrainfo);
}
@@ -2696,7 +2726,6 @@ signed_descriptor_free(signed_descriptor_t *sd)
tor_free(sd->signed_descriptor_body);
- /* XXXX remove this once more bugs go away. */
memset(sd, 99, sizeof(signed_descriptor_t)); /* Debug bad mem usage */
tor_free(sd);
}
@@ -2802,8 +2831,7 @@ routerlist_insert(routerlist_t *rl, routerinfo_t *ri)
routerinfo_t *ri_old;
signed_descriptor_t *sd_old;
{
- /* XXXX Remove if this slows us down. */
- routerinfo_t *ri_generated = router_get_my_routerinfo();
+ const routerinfo_t *ri_generated = router_get_my_routerinfo();
tor_assert(ri_generated != ri);
}
tor_assert(ri->cache_info.routerlist_index == -1);
@@ -2825,6 +2853,7 @@ routerlist_insert(routerlist_t *rl, routerinfo_t *ri)
&ri->cache_info);
smartlist_add(rl->routers, ri);
ri->cache_info.routerlist_index = smartlist_len(rl->routers) - 1;
+ nodelist_add_routerinfo(ri);
router_dir_info_changed();
#ifdef DEBUG_ROUTERLIST
routerlist_assert_ok(rl);
@@ -2845,7 +2874,6 @@ extrainfo_insert(routerlist_t *rl, extrainfo_t *ei)
extrainfo_t *ei_tmp;
{
- /* XXXX remove this code if it slows us down. */
extrainfo_t *ei_generated = router_get_my_extrainfo();
tor_assert(ei_generated != ei);
}
@@ -2891,8 +2919,7 @@ static void
routerlist_insert_old(routerlist_t *rl, routerinfo_t *ri)
{
{
- /* XXXX remove this code if it slows us down. */
- routerinfo_t *ri_generated = router_get_my_routerinfo();
+ const routerinfo_t *ri_generated = router_get_my_routerinfo();
tor_assert(ri_generated != ri);
}
tor_assert(ri->cache_info.routerlist_index == -1);
@@ -2932,6 +2959,8 @@ routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old, time_t now)
tor_assert(0 <= idx && idx < smartlist_len(rl->routers));
tor_assert(smartlist_get(rl->routers, idx) == ri);
+ nodelist_remove_routerinfo(ri);
+
/* make sure the rephist module knows that it's not running */
rep_hist_note_router_unreachable(ri->cache_info.identity_digest, now);
@@ -3043,8 +3072,7 @@ routerlist_replace(routerlist_t *rl, routerinfo_t *ri_old,
routerinfo_t *ri_tmp;
extrainfo_t *ei_tmp;
{
- /* XXXX Remove this if it turns out to slow us down. */
- routerinfo_t *ri_generated = router_get_my_routerinfo();
+ const routerinfo_t *ri_generated = router_get_my_routerinfo();
tor_assert(ri_generated != ri_new);
}
tor_assert(ri_old != ri_new);
@@ -3054,6 +3082,9 @@ routerlist_replace(routerlist_t *rl, routerinfo_t *ri_old,
tor_assert(0 <= idx && idx < smartlist_len(rl->routers));
tor_assert(smartlist_get(rl->routers, idx) == ri_old);
+ nodelist_remove_routerinfo(ri_old);
+ nodelist_add_routerinfo(ri_new);
+
router_dir_info_changed();
if (idx >= 0) {
smartlist_set(rl->routers, idx, ri_new);
@@ -3200,28 +3231,25 @@ routerlist_reset_warnings(void)
void
router_set_status(const char *digest, int up)
{
- routerinfo_t *router;
- routerstatus_t *status;
+ node_t *node;
tor_assert(digest);
SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, d,
if (tor_memeq(d->digest, digest, DIGEST_LEN))
d->is_running = up);
- router = router_get_by_digest(digest);
- if (router) {
+ node = node_get_mutable_by_id(digest);
+ if (node) {
+#if 0
log_debug(LD_DIR,"Marking router %s as %s.",
- router_describe(router), up ? "up" : "down");
- if (!up && router_is_me(router) && !we_are_hibernating())
+ node_describe(node), up ? "up" : "down");
+#endif
+ if (!up && node_is_me(node) && !we_are_hibernating())
log_warn(LD_NET, "We just marked ourself as down. Are your external "
"addresses reachable?");
- router->is_running = up;
- }
- status = router_get_consensus_status_by_id(digest);
- if (status && status->is_running != up) {
- status->is_running = up;
- control_event_networkstatus_changed_single(status);
+ node->is_running = up;
}
+
router_dir_info_changed();
}
@@ -3250,11 +3278,12 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
int from_cache, int from_fetch)
{
const char *id_digest;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int authdir = authdir_mode_handles_descs(options, router->purpose);
int authdir_believes_valid = 0;
routerinfo_t *old_router;
- networkstatus_t *consensus = networkstatus_get_latest_consensus();
+ networkstatus_t *consensus =
+ networkstatus_get_latest_consensus_by_flavor(FLAV_NS);
const smartlist_t *networkstatus_v2_list = networkstatus_get_v2_list();
int in_consensus = 0;
@@ -3265,7 +3294,7 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
id_digest = router->cache_info.identity_digest;
- old_router = router_get_by_digest(id_digest);
+ old_router = router_get_mutable_by_digest(id_digest);
/* Make sure that we haven't already got this exact descriptor. */
if (sdmap_get(routerlist->desc_digest_map,
@@ -3297,12 +3326,12 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
if (authdir) {
if (authdir_wants_to_reject_router(router, msg,
- !from_cache && !from_fetch)) {
+ !from_cache && !from_fetch,
+ &authdir_believes_valid)) {
tor_assert(*msg);
routerinfo_free(router);
return ROUTER_AUTHDIR_REJECTS;
}
- authdir_believes_valid = router->is_valid;
} else if (from_fetch) {
/* Only check the descriptor digest against the network statuses when
* we are receiving in response to a fetch. */
@@ -3329,14 +3358,15 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
SMARTLIST_FOREACH(networkstatus_v2_list, networkstatus_v2_t *, ns,
{
routerstatus_t *rs =
- networkstatus_v2_find_entry(ns, id_digest);
+ networkstatus_v2_find_mutable_entry(ns, id_digest);
if (rs && tor_memeq(rs->descriptor_digest,
router->cache_info.signed_descriptor_digest,
DIGEST_LEN))
rs->need_to_mirror = 0;
});
if (consensus) {
- routerstatus_t *rs = networkstatus_vote_find_entry(consensus, id_digest);
+ routerstatus_t *rs = networkstatus_vote_find_mutable_entry(
+ consensus, id_digest);
if (rs && tor_memeq(rs->descriptor_digest,
router->cache_info.signed_descriptor_digest,
DIGEST_LEN)) {
@@ -3950,7 +3980,7 @@ router_load_extrainfo_from_string(const char *s, const char *eos,
static int
signed_desc_digest_is_recognized(signed_descriptor_t *desc)
{
- routerstatus_t *rs;
+ const routerstatus_t *rs;
networkstatus_t *consensus = networkstatus_get_latest_consensus();
int caches = directory_caches_dir_info(get_options());
const smartlist_t *networkstatus_v2_list = networkstatus_get_v2_list();
@@ -3974,6 +4004,16 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc)
return 0;
}
+/** Update downloads for router descriptors and/or microdescriptors as
+ * appropriate. */
+void
+update_all_descriptor_downloads(time_t now)
+{
+ update_router_descriptor_downloads(now);
+ update_microdesc_downloads(now);
+ launch_dummy_descriptor_download_as_needed(now, get_options());
+}
+
/** Clear all our timeouts for fetching v2 and v3 directory stuff, and then
* give it all a try again. */
void
@@ -3982,34 +4022,34 @@ routerlist_retry_directory_downloads(time_t now)
router_reset_status_download_failures();
router_reset_descriptor_download_failures();
update_networkstatus_downloads(now);
- update_router_descriptor_downloads(now);
+ update_all_descriptor_downloads(now);
}
-/** Return 1 if all running sufficiently-stable routers will reject
+/** Return 1 if all running sufficiently-stable routers we can use will reject
* addr:port, return 0 if any might accept it. */
int
-router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port,
- int need_uptime)
-{
+router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port,
+ int need_uptime)
+{ /* XXXX MOVE */
addr_policy_result_t r;
- if (!routerlist) return 1;
- SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, router,
- {
- if (router->is_running &&
- !router_is_unreliable(router, need_uptime, 0, 0)) {
- r = compare_addr_to_addr_policy(addr, port, router->exit_policy);
+ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) {
+ if (node->is_running &&
+ !node_is_unreliable(node, need_uptime, 0, 0)) {
+
+ r = compare_tor_addr_to_node_policy(addr, port, node);
+
if (r != ADDR_POLICY_REJECTED && r != ADDR_POLICY_PROBABLY_REJECTED)
return 0; /* this one could be ok. good enough. */
}
- });
+ } SMARTLIST_FOREACH_END(node);
return 1; /* all will reject. */
}
/** Return true iff <b>router</b> does not permit exit streams.
*/
int
-router_exit_policy_rejects_all(routerinfo_t *router)
+router_exit_policy_rejects_all(const routerinfo_t *router)
{
return router->policy_is_reject_star;
}
@@ -4022,7 +4062,7 @@ trusted_dir_server_t *
add_trusted_dir_server(const char *nickname, const char *address,
uint16_t dir_port, uint16_t or_port,
const char *digest, const char *v3_auth_digest,
- authority_type_t type)
+ dirinfo_type_t type)
{
trusted_dir_server_t *ent;
uint32_t a;
@@ -4057,7 +4097,7 @@ add_trusted_dir_server(const char *nickname, const char *address,
ent->is_running = 1;
ent->type = type;
memcpy(ent->digest, digest, DIGEST_LEN);
- if (v3_auth_digest && (type & V3_AUTHORITY))
+ if (v3_auth_digest && (type & V3_DIRINFO))
memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN);
dlen = 64 + strlen(hostname) + (nickname?strlen(nickname):0);
@@ -4136,7 +4176,7 @@ int
any_trusted_dir_is_v1_authority(void)
{
if (trusted_dir_servers)
- return get_n_authorities(V1_AUTHORITY) > 0;
+ return get_n_authorities(V1_DIRINFO) > 0;
return 0;
}
@@ -4144,7 +4184,9 @@ any_trusted_dir_is_v1_authority(void)
/** For every current directory connection whose purpose is <b>purpose</b>,
* and where the resource being downloaded begins with <b>prefix</b>, split
* rest of the resource into base16 fingerprints, decode them, and set the
- * corresponding elements of <b>result</b> to a nonzero value. */
+ * corresponding elements of <b>result</b> to a nonzero value.
+ * DOCDOC purpose==microdesc
+ */
static void
list_pending_downloads(digestmap_t *result,
int purpose, const char *prefix)
@@ -4152,20 +4194,23 @@ list_pending_downloads(digestmap_t *result,
const size_t p_len = strlen(prefix);
smartlist_t *tmp = smartlist_create();
smartlist_t *conns = get_connection_array();
+ int flags = DSR_HEX;
+ if (purpose == DIR_PURPOSE_FETCH_MICRODESC)
+ flags = DSR_DIGEST256|DSR_BASE64;
tor_assert(result);
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
if (conn->type == CONN_TYPE_DIR &&
conn->purpose == purpose &&
!conn->marked_for_close) {
const char *resource = TO_DIR_CONN(conn)->requested_resource;
if (!strcmpstart(resource, prefix))
dir_split_resource_into_fingerprints(resource + p_len,
- tmp, NULL, DSR_HEX);
+ tmp, NULL, flags);
}
- });
+ } SMARTLIST_FOREACH_END(conn);
+
SMARTLIST_FOREACH(tmp, char *, d,
{
digestmap_set(result, d, (void*)1);
@@ -4185,13 +4230,21 @@ list_pending_descriptor_downloads(digestmap_t *result, int extrainfo)
list_pending_downloads(result, purpose, "d/");
}
-/** Launch downloads for all the descriptors whose digests are listed
- * as digests[i] for lo <= i < hi. (Lo and hi may be out of range.)
- * If <b>source</b> is given, download from <b>source</b>; otherwise,
- * download from an appropriate random directory server.
+/** DOCDOC */
+/*XXXX NM should use digest256, if one comes into being. */
+void
+list_pending_microdesc_downloads(digestmap_t *result)
+{
+ list_pending_downloads(result, DIR_PURPOSE_FETCH_MICRODESC, "d/");
+}
+
+/** Launch downloads for all the descriptors whose digests or digests256
+ * are listed as digests[i] for lo <= i < hi. (Lo and hi may be out of
+ * range.) If <b>source</b> is given, download from <b>source</b>;
+ * otherwise, download from an appropriate random directory server.
*/
static void
-initiate_descriptor_downloads(routerstatus_t *source,
+initiate_descriptor_downloads(const routerstatus_t *source,
int purpose,
smartlist_t *digests,
int lo, int hi, int pds_flags)
@@ -4199,6 +4252,20 @@ initiate_descriptor_downloads(routerstatus_t *source,
int i, n = hi-lo;
char *resource, *cp;
size_t r_len;
+
+ int digest_len = DIGEST_LEN, enc_digest_len = HEX_DIGEST_LEN;
+ char sep = '+';
+ int b64_256 = 0;
+
+ if (purpose == DIR_PURPOSE_FETCH_MICRODESC) {
+ /* Microdescriptors are downloaded by "-"-separated base64-encoded
+ * 256-bit digests. */
+ digest_len = DIGEST256_LEN;
+ enc_digest_len = BASE64_DIGEST256_LEN;
+ sep = '-';
+ b64_256 = 1;
+ }
+
if (n <= 0)
return;
if (lo < 0)
@@ -4206,15 +4273,19 @@ initiate_descriptor_downloads(routerstatus_t *source,
if (hi > smartlist_len(digests))
hi = smartlist_len(digests);
- r_len = 8 + (HEX_DIGEST_LEN+1)*n;
+ r_len = 8 + (enc_digest_len+1)*n;
cp = resource = tor_malloc(r_len);
memcpy(cp, "d/", 2);
cp += 2;
for (i = lo; i < hi; ++i) {
- base16_encode(cp, r_len-(cp-resource),
- smartlist_get(digests,i), DIGEST_LEN);
- cp += HEX_DIGEST_LEN;
- *cp++ = '+';
+ if (b64_256) {
+ digest256_to_base64(cp, smartlist_get(digests, i));
+ } else {
+ base16_encode(cp, r_len-(cp-resource),
+ smartlist_get(digests,i), digest_len);
+ }
+ cp += enc_digest_len;
+ *cp++ = sep;
}
memcpy(cp-1, ".z", 3);
@@ -4235,9 +4306,10 @@ initiate_descriptor_downloads(routerstatus_t *source,
* running, or otherwise not a descriptor that we would make any
* use of even if we had it. Else return 1. */
static INLINE int
-client_would_use_router(routerstatus_t *rs, time_t now, or_options_t *options)
+client_would_use_router(const routerstatus_t *rs, time_t now,
+ const or_options_t *options)
{
- if (!rs->is_running && !options->FetchUselessDescriptors) {
+ if (!rs->is_flagged_running && !options->FetchUselessDescriptors) {
/* If we had this router descriptor, we wouldn't even bother using it.
* But, if we want to have a complete list, fetch it anyway. */
return 0;
@@ -4261,6 +4333,7 @@ client_would_use_router(routerstatus_t *rs, time_t now, or_options_t *options)
* So use 96 because it's a nice number.
*/
#define MAX_DL_PER_REQUEST 96
+#define MAX_MICRODESC_DL_PER_REQUEST 92
/** Don't split our requests so finely that we are requesting fewer than
* this number per server. */
#define MIN_DL_PER_REQUEST 4
@@ -4275,35 +4348,49 @@ client_would_use_router(routerstatus_t *rs, time_t now, or_options_t *options)
* them until they have more, or until this amount of time has passed. */
#define MAX_CLIENT_INTERVAL_WITHOUT_REQUEST (10*60)
-/** Given a list of router descriptor digests in <b>downloadable</b>, decide
- * whether to delay fetching until we have more. If we don't want to delay,
- * launch one or more requests to the appropriate directory authorities. */
-static void
-launch_router_descriptor_downloads(smartlist_t *downloadable,
- routerstatus_t *source, time_t now)
+/** Given a <b>purpose</b> (FETCH_MICRODESC or FETCH_SERVERDESC) and a list of
+ * router descriptor digests or microdescriptor digest256s in
+ * <b>downloadable</b>, decide whether to delay fetching until we have more.
+ * If we don't want to delay, launch one or more requests to the appropriate
+ * directory authorities.
+ */
+void
+launch_descriptor_downloads(int purpose,
+ smartlist_t *downloadable,
+ const routerstatus_t *source, time_t now)
{
int should_delay = 0, n_downloadable;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
+ const char *descname;
+
+ tor_assert(purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
+ purpose == DIR_PURPOSE_FETCH_MICRODESC);
+
+ descname = (purpose == DIR_PURPOSE_FETCH_SERVERDESC) ?
+ "routerdesc" : "microdesc";
n_downloadable = smartlist_len(downloadable);
if (!directory_fetches_dir_info_early(options)) {
if (n_downloadable >= MAX_DL_TO_DELAY) {
log_debug(LD_DIR,
- "There are enough downloadable routerdescs to launch requests.");
+ "There are enough downloadable %ss to launch requests.",
+ descname);
should_delay = 0;
} else {
- should_delay = (last_routerdesc_download_attempted +
+ should_delay = (last_descriptor_download_attempted +
MAX_CLIENT_INTERVAL_WITHOUT_REQUEST) > now;
if (!should_delay && n_downloadable) {
- if (last_routerdesc_download_attempted) {
+ if (last_descriptor_download_attempted) {
log_info(LD_DIR,
- "There are not many downloadable routerdescs, but we've "
+ "There are not many downloadable %ss, but we've "
"been waiting long enough (%d seconds). Downloading.",
- (int)(now-last_routerdesc_download_attempted));
+ descname,
+ (int)(now-last_descriptor_download_attempted));
} else {
log_info(LD_DIR,
- "There are not many downloadable routerdescs, but we haven't "
- "tried downloading descriptors recently. Downloading.");
+ "There are not many downloadable %ss, but we haven't "
+ "tried downloading descriptors recently. Downloading.",
+ descname);
}
}
}
@@ -4330,12 +4417,19 @@ launch_router_descriptor_downloads(smartlist_t *downloadable,
* update_router_descriptor_downloads() later on, once the connections
* have succeeded or failed.
*/
- pds_flags |= PDS_NO_EXISTING_SERVERDESC_FETCH;
+ pds_flags |= (purpose == DIR_PURPOSE_FETCH_MICRODESC) ?
+ PDS_NO_EXISTING_MICRODESC_FETCH :
+ PDS_NO_EXISTING_SERVERDESC_FETCH;
}
n_per_request = CEIL_DIV(n_downloadable, MIN_REQUESTS);
- if (n_per_request > MAX_DL_PER_REQUEST)
- n_per_request = MAX_DL_PER_REQUEST;
+ if (purpose == DIR_PURPOSE_FETCH_MICRODESC) {
+ if (n_per_request > MAX_MICRODESC_DL_PER_REQUEST)
+ n_per_request = MAX_MICRODESC_DL_PER_REQUEST;
+ } else {
+ if (n_per_request > MAX_DL_PER_REQUEST)
+ n_per_request = MAX_DL_PER_REQUEST;
+ }
if (n_per_request < MIN_DL_PER_REQUEST)
n_per_request = MIN_DL_PER_REQUEST;
@@ -4350,11 +4444,11 @@ launch_router_descriptor_downloads(smartlist_t *downloadable,
req_plural, n_downloadable, rtr_plural, n_per_request);
smartlist_sort_digests(downloadable);
for (i=0; i < n_downloadable; i += n_per_request) {
- initiate_descriptor_downloads(source, DIR_PURPOSE_FETCH_SERVERDESC,
+ initiate_descriptor_downloads(source, purpose,
downloadable, i, i+n_per_request,
pds_flags);
}
- last_routerdesc_download_attempted = now;
+ last_descriptor_download_attempted = now;
}
}
@@ -4370,7 +4464,7 @@ update_router_descriptor_cache_downloads_v2(time_t now)
digestmap_t *map; /* Which descs are in progress, or assigned? */
int i, j, n;
int n_download;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
const smartlist_t *networkstatus_v2_list = networkstatus_get_v2_list();
if (! directory_fetches_dir_info_early(options)) {
@@ -4512,7 +4606,7 @@ void
update_consensus_router_descriptor_downloads(time_t now, int is_vote,
networkstatus_t *consensus)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
digestmap_t *map = NULL;
smartlist_t *no_longer_old = smartlist_create();
smartlist_t *downloadable = smartlist_create();
@@ -4540,15 +4634,14 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
map = digestmap_new();
list_pending_descriptor_downloads(map, 0);
- SMARTLIST_FOREACH(consensus->routerstatus_list, void *, rsp,
- {
+ SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, void *, rsp) {
routerstatus_t *rs =
is_vote ? &(((vote_routerstatus_t *)rsp)->status) : rsp;
signed_descriptor_t *sd;
if ((sd = router_get_by_descriptor_digest(rs->descriptor_digest))) {
- routerinfo_t *ri;
+ const routerinfo_t *ri;
++n_have;
- if (!(ri = router_get_by_digest(rs->identity_digest)) ||
+ if (!(ri = router_get_by_id_digest(rs->identity_digest)) ||
tor_memneq(ri->cache_info.signed_descriptor_digest,
sd->signed_descriptor_digest, DIGEST_LEN)) {
/* We have a descriptor with this digest, but either there is no
@@ -4581,7 +4674,8 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
if (is_vote && source) {
char time_bufnew[ISO_TIME_LEN+1];
char time_bufold[ISO_TIME_LEN+1];
- routerinfo_t *oldrouter = router_get_by_digest(rs->identity_digest);
+ const routerinfo_t *oldrouter;
+ oldrouter = router_get_by_id_digest(rs->identity_digest);
format_iso_time(time_bufnew, rs->published_on);
if (oldrouter)
format_iso_time(time_bufold, oldrouter->cache_info.published_on);
@@ -4592,7 +4686,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
source->nickname, oldrouter ? "known" : "unknown");
}
smartlist_add(downloadable, rs->descriptor_digest);
- });
+ } SMARTLIST_FOREACH_END(rsp);
if (!authdir_mode_handles_descs(options, ROUTER_PURPOSE_GENERAL)
&& smartlist_len(no_longer_old)) {
@@ -4624,7 +4718,8 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
smartlist_len(downloadable), n_delayed, n_have, n_in_oldrouters,
n_would_reject, n_wouldnt_use, n_inprogress);
- launch_router_descriptor_downloads(downloadable, source, now);
+ launch_descriptor_downloads(DIR_PURPOSE_FETCH_SERVERDESC,
+ downloadable, source, now);
digestmap_free(map, NULL);
done:
@@ -4638,27 +4733,20 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
* do this only when we aren't seeing incoming data. see bug 652. */
#define DUMMY_DOWNLOAD_INTERVAL (20*60)
-/** Launch downloads for router status as needed. */
-void
-update_router_descriptor_downloads(time_t now)
+/** As needed, launch a dummy router descriptor fetch to see if our
+ * address has changed. */
+static void
+launch_dummy_descriptor_download_as_needed(time_t now,
+ const or_options_t *options)
{
- or_options_t *options = get_options();
static time_t last_dummy_download = 0;
- if (should_delay_dir_fetches(options))
- return;
- if (directory_fetches_dir_info_early(options)) {
- update_router_descriptor_cache_downloads_v2(now);
- }
- update_consensus_router_descriptor_downloads(now, 0,
- networkstatus_get_reasonably_live_consensus(now));
-
/* XXXX023 we could be smarter here; see notes on bug 652. */
/* If we're a server that doesn't have a configured address, we rely on
* directory fetches to learn when our address changes. So if we haven't
* tried to get any routerdescs in a long time, try a dummy fetch now. */
if (!options->Address &&
server_mode(options) &&
- last_routerdesc_download_attempted + DUMMY_DOWNLOAD_INTERVAL < now &&
+ last_descriptor_download_attempted + DUMMY_DOWNLOAD_INTERVAL < now &&
last_dummy_download + DUMMY_DOWNLOAD_INTERVAL < now) {
last_dummy_download = now;
directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,
@@ -4667,11 +4755,28 @@ update_router_descriptor_downloads(time_t now)
}
}
+/** Launch downloads for router status as needed. */
+void
+update_router_descriptor_downloads(time_t now)
+{
+ const or_options_t *options = get_options();
+ if (should_delay_dir_fetches(options))
+ return;
+ if (!we_fetch_router_descriptors(options))
+ return;
+ if (directory_fetches_dir_info_early(options)) {
+ update_router_descriptor_cache_downloads_v2(now);
+ }
+
+ update_consensus_router_descriptor_downloads(now, 0,
+ networkstatus_get_reasonably_live_consensus(now, FLAV_NS));
+}
+
/** Launch extrainfo downloads as needed. */
void
update_extrainfo_downloads(time_t now)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
routerlist_t *rl;
smartlist_t *wanted;
digestmap_t *pending;
@@ -4699,7 +4804,7 @@ update_extrainfo_downloads(time_t now)
sd = &((routerinfo_t*)smartlist_get(lst, i))->cache_info;
if (sd->is_extrainfo)
continue; /* This should never happen. */
- if (old_routers && !router_get_by_digest(sd->identity_digest))
+ if (old_routers && !router_get_by_id_digest(sd->identity_digest))
continue; /* Couldn't check the signature if we got it. */
if (sd->extrainfo_is_bogus)
continue;
@@ -4793,23 +4898,31 @@ get_dir_info_status_string(void)
static void
count_usable_descriptors(int *num_present, int *num_usable,
const networkstatus_t *consensus,
- or_options_t *options, time_t now,
+ const or_options_t *options, time_t now,
routerset_t *in_set)
{
+ const int md = (consensus->flavor == FLAV_MICRODESC);
*num_present = 0, *num_usable=0;
- SMARTLIST_FOREACH(consensus->routerstatus_list, routerstatus_t *, rs,
- {
- if (in_set && ! routerset_contains_routerstatus(in_set, rs))
+ SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, routerstatus_t *, rs)
+ {
+ if (in_set && ! routerset_contains_routerstatus(in_set, rs, -1))
continue;
if (client_would_use_router(rs, now, options)) {
+ const char * const digest = rs->descriptor_digest;
+ int present;
++*num_usable; /* the consensus says we want it. */
- if (router_get_by_descriptor_digest(rs->descriptor_digest)) {
+ if (md)
+ present = NULL != microdesc_cache_lookup_by_digest256(NULL, digest);
+ else
+ present = NULL != router_get_by_descriptor_digest(digest);
+ if (present) {
/* we have the descriptor listed in the consensus. */
++*num_present;
}
}
- });
+ }
+ SMARTLIST_FOREACH_END(rs);
log_debug(LD_DIR, "%d usable, %d present.", *num_usable, *num_present);
}
@@ -4823,7 +4936,7 @@ count_loading_descriptors_progress(void)
int num_present = 0, num_usable=0;
time_t now = time(NULL);
const networkstatus_t *consensus =
- networkstatus_get_reasonably_live_consensus(now);
+ networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor());
double fraction;
if (!consensus)
@@ -4851,16 +4964,17 @@ update_router_have_minimum_dir_info(void)
int num_present = 0, num_usable=0;
time_t now = time(NULL);
int res;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
const networkstatus_t *consensus =
- networkstatus_get_reasonably_live_consensus(now);
+ networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor());
+ int using_md;
if (!consensus) {
if (!networkstatus_get_latest_consensus())
- strlcpy(dir_info_status, "We have no network-status consensus.",
+ strlcpy(dir_info_status, "We have no usable consensus.",
sizeof(dir_info_status));
else
- strlcpy(dir_info_status, "We have no recent network-status consensus.",
+ strlcpy(dir_info_status, "We have no recent usable consensus.",
sizeof(dir_info_status));
res = 0;
goto done;
@@ -4874,19 +4988,22 @@ update_router_have_minimum_dir_info(void)
goto done;
}
+ using_md = consensus->flavor == FLAV_MICRODESC;
+
count_usable_descriptors(&num_present, &num_usable, consensus, options, now,
NULL);
if (num_present < num_usable/4) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
- "We have only %d/%d usable descriptors.", num_present, num_usable);
+ "We have only %d/%d usable %sdescriptors.",
+ num_present, num_usable, using_md ? "micro" : "");
res = 0;
control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
goto done;
} else if (num_present < 2) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
- "Only %d descriptor%s here and believed reachable!",
- num_present, num_present ? "" : "s");
+ "Only %d %sdescriptor%s here and believed reachable!",
+ num_present, using_md ? "micro" : "", num_present ? "" : "s");
res = 0;
goto done;
}
@@ -4898,8 +5015,8 @@ update_router_have_minimum_dir_info(void)
if (!num_usable || !num_present) {
tor_snprintf(dir_info_status, sizeof(dir_info_status),
- "We have only %d/%d usable entry node descriptors.",
- num_present, num_usable);
+ "We have only %d/%d usable entry node %sdescriptors.",
+ num_present, num_usable, using_md?"micro":"");
res = 0;
goto done;
}
@@ -4939,7 +5056,7 @@ void
router_reset_descriptor_download_failures(void)
{
networkstatus_reset_download_failures();
- last_routerdesc_download_attempted = 0;
+ last_descriptor_download_attempted = 0;
if (!routerlist)
return;
SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
@@ -4963,7 +5080,7 @@ router_reset_descriptor_download_failures(void)
* would not cause a recent (post 0.1.1.6) dirserver to republish.
*/
int
-router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2)
+router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2)
{
time_t r1pub, r2pub;
long time_difference;
@@ -4971,7 +5088,7 @@ router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2)
/* r1 should be the one that was published first. */
if (r1->cache_info.published_on > r2->cache_info.published_on) {
- routerinfo_t *ri_tmp = r2;
+ const routerinfo_t *ri_tmp = r2;
r2 = r1;
r1 = ri_tmp;
}
@@ -4990,7 +5107,6 @@ router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2)
(r1->contact_info && r2->contact_info &&
strcasecmp(r1->contact_info, r2->contact_info)) ||
r1->is_hibernating != r2->is_hibernating ||
- r1->has_old_dnsworkers != r2->has_old_dnsworkers ||
cmp_addr_policies(r1->exit_policy, r2->exit_policy))
return 0;
if ((r1->declared_family == NULL) != (r2->declared_family == NULL))
@@ -5045,7 +5161,8 @@ router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2)
* incompatibility (if any).
**/
int
-routerinfo_incompatible_with_extrainfo(routerinfo_t *ri, extrainfo_t *ei,
+routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
+ extrainfo_t *ei,
signed_descriptor_t *sd,
const char **msg)
{
@@ -5053,7 +5170,7 @@ routerinfo_incompatible_with_extrainfo(routerinfo_t *ri, extrainfo_t *ei,
tor_assert(ri);
tor_assert(ei);
if (!sd)
- sd = &ri->cache_info;
+ sd = (signed_descriptor_t*)&ri->cache_info;
if (ei->bad_sig) {
if (msg) *msg = "Extrainfo signature was bad, or signed with wrong key.";
@@ -5118,7 +5235,7 @@ routerinfo_incompatible_with_extrainfo(routerinfo_t *ri, extrainfo_t *ei,
/** Assert that the internal representation of <b>rl</b> is
* self-consistent. */
void
-routerlist_assert_ok(routerlist_t *rl)
+routerlist_assert_ok(const routerlist_t *rl)
{
routerinfo_t *r2;
signed_descriptor_t *sd2;
@@ -5208,7 +5325,7 @@ routerlist_assert_ok(routerlist_t *rl)
* If <b>router</b> is NULL, it just frees its internal memory and returns.
*/
const char *
-esc_router_info(routerinfo_t *router)
+esc_router_info(const routerinfo_t *router)
{
static char *info=NULL;
char *esc_contact, *esc_platform;
@@ -5310,38 +5427,6 @@ routerset_get_countryname(const char *c)
return country;
}
-#if 0
-/** Add the GeoIP database's integer index (+1) of a valid two-character
- * country code to the routerset's <b>countries</b> bitarray. Return the
- * integer index if the country code is valid, -1 otherwise.*/
-static int
-routerset_add_country(const char *c)
-{
- char country[3];
- country_t cc;
-
- /* XXXX: Country codes must be of the form \{[a-z\?]{2}\} but this accepts
- \{[.]{2}\}. Do we need to be strict? -RH */
- /* Nope; if the country code is bad, we'll get 0 when we look it up. */
-
- if (!geoip_is_loaded()) {
- log(LOG_WARN, LD_CONFIG, "GeoIP database not loaded: Cannot add country"
- "entry %s, ignoring.", c);
- return -1;
- }
-
- memcpy(country, c+1, 2);
- country[2] = '\0';
- tor_strlower(country);
-
- if ((cc=geoip_get_country(country))==-1) {
- log(LOG_WARN, LD_CONFIG, "Country code '%s' is not valid, ignoring.",
- country);
- }
- return cc;
-}
-#endif
-
/** Update the routerset's <b>countries</b> bitarray_t. Called whenever
* the GeoIP database is reloaded.
*/
@@ -5430,7 +5515,7 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
void
refresh_all_country_info(void)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (options->EntryNodes)
routerset_refresh_countries(options->EntryNodes);
@@ -5443,7 +5528,7 @@ refresh_all_country_info(void)
if (options->_ExcludeExitNodesUnion)
routerset_refresh_countries(options->_ExcludeExitNodesUnion);
- routerlist_refresh_countries();
+ nodelist_refresh_countries();
}
/** Add all members of the set <b>source</b> to <b>target</b>. */
@@ -5493,11 +5578,11 @@ routerset_is_empty(const routerset_t *set)
static int
routerset_contains(const routerset_t *set, const tor_addr_t *addr,
uint16_t orport,
- const char *nickname, const char *id_digest, int is_named,
+ const char *nickname, const char *id_digest,
country_t country)
{
- if (!set || !set->list) return 0;
- (void) is_named; /* not supported */
+ if (!set || !set->list)
+ return 0;
if (nickname && strmap_get_lc(set->names, nickname))
return 4;
if (id_digest && digestmap_get(set->digests, id_digest))
@@ -5525,13 +5610,14 @@ routerset_contains_extendinfo(const routerset_t *set, const extend_info_t *ei)
ei->port,
ei->nickname,
ei->identity_digest,
- -1, /*is_named*/
-1 /*country*/);
}
-/** Return true iff <b>ri</b> is in <b>set</b>. */
+/** Return true iff <b>ri</b> is in <b>set</b>. If country is <b>-1</b>, we
+ * look up the country. */
int
-routerset_contains_router(const routerset_t *set, routerinfo_t *ri)
+routerset_contains_router(const routerset_t *set, const routerinfo_t *ri,
+ country_t country)
{
tor_addr_t addr;
tor_addr_from_ipv4h(&addr, ri->addr);
@@ -5540,13 +5626,15 @@ routerset_contains_router(const routerset_t *set, routerinfo_t *ri)
ri->or_port,
ri->nickname,
ri->cache_info.identity_digest,
- ri->is_named,
- ri->country);
+ country);
}
-/** Return true iff <b>rs</b> is in <b>set</b>. */
+/** Return true iff <b>rs</b> is in <b>set</b>. If country is <b>-1</b>, we
+ * look up the country. */
int
-routerset_contains_routerstatus(const routerset_t *set, routerstatus_t *rs)
+routerset_contains_routerstatus(const routerset_t *set,
+ const routerstatus_t *rs,
+ country_t country)
{
tor_addr_t addr;
tor_addr_from_ipv4h(&addr, rs->addr);
@@ -5555,50 +5643,59 @@ routerset_contains_routerstatus(const routerset_t *set, routerstatus_t *rs)
rs->or_port,
rs->nickname,
rs->identity_digest,
- rs->is_named,
- -1);
+ country);
+}
+
+/** Return true iff <b>node</b> is in <b>set</b>. */
+int
+routerset_contains_node(const routerset_t *set, const node_t *node)
+{
+ if (node->rs)
+ return routerset_contains_routerstatus(set, node->rs, node->country);
+ else if (node->ri)
+ return routerset_contains_router(set, node->ri, node->country);
+ else
+ return 0;
}
-/** Add every known routerinfo_t that is a member of <b>routerset</b> to
+/** Add every known node_t that is a member of <b>routerset</b> to
* <b>out</b>, but never add any that are part of <b>excludeset</b>.
* If <b>running_only</b>, only add the running ones. */
void
-routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset,
- const routerset_t *excludeset, int running_only)
-{
+routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
+ const routerset_t *excludeset, int running_only)
+{ /* XXXX MOVE */
tor_assert(out);
if (!routerset || !routerset->list)
return;
- if (!warned_nicknames)
- warned_nicknames = smartlist_create();
- if (routerset_is_list(routerset)) {
+ if (routerset_is_list(routerset)) {
/* No routers are specified by type; all are given by name or digest.
* we can do a lookup in O(len(routerset)). */
SMARTLIST_FOREACH(routerset->list, const char *, name, {
- routerinfo_t *router = router_get_by_nickname(name, 1);
- if (router) {
- if (!running_only || router->is_running)
- if (!routerset_contains_router(excludeset, router))
- smartlist_add(out, router);
+ const node_t *node = node_get_by_nickname(name, 1);
+ if (node) {
+ if (!running_only || node->is_running)
+ if (!routerset_contains_node(excludeset, node))
+ smartlist_add(out, (void*)node);
}
});
} else {
/* We need to iterate over the routerlist to get all the ones of the
* right kind. */
- routerlist_t *rl = router_get_routerlist();
- SMARTLIST_FOREACH(rl->routers, routerinfo_t *, router, {
- if (running_only && !router->is_running)
+ smartlist_t *nodes = nodelist_get_list();
+ SMARTLIST_FOREACH(nodes, const node_t *, node, {
+ if (running_only && !node->is_running)
continue;
- if (routerset_contains_router(routerset, router) &&
- !routerset_contains_router(excludeset, router))
- smartlist_add(out, router);
+ if (routerset_contains_node(routerset, node) &&
+ !routerset_contains_node(excludeset, node))
+ smartlist_add(out, (void*)node);
});
}
}
#if 0
-/** Add to <b>target</b> every routerinfo_t from <b>source</b> except:
+/** Add to <b>target</b> every node_t from <b>source</b> except:
*
* 1) Don't add it if <b>include</b> is non-empty and the relay isn't in
* <b>include</b>; and
@@ -5607,40 +5704,40 @@ routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset,
* 3) If <b>running_only</b>, don't add non-running routers.
*/
void
-routersets_get_disjunction(smartlist_t *target,
+routersets_get_node_disjunction(smartlist_t *target,
const smartlist_t *source,
const routerset_t *include,
const routerset_t *exclude, int running_only)
{
- SMARTLIST_FOREACH(source, routerinfo_t *, router, {
+ SMARTLIST_FOREACH(source, const node_t *, node, {
int include_result;
- if (running_only && !router->is_running)
+ if (running_only && !node->is_running)
continue;
if (!routerset_is_empty(include))
- include_result = routerset_contains_router(include, router);
+ include_result = routerset_contains_node(include, node);
else
include_result = 1;
if (include_result) {
- int exclude_result = routerset_contains_router(exclude, router);
+ int exclude_result = routerset_contains_node(exclude, node);
if (include_result >= exclude_result)
- smartlist_add(target, router);
+ smartlist_add(target, (void*)node);
}
});
}
#endif
-/** Remove every routerinfo_t from <b>lst</b> that is in <b>routerset</b>. */
+/** Remove every node_t from <b>lst</b> that is in <b>routerset</b>. */
void
-routerset_subtract_routers(smartlist_t *lst, const routerset_t *routerset)
-{
+routerset_subtract_nodes(smartlist_t *lst, const routerset_t *routerset)
+{ /*XXXX MOVE ? */
tor_assert(lst);
if (!routerset)
return;
- SMARTLIST_FOREACH(lst, routerinfo_t *, r, {
- if (routerset_contains_router(routerset, r)) {
+ SMARTLIST_FOREACH(lst, const node_t *, node, {
+ if (routerset_contains_node(routerset, node)) {
//log_debug(LD_DIR, "Subtracting %s",r->nickname);
- SMARTLIST_DEL_CURRENT(lst, r);
+ SMARTLIST_DEL_CURRENT(lst, node);
}
});
}
@@ -5706,18 +5803,23 @@ routerset_free(routerset_t *routerset)
/** Refresh the country code of <b>ri</b>. This function MUST be called on
* each router when the GeoIP database is reloaded, and on all new routers. */
void
-routerinfo_set_country(routerinfo_t *ri)
+node_set_country(node_t *node)
{
- ri->country = geoip_get_country_by_ip(ri->addr);
+ if (node->rs)
+ node->country = geoip_get_country_by_ip(node->rs->addr);
+ else if (node->ri)
+ node->country = geoip_get_country_by_ip(node->ri->addr);
+ else
+ node->country = -1;
}
/** Set the country code of all routers in the routerlist. */
void
-routerlist_refresh_countries(void)
+nodelist_refresh_countries(void) /* MOVE */
{
- routerlist_t *rl = router_get_routerlist();
- SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
- routerinfo_set_country(ri));
+ smartlist_t *nodes = nodelist_get_list();
+ SMARTLIST_FOREACH(nodes, node_t *, node,
+ node_set_country(node));
}
/** Determine the routers that are responsible for <b>id</b> (binary) and
@@ -5760,7 +5862,7 @@ hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
int
hid_serv_acting_as_directory(void)
{
- routerinfo_t *me = router_get_my_routerinfo();
+ const routerinfo_t *me = router_get_my_routerinfo();
if (!me)
return 0;
if (!get_options()->HidServDirectoryV2) {
@@ -5776,7 +5878,7 @@ hid_serv_acting_as_directory(void)
int
hid_serv_responsible_for_desc_id(const char *query)
{
- routerinfo_t *me;
+ const routerinfo_t *me;
routerstatus_t *last_rs;
const char *my_id, *last_id;
int result;
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index fec18705b..cae881433 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -11,7 +11,7 @@
#ifndef _TOR_ROUTERLIST_H
#define _TOR_ROUTERLIST_H
-int get_n_authorities(authority_type_t type);
+int get_n_authorities(dirinfo_type_t type);
int trusted_dirs_reload_certs(void);
int trusted_dirs_load_certs_from_string(const char *contents, int from_store,
int flush);
@@ -27,53 +27,50 @@ int router_reload_router_list(void);
int authority_cert_dl_looks_uncertain(const char *id_digest);
smartlist_t *router_get_trusted_dir_servers(void);
-routerstatus_t *router_pick_directory_server(authority_type_t type, int flags);
+const routerstatus_t *router_pick_directory_server(dirinfo_type_t type,
+ int flags);
trusted_dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
trusted_dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
-routerstatus_t *router_pick_trusteddirserver(authority_type_t type, int flags);
+const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
+ int flags);
int router_get_my_share_of_directory_requests(double *v2_share_out,
double *v3_share_out);
void router_reset_status_download_failures(void);
-void routerlist_add_family(smartlist_t *sl, routerinfo_t *router);
-int routers_in_same_family(routerinfo_t *r1, routerinfo_t *r2);
int routers_have_same_or_addr(const routerinfo_t *r1, const routerinfo_t *r2);
-void add_nickname_list_to_smartlist(smartlist_t *sl, const char *list,
- int must_be_running);
-int router_nickname_is_in_list(routerinfo_t *router, const char *list);
-routerinfo_t *routerlist_find_my_routerinfo(void);
-routerinfo_t *router_find_exact_exit_enclave(const char *address,
+int router_nickname_is_in_list(const routerinfo_t *router, const char *list);
+const routerinfo_t *routerlist_find_my_routerinfo(void);
+const node_t *router_find_exact_exit_enclave(const char *address,
uint16_t port);
-int router_is_unreliable(routerinfo_t *router, int need_uptime,
+int node_is_unreliable(const node_t *router, int need_uptime,
int need_capacity, int need_guard);
-uint32_t router_get_advertised_bandwidth(routerinfo_t *router);
-uint32_t router_get_advertised_bandwidth_capped(routerinfo_t *router);
+uint32_t router_get_advertised_bandwidth(const routerinfo_t *router);
+uint32_t router_get_advertised_bandwidth_capped(const routerinfo_t *router);
-routerinfo_t *routerlist_sl_choose_by_bandwidth(smartlist_t *sl,
- bandwidth_weight_rule_t rule);
-routerstatus_t *routerstatus_sl_choose_by_bandwidth(smartlist_t *sl,
- bandwidth_weight_rule_t rule);
+const node_t *node_sl_choose_by_bandwidth(smartlist_t *sl,
+ bandwidth_weight_rule_t rule);
-routerinfo_t *router_choose_random_node(smartlist_t *excludedsmartlist,
+const node_t *router_choose_random_node(smartlist_t *excludedsmartlist,
struct routerset_t *excludedset,
router_crn_flags_t flags);
-routerinfo_t *router_get_by_nickname(const char *nickname,
+const routerinfo_t *router_get_by_nickname(const char *nickname,
int warn_if_unnamed);
-int router_digest_version_as_new_as(const char *digest, const char *cutoff);
+int router_is_named(const routerinfo_t *router);
int router_digest_is_trusted_dir_type(const char *digest,
- authority_type_t type);
+ dirinfo_type_t type);
#define router_digest_is_trusted_dir(d) \
- router_digest_is_trusted_dir_type((d), NO_AUTHORITY)
+ router_digest_is_trusted_dir_type((d), NO_DIRINFO)
int router_addr_is_trusted_dir(uint32_t addr);
int hexdigest_to_digest(const char *hexdigest, char *digest);
-routerinfo_t *router_get_by_hexdigest(const char *hexdigest);
-routerinfo_t *router_get_by_digest(const char *digest);
+const routerinfo_t *router_get_by_hexdigest(const char *hexdigest);
+const routerinfo_t *router_get_by_id_digest(const char *digest);
+routerinfo_t *router_get_mutable_by_digest(const char *digest);
signed_descriptor_t *router_get_by_descriptor_digest(const char *digest);
signed_descriptor_t *router_get_by_extrainfo_digest(const char *digest);
signed_descriptor_t *extrainfo_get_by_descriptor_digest(const char *digest);
-const char *signed_descriptor_get_body(signed_descriptor_t *desc);
-const char *signed_descriptor_get_annotations(signed_descriptor_t *desc);
+const char *signed_descriptor_get_body(const signed_descriptor_t *desc);
+const char *signed_descriptor_get_annotations(const signed_descriptor_t *desc);
routerlist_t *router_get_routerlist(void);
void routerinfo_free(routerinfo_t *router);
void extrainfo_free(extrainfo_t *extrainfo);
@@ -132,63 +129,71 @@ void router_load_extrainfo_from_string(const char *s, const char *eos,
int descriptor_digests);
void routerlist_retry_directory_downloads(time_t now);
-int router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port,
- int need_uptime);
-int router_exit_policy_rejects_all(routerinfo_t *router);
+int router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port,
+ int need_uptime);
+
+int router_exit_policy_rejects_all(const routerinfo_t *router);
trusted_dir_server_t *add_trusted_dir_server(const char *nickname,
const char *address,
uint16_t dir_port, uint16_t or_port,
const char *digest, const char *v3_auth_digest,
- authority_type_t type);
+ dirinfo_type_t type);
void authority_cert_free(authority_cert_t *cert);
void clear_trusted_dir_servers(void);
int any_trusted_dir_is_v1_authority(void);
void update_consensus_router_descriptor_downloads(time_t now, int is_vote,
networkstatus_t *consensus);
void update_router_descriptor_downloads(time_t now);
+void update_all_descriptor_downloads(time_t now);
void update_extrainfo_downloads(time_t now);
int router_have_minimum_dir_info(void);
void router_dir_info_changed(void);
const char *get_dir_info_status_string(void);
int count_loading_descriptors_progress(void);
void router_reset_descriptor_download_failures(void);
-int router_differences_are_cosmetic(routerinfo_t *r1, routerinfo_t *r2);
-int routerinfo_incompatible_with_extrainfo(routerinfo_t *ri, extrainfo_t *ei,
+int router_differences_are_cosmetic(const routerinfo_t *r1,
+ const routerinfo_t *r2);
+int routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
+ extrainfo_t *ei,
signed_descriptor_t *sd,
const char **msg);
-void routerlist_assert_ok(routerlist_t *rl);
-const char *esc_router_info(routerinfo_t *router);
+void routerlist_assert_ok(const routerlist_t *rl);
+const char *esc_router_info(const routerinfo_t *router);
void routers_sort_by_identity(smartlist_t *routers);
routerset_t *routerset_new(void);
+void routerset_refresh_countries(routerset_t *rs);
int routerset_parse(routerset_t *target, const char *s,
const char *description);
void routerset_union(routerset_t *target, const routerset_t *source);
int routerset_is_list(const routerset_t *set);
int routerset_needs_geoip(const routerset_t *set);
int routerset_is_empty(const routerset_t *set);
-int routerset_contains_router(const routerset_t *set, routerinfo_t *ri);
+int routerset_contains_router(const routerset_t *set, const routerinfo_t *ri,
+ country_t country);
int routerset_contains_routerstatus(const routerset_t *set,
- routerstatus_t *rs);
+ const routerstatus_t *rs,
+ country_t country);
int routerset_contains_extendinfo(const routerset_t *set,
const extend_info_t *ei);
-void routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset,
- const routerset_t *excludeset,
- int running_only);
+
+int routerset_contains_node(const routerset_t *set, const node_t *node);
+void routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
+ const routerset_t *excludeset,
+ int running_only);
#if 0
-void routersets_get_disjunction(smartlist_t *target, const smartlist_t *source,
+void routersets_get_node_disjunction(smartlist_t *target,
+ const smartlist_t *source,
const routerset_t *include,
const routerset_t *exclude, int running_only);
#endif
-void routerset_subtract_routers(smartlist_t *out,
+void routerset_subtract_nodes(smartlist_t *out,
const routerset_t *routerset);
+
char *routerset_to_string(const routerset_t *routerset);
-void routerset_refresh_countries(routerset_t *target);
int routerset_equal(const routerset_t *old, const routerset_t *new);
void routerset_free(routerset_t *routerset);
-void routerinfo_set_country(routerinfo_t *ri);
-void routerlist_refresh_countries(void);
void refresh_all_country_info(void);
int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
@@ -196,5 +201,16 @@ int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
int hid_serv_acting_as_directory(void);
int hid_serv_responsible_for_desc_id(const char *id);
+void list_pending_microdesc_downloads(digestmap_t *result);
+void launch_descriptor_downloads(int purpose,
+ smartlist_t *downloadable,
+ const routerstatus_t *source,
+ time_t now);
+
+int hex_digest_nickname_decode(const char *hexdigest,
+ char *digest_out,
+ char *nickname_qualifier_out,
+ char *nickname_out);
+
#endif
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 001c4d6c7..e8b2dd7d2 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -70,7 +70,6 @@ typedef enum {
K_V,
K_W,
K_M,
- K_EVENTDNS,
K_EXTRA_INFO,
K_EXTRA_INFO_DIGEST,
K_CACHES_EXTRA_INFO,
@@ -287,7 +286,6 @@ static token_rule_t routerdesc_token_table[] = {
T01("family", K_FAMILY, ARGS, NO_OBJ ),
T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ),
- T01("eventdns", K_EVENTDNS, ARGS, NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ),
@@ -1357,7 +1355,6 @@ router_parse_entry_from_string(const char *s, const char *end,
tor_assert(tok->n_args >= 5);
router = tor_malloc_zero(sizeof(routerinfo_t));
- router->country = -1;
router->cache_info.routerlist_index = -1;
router->cache_info.annotations_len = s-start_of_annotations + prepend_len;
router->cache_info.signed_descriptor_len = end-s;
@@ -1503,13 +1500,6 @@ router_parse_entry_from_string(const char *s, const char *end,
router->contact_info = tor_strdup(tok->args[0]);
}
- if ((tok = find_opt_by_keyword(tokens, K_EVENTDNS))) {
- router->has_old_dnsworkers = tok->n_args && !strcmp(tok->args[0], "0");
- } else if (router->platform) {
- if (! tor_version_as_new_as(router->platform, "0.1.2.2-alpha"))
- router->has_old_dnsworkers = 1;
- }
-
if (find_opt_by_keyword(tokens, K_REJECT6) ||
find_opt_by_keyword(tokens, K_ACCEPT6)) {
log_warn(LD_DIR, "Rejecting router with reject6/accept6 line: they crash "
@@ -1574,8 +1564,6 @@ router_parse_entry_from_string(const char *s, const char *end,
"router descriptor") < 0)
goto err;
- routerinfo_set_country(router);
-
if (!router->or_port) {
log_warn(LD_DIR,"or_port unreadable or 0. Failing.");
goto err;
@@ -1823,9 +1811,9 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
struct in_addr in;
char *address = NULL;
tor_assert(tok->n_args);
- /* XXX023 use tor_addr_port_parse() below instead. -RD */
- if (parse_addr_port(LOG_WARN, tok->args[0], &address, NULL,
- &cert->dir_port)<0 ||
+ /* XXX023 use some tor_addr parse function below instead. -RD */
+ if (tor_addr_port_split(LOG_WARN, tok->args[0], &address,
+ &cert->dir_port) < 0 ||
tor_inet_aton(address, &in) == 0) {
log_warn(LD_DIR, "Couldn't parse dir-address in certificate");
tor_free(address);
@@ -1975,6 +1963,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
if (!consensus_method)
flav = FLAV_NS;
+ tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC);
eos = find_start_of_next_routerstatus(*s);
@@ -1987,15 +1976,16 @@ routerstatus_parse_entry_from_string(memarea_t *area,
goto err;
}
tok = find_by_keyword(tokens, K_R);
- tor_assert(tok->n_args >= 7);
+ tor_assert(tok->n_args >= 7); /* guaranteed by GE(7) in K_R setup */
if (flav == FLAV_NS) {
if (tok->n_args < 8) {
log_warn(LD_DIR, "Too few arguments to r");
goto err;
}
- } else {
- offset = -1;
+ } else if (flav == FLAV_MICRODESC) {
+ offset = -1; /* There is no identity digest */
}
+
if (vote_rs) {
rs = &vote_rs->status;
} else {
@@ -2069,7 +2059,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
else if (!strcmp(tok->args[i], "Fast"))
rs->is_fast = 1;
else if (!strcmp(tok->args[i], "Running"))
- rs->is_running = 1;
+ rs->is_flagged_running = 1;
else if (!strcmp(tok->args[i], "Named"))
rs->is_named = 1;
else if (!strcmp(tok->args[i], "Valid"))
@@ -2100,6 +2090,8 @@ routerstatus_parse_entry_from_string(memarea_t *area,
rs->version_supports_begindir = 1;
rs->version_supports_extrainfo_upload = 1;
rs->version_supports_conditional_consensus = 1;
+ rs->version_supports_microdesc_cache = 1;
+ rs->version_supports_optimistic_data = 1;
} else {
rs->version_supports_begindir =
tor_version_as_new_as(tok->args[0], "0.2.0.1-alpha");
@@ -2109,6 +2101,16 @@ routerstatus_parse_entry_from_string(memarea_t *area,
tor_version_as_new_as(tok->args[0], "0.2.0.8-alpha");
rs->version_supports_conditional_consensus =
tor_version_as_new_as(tok->args[0], "0.2.1.1-alpha");
+ /* XXXX023 NM microdescs: 0.2.3.1-alpha isn't widely used yet, but
+ * not all 0.2.3.0-alpha "versions" actually support microdesc cacheing
+ * right. There's a compromise here. Since this is 5 May, let's
+ * err on the side of having some possible caches to use. Once more
+ * caches are running 0.2.3.1-alpha, we can bump this version number.
+ */
+ rs->version_supports_microdesc_cache =
+ tor_version_as_new_as(tok->args[0], "0.2.3.0-alpha");
+ rs->version_supports_optimistic_data =
+ tor_version_as_new_as(tok->args[0], "0.2.3.1-alpha");
}
if (vote_rs) {
vote_rs->version = tor_strdup(tok->args[0]);
@@ -2171,6 +2173,16 @@ routerstatus_parse_entry_from_string(memarea_t *area,
vote_rs->microdesc = line;
}
} SMARTLIST_FOREACH_END(t);
+ } else if (flav == FLAV_MICRODESC) {
+ tok = find_opt_by_keyword(tokens, K_M);
+ if (tok) {
+ tor_assert(tok->n_args);
+ if (digest256_from_base64(rs->descriptor_digest, tok->args[0])) {
+ log_warn(LD_DIR, "Error decoding microdescriptor digest %s",
+ escaped(tok->args[0]));
+ goto err;
+ }
+ }
}
if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
@@ -3502,10 +3514,10 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
siglist = detached_get_signatures(sigs, flavor);
is_duplicate = 0;
- SMARTLIST_FOREACH(siglist, document_signature_t *, s, {
- if (s->alg == alg &&
- tor_memeq(id_digest, s->identity_digest, DIGEST_LEN) &&
- tor_memeq(sk_digest, s->signing_key_digest, DIGEST_LEN)) {
+ SMARTLIST_FOREACH(siglist, document_signature_t *, dsig, {
+ if (dsig->alg == alg &&
+ tor_memeq(id_digest, dsig->identity_digest, DIGEST_LEN) &&
+ tor_memeq(sk_digest, dsig->signing_key_digest, DIGEST_LEN)) {
is_duplicate = 1;
}
});
@@ -4358,7 +4370,7 @@ microdescs_parse_from_string(const char *s, const char *eos,
}
if ((tok = find_opt_by_keyword(tokens, K_P))) {
- md->exitsummary = tor_strdup(tok->args[0]);
+ md->exit_policy = parse_short_policy(tok->args[0]);
}
crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);
@@ -4969,7 +4981,7 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed,
info->identity_digest, DIGEST_LEN);
/* Parse IP address. */
tok = find_by_keyword(tokens, R_IPO_IP_ADDRESS);
- if (tor_addr_from_str(&info->addr, tok->args[0])<0) {
+ if (tor_addr_parse(&info->addr, tok->args[0])<0) {
log_warn(LD_REND, "Could not parse introduction point address.");
rend_intro_point_free(intro);
goto err;
diff --git a/src/or/status.c b/src/or/status.c
new file mode 100644
index 000000000..3e4cb779a
--- /dev/null
+++ b/src/or/status.c
@@ -0,0 +1,115 @@
+/* Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file status.c
+ * \brief Keep status information and log the heartbeat messages.
+ **/
+
+#include "or.h"
+#include "config.h"
+#include "status.h"
+#include "nodelist.h"
+#include "router.h"
+#include "circuitlist.h"
+#include "main.h"
+
+/** Return the total number of circuits. */
+static int
+count_circuits(void)
+{
+ circuit_t *circ;
+ int nr=0;
+
+ for (circ = _circuit_get_global_list(); circ; circ = circ->next)
+ nr++;
+
+ return nr;
+}
+
+/** Take seconds <b>secs</b> and return a newly allocated human-readable
+ * uptime string */
+static char *
+secs_to_uptime(long secs)
+{
+ long int days = secs / 86400;
+ int hours = (int)((secs - (days * 86400)) / 3600);
+ int minutes = (int)((secs - (days * 86400) - (hours * 3600)) / 60);
+ char *uptime_string = NULL;
+
+ switch (days) {
+ case 0:
+ tor_asprintf(&uptime_string, "%d:%02d hours", hours, minutes);
+ break;
+ case 1:
+ tor_asprintf(&uptime_string, "%ld day %d:%02d hours",
+ days, hours, minutes);
+ break;
+ default:
+ tor_asprintf(&uptime_string, "%ld days %d:%02d hours",
+ days, hours, minutes);
+ break;
+ }
+
+ return uptime_string;
+}
+
+/** Take <b>bytes</b> and returns a newly allocated human-readable usage
+ * string. */
+static char *
+bytes_to_usage(uint64_t bytes)
+{
+ char *bw_string = NULL;
+
+ if (bytes < (1<<20)) { /* Less than a megabyte. */
+ tor_asprintf(&bw_string, U64_FORMAT" kB", U64_PRINTF_ARG(bytes>>10));
+ } else if (bytes < (1<<30)) { /* Megabytes. Let's add some precision. */
+ double bw = U64_TO_DBL(bytes);
+ tor_asprintf(&bw_string, "%.2f MB", bw/(1<<20));
+ } else { /* Gigabytes. */
+ double bw = U64_TO_DBL(bytes);
+ tor_asprintf(&bw_string, "%.2f GB", bw/(1<<30));
+ }
+
+ return bw_string;
+}
+
+/** Log a "heartbeat" message describing Tor's status and history so that the
+ * user can know that there is indeed a running Tor. Return 0 on success and
+ * -1 on failure. */
+int
+log_heartbeat(time_t now)
+{
+ char *bw_sent = NULL;
+ char *bw_rcvd = NULL;
+ char *uptime = NULL;
+ const routerinfo_t *me;
+
+ const or_options_t *options = get_options();
+ (void)now;
+
+ if (public_server_mode(options)) {
+ /* Let's check if we are in the current cached consensus. */
+ if (!(me = router_get_my_routerinfo()))
+ return -1; /* Something stinks, we won't even attempt this. */
+ else
+ if (!node_get_by_id(me->cache_info.identity_digest))
+ log_fn(LOG_NOTICE, LD_HEARTBEAT, "Heartbeat: It seems like we are not "
+ "in the cached consensus.");
+ }
+
+ uptime = secs_to_uptime(get_uptime());
+ bw_rcvd = bytes_to_usage(get_bytes_read());
+ bw_sent = bytes_to_usage(get_bytes_written());
+
+ log_fn(LOG_NOTICE, LD_HEARTBEAT, "Heartbeat: Tor's uptime is %s, with %d "
+ "circuits open. I've sent %s and received %s.",
+ uptime, count_circuits(),bw_sent,bw_rcvd);
+
+ tor_free(uptime);
+ tor_free(bw_sent);
+ tor_free(bw_rcvd);
+
+ return 0;
+}
+
diff --git a/src/or/status.h b/src/or/status.h
new file mode 100644
index 000000000..ac726a1d2
--- /dev/null
+++ b/src/or/status.h
@@ -0,0 +1,10 @@
+/* Copyright (c) 2010, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef _TOR_STATUS_H
+#define _TOR_STATUS_H
+
+int log_heartbeat(time_t now);
+
+#endif
+
diff --git a/src/or/transports.c b/src/or/transports.c
new file mode 100644
index 000000000..6e8200f40
--- /dev/null
+++ b/src/or/transports.c
@@ -0,0 +1,1048 @@
+/* Copyright (c) 2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file transports.c
+ * \brief Pluggable Transports related code.
+ **/
+
+#define PT_PRIVATE
+#include "or.h"
+#include "config.h"
+#include "circuitbuild.h"
+#include "transports.h"
+#include "util.h"
+
+static void set_managed_proxy_environment(char ***envp,
+ const managed_proxy_t *mp);
+static INLINE int proxy_configuration_finished(const managed_proxy_t *mp);
+
+static void managed_proxy_destroy(managed_proxy_t *mp);
+
+static void handle_finished_proxy(managed_proxy_t *mp);
+static void configure_proxy(managed_proxy_t *mp);
+
+static void parse_method_error(const char *line, int is_server_method);
+#define parse_server_method_error(l) parse_method_error(l, 1)
+#define parse_client_method_error(l) parse_method_error(l, 0)
+
+static INLINE void free_execve_args(char **arg);
+
+/** Managed proxy protocol strings */
+#define PROTO_ENV_ERROR "ENV-ERROR"
+#define PROTO_NEG_SUCCESS "VERSION"
+#define PROTO_NEG_FAIL "VERSION-ERROR no-version"
+#define PROTO_CMETHOD "CMETHOD"
+#define PROTO_SMETHOD "SMETHOD"
+#define PROTO_CMETHOD_ERROR "CMETHOD-ERROR"
+#define PROTO_SMETHOD_ERROR "SMETHOD-ERROR"
+#define PROTO_CMETHODS_DONE "CMETHODS DONE"
+#define PROTO_SMETHODS_DONE "SMETHODS DONE"
+
+/* The smallest valid managed proxy protocol line that can
+ appear. It's the size of "VERSION 1" */
+#define SMALLEST_MANAGED_LINE_SIZE 9
+
+/** Number of environment variables for managed proxy clients/servers. */
+#define ENVIRON_SIZE_CLIENT 5
+#define ENVIRON_SIZE_SERVER 8
+
+/** The first and only supported - at the moment - configuration
+ protocol version. */
+#define PROTO_VERSION_ONE 1
+
+/** List of unconfigured managed proxies. */
+static smartlist_t *managed_proxy_list = NULL;
+/** Number of still unconfigured proxies. */
+static int unconfigured_proxies_n = 0;
+
+/** "The main idea is:"
+
+ Each managed proxy is represented by a 'managed_proxy_t'.
+ Each managed proxy can support multiple transports.
+ Each managed proxy gets configured through a multistep process.
+
+ 'managed_proxy_list' contains all the managed proxies this tor
+ instance is supporting.
+ In the 'managed_proxy_list' there are 'unconfigured_proxies_n'
+ managed proxies that are still unconfigured.
+
+ In every run_scheduled_event() tick, we attempt to launch and then
+ configure the unconfiged managed proxies, using the configuration
+ protocol defined in the 180_pluggable_transport.txt proposal. A
+ managed proxy might need several ticks to get fully configured.
+
+ When a managed proxy is fully configured, we register all its
+ transports to the circuitbuild.c subsystem. At that point the
+ transports are owned by the circuitbuild.c subsystem.
+
+ When a managed proxy fails to follow the 180 configuration
+ protocol, it gets marked as broken and gets destroyed.
+
+ "In a little more technical detail:"
+
+ While we are serially parsing torrc, we store all the transports
+ that a proxy should spawn in its 'transports_to_launch' element.
+
+ When we finish reading the torrc, we spawn the managed proxy and
+ expect {S,C}METHOD lines from its output. We add transports
+ described by METHOD lines to its 'transports' element, as
+ 'transport_t' structs.
+
+ When the managed proxy stops spitting METHOD lines (signified by a
+ '{S,C}METHODS DONE' message) we register all the transports
+ collected to the circuitbuild.c subsystem. At this point, the
+ 'transport_t's can be transformed into dangling pointers at any
+ point by the circuitbuild.c subsystem, and so we replace all
+ 'transport_t's with strings describing the transport names. We
+ can still go from a transport name to a 'transport_t' using the
+ fact that transport names uniquely identify 'transport_t's.
+
+ "In even more technical detail I shall describe what happens when
+ the SIGHUP bell tolls:"
+
+ We immediately destroy all unconfigured proxies (We shouldn't have
+ unconfigured proxies in the first place, except when SIGHUP rings
+ immediately after tor is launched.).
+
+ We mark all managed proxies and transports to signify that they
+ must be removed if they don't contribute by the new torrc
+ (marked_for_removal).
+ We also mark all managed proxies to signify that they might need
+ to be restarted so that they end up supporting all the transports
+ the new torrc wants them to support (got_hup).
+ We also clear their 'transports_to_launch' list so that we can put
+ there the transports we need to launch according to the new torrc.
+
+ We then start parsing torrc again.
+
+ Everytime we encounter a transport line using a known pre-SIGHUP
+ managed proxy, we cleanse that proxy from the removal mark.
+
+ We also mark it as unconfigured so that on the next scheduled
+ events tick, we investigate whether we need to restart the proxy
+ so that it also spawns the new transports.
+ If the post-SIGHUP 'transports_to_launch' list is identical to the
+ pre-SIGHUP one, it means that no changes were introduced to this
+ proxy during the SIGHUP and no restart has to take place.
+
+ During the post-SIGHUP torrc parsing, we unmark all transports
+ spawned by managed proxies that we find in our torrc.
+ We do that so that if we don't need to restart a managed proxy, we
+ can continue using its old transports normally.
+ If we end up restarting the proxy, we destroy and unregister all
+ old transports from the circuitbuild.c subsystem.
+*/
+
+/** Return true if there are still unconfigured managed proxies. */
+int
+pt_proxies_configuration_pending(void)
+{
+ return !! unconfigured_proxies_n;
+}
+
+/** Return true if <b>mp</b> has the same argv as <b>proxy_argv</b> */
+static int
+managed_proxy_has_argv(const managed_proxy_t *mp, char **proxy_argv)
+{
+ char **tmp1=proxy_argv;
+ char **tmp2=mp->argv;
+
+ tor_assert(tmp1);
+ tor_assert(tmp2);
+
+ while (*tmp1 && *tmp2) {
+ if (strcmp(*tmp1++, *tmp2++))
+ return 0;
+ }
+
+ if (!*tmp1 && !*tmp2)
+ return 1;
+
+ return 0;
+}
+
+/** Return a managed proxy with the same argv as <b>proxy_argv</b>.
+ * If no such managed proxy exists, return NULL. */
+static managed_proxy_t *
+get_managed_proxy_by_argv_and_type(char **proxy_argv, int is_server)
+{
+ if (!managed_proxy_list)
+ return NULL;
+
+ SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
+ if (managed_proxy_has_argv(mp, proxy_argv) &&
+ mp->is_server == is_server)
+ return mp;
+ } SMARTLIST_FOREACH_END(mp);
+
+ return NULL;
+}
+
+/** Add <b>transport</b> to managed proxy <b>mp</b>. */
+static void
+add_transport_to_proxy(const char *transport, managed_proxy_t *mp)
+{
+ tor_assert(mp->transports_to_launch);
+ if (!smartlist_string_isin(mp->transports_to_launch, transport))
+ smartlist_add(mp->transports_to_launch, tor_strdup(transport));
+}
+
+/** Called when a SIGHUP occurs. Returns true if managed proxy
+ * <b>mp</b> needs to be restarted after the SIGHUP, based on the new
+ * torrc. */
+static int
+proxy_needs_restart(const managed_proxy_t *mp)
+{
+ /* mp->transport_to_launch is populated with the names of the
+ transports that must be launched *after* the SIGHUP.
+ mp->transports is populated with the names of the transports that
+ were launched *before* the SIGHUP.
+
+ If the two lists contain the same strings, we don't need to
+ restart the proxy, since it already does what we want. */
+
+ tor_assert(smartlist_len(mp->transports_to_launch) > 0);
+ tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
+
+ if (smartlist_len(mp->transports_to_launch) != smartlist_len(mp->transports))
+ goto needs_restart;
+
+ SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, char *, t_t_l) {
+ if (!smartlist_string_isin(mp->transports, t_t_l))
+ goto needs_restart;
+
+ } SMARTLIST_FOREACH_END(t_t_l);
+
+ return 0;
+
+ needs_restart:
+ return 1;
+}
+
+/** Managed proxy <b>mp</b> must be restarted. Do all the necessary
+ * preparations and then flag its state so that it will be relaunched
+ * in the next tick. */
+static void
+proxy_prepare_for_restart(managed_proxy_t *mp)
+{
+ transport_t *t_tmp = NULL;
+
+ tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
+ tor_assert(mp->pid);
+
+ /* kill the old obfsproxy process */
+ tor_terminate_process(mp->pid);
+ mp->pid = 0;
+ fclose(mp->_stdout);
+
+ /* destroy all its old transports. we no longer use them. */
+ SMARTLIST_FOREACH_BEGIN(mp->transports, const char *, t_name) {
+ t_tmp = transport_get_by_name(t_name);
+ if (t_tmp)
+ t_tmp->marked_for_removal = 1;
+ } SMARTLIST_FOREACH_END(t_name);
+ sweep_transport_list();
+
+ /* free the transport names in mp->transports */
+ SMARTLIST_FOREACH(mp->transports, char *, t_name, tor_free(t_name));
+ smartlist_clear(mp->transports);
+
+ /* flag it as an infant proxy so that it gets launched on next tick */
+ mp->conf_state = PT_PROTO_INFANT;
+}
+
+/** Launch managed proxy <b>mp</b>. */
+static int
+launch_managed_proxy(managed_proxy_t *mp)
+{
+ (void) mp;
+ (void) set_managed_proxy_environment;
+ return -1;
+#if 0
+ /* XXXX023 we must reenable this code for managed proxies to work.
+ * "All it needs" is revision to work with the new tor_spawn_background
+ * API. */
+ char **envp=NULL;
+ int pid;
+ process_handle_t proc;
+ FILE *stdout_read = NULL;
+ int stdout_pipe=-1, stderr_pipe=-1;
+
+ /* prepare the environment variables for the managed proxy */
+ set_managed_proxy_environment(&envp, mp);
+
+ pid = tor_spawn_background(mp->argv[0], (const char **)mp->argv,
+ (const char **)envp, &proc);
+ if (pid < 0) {
+ log_warn(LD_GENERAL, "Managed proxy at '%s' failed at launch.",
+ mp->argv[0]);
+ return -1;
+ }
+
+ /* free the memory allocated by set_managed_proxy_environment(). */
+ free_execve_args(envp);
+
+ /* Set stdout/stderr pipes to be non-blocking */
+#ifdef _WIN32
+ {
+ u_long nonblocking = 1;
+ ioctlsocket(stdout_pipe, FIONBIO, &nonblocking);
+ }
+#else
+ fcntl(stdout_pipe, F_SETFL, O_NONBLOCK);
+#endif
+
+ /* Open the buffered IO streams */
+ stdout_read = fdopen(stdout_pipe, "r");
+
+ log_info(LD_CONFIG, "Managed proxy has spawned at PID %d.", pid);
+
+ mp->conf_state = PT_PROTO_LAUNCHED;
+ mp->_stdout = stdout_read;
+ mp->pid = pid;
+#endif
+ return 0;
+}
+
+/** Check if any of the managed proxies we are currently trying to
+ * configure have anything new to say. This is called from
+ * run_scheduled_events(). */
+void
+pt_configure_remaining_proxies(void)
+{
+ log_debug(LD_CONFIG, "Configuring remaining managed proxies (%d)!",
+ unconfigured_proxies_n);
+ SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
+ tor_assert(mp->conf_state != PT_PROTO_BROKEN);
+
+ if (mp->got_hup) {
+ mp->got_hup = 0;
+
+ /* This proxy is marked by a SIGHUP. Check whether we need to
+ restart it. */
+ if (proxy_needs_restart(mp)) {
+ log_info(LD_GENERAL, "Preparing managed proxy for restart.");
+ proxy_prepare_for_restart(mp);
+ continue;
+ } else { /* it doesn't need to be restarted. */
+ log_info(LD_GENERAL, "Nothing changed for managed proxy after HUP: "
+ "not restarting.");
+ unconfigured_proxies_n--;
+ tor_assert(unconfigured_proxies_n >= 0);
+ }
+
+ continue;
+ }
+
+ /* If the proxy is not fully configured, try to configure it
+ futher. */
+ if (!proxy_configuration_finished(mp))
+ configure_proxy(mp);
+
+ } SMARTLIST_FOREACH_END(mp);
+}
+
+/** Attempt to continue configuring managed proxy <b>mp</b>. */
+static void
+configure_proxy(managed_proxy_t *mp)
+{
+ enum stream_status r;
+ char stdout_buf[200];
+
+ /* if we haven't launched the proxy yet, do it now */
+ if (mp->conf_state == PT_PROTO_INFANT) {
+ launch_managed_proxy(mp);
+ return;
+ }
+
+ tor_assert(mp->conf_state != PT_PROTO_INFANT);
+
+ while (1) {
+ r = get_string_from_pipe(mp->_stdout, stdout_buf,
+ sizeof(stdout_buf) - 1);
+
+ if (r == IO_STREAM_OKAY) { /* got a line; handle it! */
+ handle_proxy_line((const char *)stdout_buf, mp);
+ } else if (r == IO_STREAM_EAGAIN) { /* check back later */
+ return;
+ } else if (r == IO_STREAM_CLOSED || r == IO_STREAM_TERM) { /* snap! */
+ log_notice(LD_GENERAL, "Managed proxy stream closed. "
+ "Most probably application stopped running");
+ mp->conf_state = PT_PROTO_BROKEN;
+ } else { /* unknown stream status */
+ log_notice(LD_GENERAL, "Unknown stream status while configuring proxy.");
+ }
+
+ /* if the proxy finished configuring, exit the loop. */
+ if (proxy_configuration_finished(mp)) {
+ handle_finished_proxy(mp);
+ return;
+ }
+ }
+}
+
+/** Register server managed proxy <b>mp</b> transports to state */
+static void
+register_server_proxy(managed_proxy_t *mp)
+{
+ /* After we register this proxy's transports, we switch its
+ mp->transports to a list containing strings of its transport
+ names. (See transports.h) */
+ smartlist_t *sm_tmp = smartlist_create();
+
+ tor_assert(mp->conf_state != PT_PROTO_COMPLETED);
+ SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) {
+ save_transport_to_state(t->name, &t->addr, t->port);
+ smartlist_add(sm_tmp, tor_strdup(t->name));
+ } SMARTLIST_FOREACH_END(t);
+
+ /* Since server proxies don't register their transports in the
+ circuitbuild.c subsystem, it's our duty to free them when we
+ switch mp->transports to strings. */
+ SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
+ smartlist_free(mp->transports);
+
+ mp->transports = sm_tmp;
+}
+
+/** Register all the transports supported by client managed proxy
+ * <b>mp</b> to the bridge subsystem. */
+static void
+register_client_proxy(managed_proxy_t *mp)
+{
+ int r;
+ /* After we register this proxy's transports, we switch its
+ mp->transports to a list containing strings of its transport
+ names. (See transports.h) */
+ smartlist_t *sm_tmp = smartlist_create();
+
+ tor_assert(mp->conf_state != PT_PROTO_COMPLETED);
+ SMARTLIST_FOREACH_BEGIN(mp->transports, transport_t *, t) {
+ r = transport_add(t);
+ switch (r) {
+ case -1:
+ log_notice(LD_GENERAL, "Could not add transport %s. Skipping.", t->name);
+ transport_free(t);
+ break;
+ case 0:
+ log_info(LD_GENERAL, "Succesfully registered transport %s", t->name);
+ smartlist_add(sm_tmp, tor_strdup(t->name));
+ break;
+ case 1:
+ log_info(LD_GENERAL, "Succesfully registered transport %s", t->name);
+ smartlist_add(sm_tmp, tor_strdup(t->name));
+ transport_free(t);
+ break;
+ }
+ } SMARTLIST_FOREACH_END(t);
+
+ smartlist_free(mp->transports);
+ mp->transports = sm_tmp;
+}
+
+/** Register the transports of managed proxy <b>mp</b>. */
+static INLINE void
+register_proxy(managed_proxy_t *mp)
+{
+ if (mp->is_server)
+ register_server_proxy(mp);
+ else
+ register_client_proxy(mp);
+}
+
+/** Free memory allocated by managed proxy <b>mp</b>. */
+static void
+managed_proxy_destroy(managed_proxy_t *mp)
+{
+ if (mp->conf_state != PT_PROTO_COMPLETED)
+ SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
+ else
+ SMARTLIST_FOREACH(mp->transports, char *, t_name, tor_free(t_name));
+
+ /* free the transports smartlist */
+ smartlist_free(mp->transports);
+
+ /* free the transports_to_launch smartlist */
+ SMARTLIST_FOREACH(mp->transports_to_launch, char *, t, tor_free(t));
+ smartlist_free(mp->transports_to_launch);
+
+ /* remove it from the list of managed proxies */
+ smartlist_remove(managed_proxy_list, mp);
+
+ /* close its stdout stream */
+ if (mp->_stdout)
+ fclose(mp->_stdout);
+
+ /* free the argv */
+ free_execve_args(mp->argv);
+
+ if (mp->pid)
+ tor_terminate_process(mp->pid);
+
+ tor_free(mp);
+}
+
+/** Handle a configured or broken managed proxy <b>mp</b>. */
+static void
+handle_finished_proxy(managed_proxy_t *mp)
+{
+ switch (mp->conf_state) {
+ case PT_PROTO_BROKEN: /* if broken: */
+ managed_proxy_destroy(mp); /* annihilate it. */
+ break;
+ case PT_PROTO_CONFIGURED: /* if configured correctly: */
+ register_proxy(mp); /* register its transports */
+ mp->conf_state = PT_PROTO_COMPLETED; /* and mark it as completed. */
+ break;
+ case PT_PROTO_INFANT:
+ case PT_PROTO_LAUNCHED:
+ case PT_PROTO_ACCEPTING_METHODS:
+ case PT_PROTO_COMPLETED:
+ default:
+ log_warn(LD_CONFIG, "Unexpected managed proxy state in "
+ "handle_finished_proxy().");
+ tor_assert(0);
+ }
+
+ unconfigured_proxies_n--;
+ tor_assert(unconfigured_proxies_n >= 0);
+}
+
+/** Return true if the configuration of the managed proxy <b>mp</b> is
+ finished. */
+static INLINE int
+proxy_configuration_finished(const managed_proxy_t *mp)
+{
+ return (mp->conf_state == PT_PROTO_CONFIGURED ||
+ mp->conf_state == PT_PROTO_BROKEN);
+}
+
+/** This function is called when a proxy sends an {S,C}METHODS DONE message. */
+static void
+handle_methods_done(const managed_proxy_t *mp)
+{
+ tor_assert(mp->transports);
+
+ if (smartlist_len(mp->transports) == 0)
+ log_notice(LD_GENERAL, "Proxy was spawned successfully, "
+ "but it didn't laucn any pluggable transport listeners!");
+
+ log_info(LD_CONFIG, "%s managed proxy configuration completed!",
+ mp->is_server ? "Server" : "Client");
+}
+
+/** Handle a configuration protocol <b>line</b> received from a
+ * managed proxy <b>mp</b>. */
+void
+handle_proxy_line(const char *line, managed_proxy_t *mp)
+{
+ log_debug(LD_GENERAL, "Got a line from managed proxy: %s\n", line);
+
+ if (strlen(line) < SMALLEST_MANAGED_LINE_SIZE) {
+ log_warn(LD_GENERAL, "Managed proxy configuration line is too small. "
+ "Discarding");
+ goto err;
+ }
+
+ if (!strcmpstart(line, PROTO_ENV_ERROR)) {
+ if (mp->conf_state != PT_PROTO_LAUNCHED)
+ goto err;
+
+ parse_env_error(line);
+ goto err;
+ } else if (!strcmpstart(line, PROTO_NEG_FAIL)) {
+ if (mp->conf_state != PT_PROTO_LAUNCHED)
+ goto err;
+
+ log_warn(LD_CONFIG, "Managed proxy could not pick a "
+ "configuration protocol version.");
+ goto err;
+ } else if (!strcmpstart(line, PROTO_NEG_SUCCESS)) {
+ if (mp->conf_state != PT_PROTO_LAUNCHED)
+ goto err;
+
+ if (parse_version(line,mp) < 0)
+ goto err;
+
+ tor_assert(mp->conf_protocol != 0);
+ mp->conf_state = PT_PROTO_ACCEPTING_METHODS;
+ return;
+ } else if (!strcmpstart(line, PROTO_CMETHODS_DONE)) {
+ if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+ goto err;
+
+ handle_methods_done(mp);
+
+ mp->conf_state = PT_PROTO_CONFIGURED;
+ return;
+ } else if (!strcmpstart(line, PROTO_SMETHODS_DONE)) {
+ if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+ goto err;
+
+ handle_methods_done(mp);
+
+ mp->conf_state = PT_PROTO_CONFIGURED;
+ return;
+ } else if (!strcmpstart(line, PROTO_CMETHOD_ERROR)) {
+ if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+ goto err;
+
+ parse_client_method_error(line);
+ goto err;
+ } else if (!strcmpstart(line, PROTO_SMETHOD_ERROR)) {
+ if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+ goto err;
+
+ parse_server_method_error(line);
+ goto err;
+ } else if (!strcmpstart(line, PROTO_CMETHOD)) {
+ if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+ goto err;
+
+ if (parse_cmethod_line(line, mp) < 0)
+ goto err;
+
+ return;
+ } else if (!strcmpstart(line, PROTO_SMETHOD)) {
+ if (mp->conf_state != PT_PROTO_ACCEPTING_METHODS)
+ goto err;
+
+ if (parse_smethod_line(line, mp) < 0)
+ goto err;
+
+ return;
+ } else if (!strcmpstart(line, SPAWN_ERROR_MESSAGE)) {
+ log_warn(LD_GENERAL, "Could not launch managed proxy executable!");
+ goto err;
+ }
+
+ log_warn(LD_CONFIG, "Unknown line received by managed proxy. (%s)", line);
+
+ err:
+ mp->conf_state = PT_PROTO_BROKEN;
+ return;
+}
+
+/** Parses an ENV-ERROR <b>line</b> and warns the user accordingly. */
+void
+parse_env_error(const char *line)
+{
+ /* (Length of the protocol string) plus (a space) and (the first char of
+ the error message) */
+ if (strlen(line) < (strlen(PROTO_ENV_ERROR) + 2))
+ log_notice(LD_CONFIG, "Managed proxy sent us an %s without an error "
+ "message.", PROTO_ENV_ERROR);
+
+ log_warn(LD_CONFIG, "Managed proxy couldn't understand the "
+ "pluggable transport environment variables. (%s)",
+ line+strlen(PROTO_ENV_ERROR)+1);
+}
+
+/** Handles a VERSION <b>line</b>. Updates the configuration protocol
+ * version in <b>mp</b>. */
+int
+parse_version(const char *line, managed_proxy_t *mp)
+{
+ if (strlen(line) < (strlen(PROTO_NEG_SUCCESS) + 2)) {
+ log_warn(LD_CONFIG, "Managed proxy sent us malformed %s line.",
+ PROTO_NEG_SUCCESS);
+ return -1;
+ }
+
+ if (strcmp("1", line+strlen(PROTO_NEG_SUCCESS)+1)) { /* hardcoded temp */
+ log_warn(LD_CONFIG, "Managed proxy tried to negotiate on version '%s'. "
+ "We only support version '1'", line+strlen(PROTO_NEG_SUCCESS)+1);
+ return -1;
+ }
+
+ mp->conf_protocol = PROTO_VERSION_ONE; /* temp. till more versions appear */
+ return 0;
+}
+
+/** Parses {C,S}METHOD-ERROR <b>line</b> and warns the user
+ * accordingly. If <b>is_server</b> it is an SMETHOD-ERROR,
+ * otherwise it is a CMETHOD-ERROR. */
+static void
+parse_method_error(const char *line, int is_server)
+{
+ const char* error = is_server ?
+ PROTO_SMETHOD_ERROR : PROTO_CMETHOD_ERROR;
+
+ /* (Length of the protocol string) plus (a space) and (the first char of
+ the error message) */
+ if (strlen(line) < (strlen(error) + 2))
+ log_warn(LD_CONFIG, "Managed proxy sent us an %s without an error "
+ "message.", error);
+
+ log_warn(LD_CONFIG, "%s managed proxy encountered a method error. (%s)",
+ is_server ? "Server" : "Client",
+ line+strlen(error)+1);
+}
+
+/** Parses an SMETHOD <b>line</b> and if well-formed it registers the
+ * new transport in <b>mp</b>. */
+int
+parse_smethod_line(const char *line, managed_proxy_t *mp)
+{
+ int r;
+ smartlist_t *items = NULL;
+
+ char *method_name=NULL;
+
+ char *addrport=NULL;
+ tor_addr_t addr;
+ uint16_t port = 0;
+
+ transport_t *transport=NULL;
+
+ items = smartlist_create();
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+ if (smartlist_len(items) < 3) {
+ log_warn(LD_CONFIG, "Server managed proxy sent us a SMETHOD line "
+ "with too few arguments.");
+ goto err;
+ }
+
+ tor_assert(!strcmp(smartlist_get(items,0),PROTO_SMETHOD));
+
+ method_name = smartlist_get(items,1);
+ if (!string_is_C_identifier(method_name)) {
+ log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
+ method_name);
+ goto err;
+ }
+
+ addrport = smartlist_get(items, 2);
+ if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
+ log_warn(LD_CONFIG, "Error parsing transport "
+ "address '%s'", addrport);
+ goto err;
+ }
+
+ if (!port) {
+ log_warn(LD_CONFIG,
+ "Transport address '%s' has no port.", addrport);
+ goto err;
+ }
+
+ transport = transport_create(&addr, port, method_name, PROXY_NONE);
+ if (!transport)
+ goto err;
+
+ smartlist_add(mp->transports, transport);
+
+ /* For now, notify the user so that he knows where the server
+ transport is listening. */
+ log_info(LD_CONFIG, "Server transport %s at %s:%d.",
+ method_name, fmt_addr(&addr), (int)port);
+
+ r=0;
+ goto done;
+
+ err:
+ r = -1;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ return r;
+}
+
+/** Parses a CMETHOD <b>line</b>, and if well-formed it registers
+ * the new transport in <b>mp</b>. */
+int
+parse_cmethod_line(const char *line, managed_proxy_t *mp)
+{
+ int r;
+ smartlist_t *items = NULL;
+
+ char *method_name=NULL;
+
+ char *socks_ver_str=NULL;
+ int socks_ver=PROXY_NONE;
+
+ char *addrport=NULL;
+ tor_addr_t addr;
+ uint16_t port = 0;
+
+ transport_t *transport=NULL;
+
+ items = smartlist_create();
+ smartlist_split_string(items, line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+ if (smartlist_len(items) < 4) {
+ log_warn(LD_CONFIG, "Client managed proxy sent us a CMETHOD line "
+ "with too few arguments.");
+ goto err;
+ }
+
+ tor_assert(!strcmp(smartlist_get(items,0),PROTO_CMETHOD));
+
+ method_name = smartlist_get(items,1);
+ if (!string_is_C_identifier(method_name)) {
+ log_warn(LD_CONFIG, "Transport name is not a C identifier (%s).",
+ method_name);
+ goto err;
+ }
+
+ socks_ver_str = smartlist_get(items,2);
+
+ if (!strcmp(socks_ver_str,"socks4")) {
+ socks_ver = PROXY_SOCKS4;
+ } else if (!strcmp(socks_ver_str,"socks5")) {
+ socks_ver = PROXY_SOCKS5;
+ } else {
+ log_warn(LD_CONFIG, "Client managed proxy sent us a proxy protocol "
+ "we don't recognize. (%s)", socks_ver_str);
+ goto err;
+ }
+
+ addrport = smartlist_get(items, 3);
+ if (tor_addr_port_lookup(addrport, &addr, &port)<0) {
+ log_warn(LD_CONFIG, "Error parsing transport "
+ "address '%s'", addrport);
+ goto err;
+ }
+
+ if (!port) {
+ log_warn(LD_CONFIG,
+ "Transport address '%s' has no port.", addrport);
+ goto err;
+ }
+
+ transport = transport_create(&addr, port, method_name, socks_ver);
+ if (!transport)
+ goto err;
+
+ smartlist_add(mp->transports, transport);
+
+ log_info(LD_CONFIG, "Transport %s at %s:%d with SOCKS %d. "
+ "Attached to managed proxy.",
+ method_name, fmt_addr(&addr), (int)port, socks_ver);
+
+ r=0;
+ goto done;
+
+ err:
+ r = -1;
+
+ done:
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+ return r;
+}
+
+/** Return a string containing the address:port that <b>transport</b>
+ * should use. It's the responsibility of the caller to free() the
+ * received string. */
+static char *
+get_bindaddr_for_proxy(const managed_proxy_t *mp)
+{
+ char *bindaddr = NULL;
+ smartlist_t *string_tmp = smartlist_create();
+
+ tor_assert(mp->is_server);
+
+ SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, char *, t) {
+ tor_asprintf(&bindaddr, "%s-%s", t, get_bindaddr_for_transport(t));
+ smartlist_add(string_tmp, bindaddr);
+ } SMARTLIST_FOREACH_END(t);
+
+ bindaddr = smartlist_join_strings(string_tmp, ",", 0, NULL);
+
+ SMARTLIST_FOREACH(string_tmp, char *, t, tor_free(t));
+ smartlist_free(string_tmp);
+
+ return bindaddr;
+}
+
+/** Prepare the <b>envp</b> of managed proxy <b>mp</b> */
+static void
+set_managed_proxy_environment(char ***envp, const managed_proxy_t *mp)
+{
+ const or_options_t *options = get_options();
+ char **tmp=NULL;
+ char *state_loc=NULL;
+ char *transports_to_launch=NULL;
+ char *bindaddr=NULL;
+
+ int n_envs = mp->is_server ? ENVIRON_SIZE_SERVER : ENVIRON_SIZE_CLIENT;
+
+ /* allocate enough space for our env. vars and a NULL pointer */
+ *envp = tor_malloc(sizeof(char*)*(n_envs+1));
+ tmp = *envp;
+
+ state_loc = get_datadir_fname("pt_state/"); /* XXX temp */
+ transports_to_launch =
+ smartlist_join_strings(mp->transports_to_launch, ",", 0, NULL);
+
+ tor_asprintf(tmp++, "HOME=%s", getenv("HOME"));
+ tor_asprintf(tmp++, "PATH=%s", getenv("PATH"));
+ tor_asprintf(tmp++, "TOR_PT_STATE_LOCATION=%s", state_loc);
+ tor_asprintf(tmp++, "TOR_PT_MANAGED_TRANSPORT_VER=1"); /* temp */
+ if (mp->is_server) {
+ bindaddr = get_bindaddr_for_proxy(mp);
+
+ /* XXX temp */
+ tor_asprintf(tmp++, "TOR_PT_ORPORT=127.0.0.1:%d", options->ORPort);
+ tor_asprintf(tmp++, "TOR_PT_SERVER_BINDADDR=%s", bindaddr);
+ tor_asprintf(tmp++, "TOR_PT_SERVER_TRANSPORTS=%s", transports_to_launch);
+ /* XXX temp*/
+ tor_asprintf(tmp++, "TOR_PT_EXTENDED_SERVER_PORT=127.0.0.1:4200");
+ } else {
+ tor_asprintf(tmp++, "TOR_PT_CLIENT_TRANSPORTS=%s", transports_to_launch);
+ }
+ *tmp = NULL;
+
+ tor_free(state_loc);
+ tor_free(transports_to_launch);
+ tor_free(bindaddr);
+}
+
+/** Create and return a new managed proxy for <b>transport</b> using
+ * <b>proxy_argv</b>. If <b>is_server</b> is true, it's a server
+ * managed proxy. */
+static managed_proxy_t *
+managed_proxy_create(const smartlist_t *transport_list,
+ char **proxy_argv, int is_server)
+{
+ managed_proxy_t *mp = tor_malloc_zero(sizeof(managed_proxy_t));
+ mp->conf_state = PT_PROTO_INFANT;
+ mp->is_server = is_server;
+ mp->argv = proxy_argv;
+ mp->transports = smartlist_create();
+
+ mp->transports_to_launch = smartlist_create();
+ SMARTLIST_FOREACH(transport_list, const char *, transport,
+ add_transport_to_proxy(transport, mp));
+
+ /* register the managed proxy */
+ if (!managed_proxy_list)
+ managed_proxy_list = smartlist_create();
+ smartlist_add(managed_proxy_list, mp);
+ unconfigured_proxies_n++;
+
+ return mp;
+}
+
+/** Register <b>transport</b> using proxy with <b>proxy_argv</b> to
+ * the managed proxy subsystem.
+ * If <b>is_server</b> is true, then the proxy is a server proxy. */
+void
+pt_kickstart_proxy(const smartlist_t *transport_list,
+ char **proxy_argv, int is_server)
+{
+ managed_proxy_t *mp=NULL;
+ transport_t *old_transport = NULL;
+
+ mp = get_managed_proxy_by_argv_and_type(proxy_argv, is_server);
+
+ if (!mp) { /* we haven't seen this proxy before */
+ managed_proxy_create(transport_list, proxy_argv, is_server);
+
+ } else { /* known proxy. add its transport to its transport list */
+ if (mp->got_hup) {
+ /* If the managed proxy we found is marked by a SIGHUP, it means
+ that it's not useless and should be kept. If it's marked for
+ removal, unmark it and increase the unconfigured proxies so
+ that we try to restart it if we need to. Afterwards, check if
+ a transport_t for 'transport' used to exist before the SIGHUP
+ and make sure it doesn't get deleted because we might reuse
+ it. */
+ if (mp->marked_for_removal) {
+ mp->marked_for_removal = 0;
+ unconfigured_proxies_n++;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport) {
+ old_transport = transport_get_by_name(transport);
+ if (old_transport)
+ old_transport->marked_for_removal = 0;
+ } SMARTLIST_FOREACH_END(transport);
+ }
+
+ SMARTLIST_FOREACH(transport_list, const char *, transport,
+ add_transport_to_proxy(transport, mp));
+ free_execve_args(proxy_argv);
+ }
+}
+
+/** Frees the array of pointers in <b>arg</b> used as arguments to
+ execve(2). */
+static INLINE void
+free_execve_args(char **arg)
+{
+ char **tmp = arg;
+ while (*tmp) /* use the fact that the last element of the array is a
+ NULL pointer to know when to stop freeing */
+ _tor_free(*tmp++);
+
+ tor_free(arg);
+}
+
+/** Tor will read its config.
+ * Prepare the managed proxy list so that proxies not used in the new
+ * config will shutdown, and proxies that need to spawn different
+ * transports will do so. */
+void
+pt_prepare_proxy_list_for_config_read(void)
+{
+ if (!managed_proxy_list)
+ return;
+
+ SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
+ /* Destroy unconfigured proxies. */
+ if (mp->conf_state != PT_PROTO_COMPLETED) {
+ managed_proxy_destroy(mp);
+ unconfigured_proxies_n--;
+ continue;
+ }
+
+ tor_assert(mp->conf_state == PT_PROTO_COMPLETED);
+
+ mp->marked_for_removal = 1;
+ mp->got_hup = 1;
+ SMARTLIST_FOREACH(mp->transports_to_launch, char *, t, tor_free(t));
+ smartlist_clear(mp->transports_to_launch);
+ } SMARTLIST_FOREACH_END(mp);
+
+ tor_assert(unconfigured_proxies_n == 0);
+}
+
+/** The tor config was read.
+ * Destroy all managed proxies that were marked by a previous call to
+ * prepare_proxy_list_for_config_read() and are not used by the new
+ * config. */
+void
+sweep_proxy_list(void)
+{
+ if (!managed_proxy_list)
+ return;
+
+ SMARTLIST_FOREACH_BEGIN(managed_proxy_list, managed_proxy_t *, mp) {
+ if (mp->marked_for_removal) {
+ SMARTLIST_DEL_CURRENT(managed_proxy_list, mp);
+ managed_proxy_destroy(mp);
+ }
+ } SMARTLIST_FOREACH_END(mp);
+}
+
+/** Release all storage held by the pluggable transports subsystem. */
+void
+pt_free_all(void)
+{
+ if (managed_proxy_list) {
+ /* If the proxy is in PT_PROTO_COMPLETED, it has registered its
+ transports and it's the duty of the circuitbuild.c subsystem to
+ free them. Otherwise, it hasn't registered its transports yet
+ and we should free them here. */
+ SMARTLIST_FOREACH(managed_proxy_list, managed_proxy_t *, mp,
+ managed_proxy_destroy(mp));
+
+ smartlist_free(managed_proxy_list);
+ managed_proxy_list=NULL;
+ }
+}
+
diff --git a/src/or/transports.h b/src/or/transports.h
new file mode 100644
index 000000000..4a9338759
--- /dev/null
+++ b/src/or/transports.h
@@ -0,0 +1,105 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file transports.h
+ * \brief Headers for transports.c
+ **/
+
+#ifndef TOR_TRANSPORTS_H
+#define TOR_TRANSPORTS_H
+
+void pt_kickstart_proxy(const smartlist_t *transport_list, char **proxy_argv,
+ int is_server);
+
+#define pt_kickstart_client_proxy(tl, pa) \
+ pt_kickstart_proxy(tl, pa, 0)
+#define pt_kickstart_server_proxy(tl, pa) \
+ pt_kickstart_proxy(tl, pa, 1)
+
+void pt_configure_remaining_proxies(void);
+
+int pt_proxies_configuration_pending(void);
+
+void pt_free_all(void);
+
+void pt_prepare_proxy_list_for_config_read(void);
+void sweep_proxy_list(void);
+
+#ifdef PT_PRIVATE
+/** State of the managed proxy configuration protocol. */
+enum pt_proto_state {
+ PT_PROTO_INFANT, /* was just born */
+ PT_PROTO_LAUNCHED, /* was just launched */
+ PT_PROTO_ACCEPTING_METHODS, /* accepting methods */
+ PT_PROTO_CONFIGURED, /* configured successfully */
+ PT_PROTO_COMPLETED, /* configure and registered its transports */
+ PT_PROTO_BROKEN
+};
+
+/** Structure containing information of a managed proxy. */
+typedef struct {
+ enum pt_proto_state conf_state; /* the current configuration state */
+ char **argv; /* the cli arguments of this proxy */
+ int conf_protocol; /* the configuration protocol version used */
+
+ int is_server; /* is it a server proxy? */
+
+ FILE *_stdout; /* a stream to its stdout
+ (closed in managed_proxy_destroy()) */
+
+ int pid; /* The Process ID this managed proxy is using. */
+
+ /** Boolean: We are re-parsing our config, and we are going to
+ * remove this managed proxy if we don't find it any transport
+ * plugins that use it. */
+ unsigned int marked_for_removal : 1;
+
+ /** Boolean: We got a SIGHUP while this proxy was running. We use
+ * this flag to signify that this proxy might need to be restarted
+ * so that it can listen for other transports according to the new
+ * torrc. */
+ unsigned int got_hup : 1;
+
+ /* transports to-be-launched by this proxy */
+ smartlist_t *transports_to_launch;
+
+ /* The 'transports' list contains all the transports this proxy has
+ launched.
+
+ Before a managed_proxy_t reaches the PT_PROTO_COMPLETED phase,
+ this smartlist contains a 'transport_t' for every transport it
+ has launched.
+
+ When the managed_proxy_t reaches the PT_PROTO_COMPLETED phase, it
+ registers all its transports to the circuitbuild.c subsystem. At
+ that point the 'transport_t's are owned by the circuitbuild.c
+ subsystem.
+
+ To avoid carrying dangling 'transport_t's in this smartlist,
+ right before the managed_proxy_t reaches the PT_PROTO_COMPLETED
+ phase we replace all 'transport_t's with strings of their
+ transport names.
+
+ So, tl;dr:
+ When (conf_state != PT_PROTO_COMPLETED) this list carries
+ (transport_t *).
+ When (conf_state == PT_PROTO_COMPLETED) this list carries
+ (char *).
+ */
+ smartlist_t *transports;
+} managed_proxy_t;
+
+int parse_cmethod_line(const char *line, managed_proxy_t *mp);
+int parse_smethod_line(const char *line, managed_proxy_t *mp);
+
+int parse_version(const char *line, managed_proxy_t *mp);
+void parse_env_error(const char *line);
+void handle_proxy_line(const char *line, managed_proxy_t *mp);
+
+#endif
+
+#endif
+
diff --git a/src/test/Makefile.am b/src/test/Makefile.am
index 904719d94..301452b4e 100644
--- a/src/test/Makefile.am
+++ b/src/test/Makefile.am
@@ -1,6 +1,6 @@
TESTS = test
-noinst_PROGRAMS = test
+noinst_PROGRAMS = test test-child
AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
@@ -12,12 +12,14 @@ AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
# matters a lot there, and is quite hard to debug if you forget to do it.
test_SOURCES = \
- test_data.c \
test.c \
test_addr.c \
+ test_containers.c \
test_crypto.c \
+ test_data.c \
test_dir.c \
- test_containers.c \
+ test_microdesc.c \
+ test_pt.c \
test_util.c \
tinytest.c
@@ -25,6 +27,12 @@ test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
test_LDADD = ../or/libtor.a ../common/libor.a ../common/libor-crypto.a \
../common/libor-event.a \
- @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+ @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+
+noinst_HEADERS = \
+ tinytest.h \
+ tinytest_macros.h \
+ test.h
+
-noinst_HEADERS = tinytest.h tinytest_macros.h test.h
diff --git a/src/test/test-child.c b/src/test/test-child.c
new file mode 100644
index 000000000..1b9c5e3d5
--- /dev/null
+++ b/src/test/test-child.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include "orconfig.h"
+#ifdef MS_WINDOWS
+#define WINDOWS_LEAN_AND_MEAN
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+/** Trivial test program which prints out its command line arguments so we can
+ * check if tor_spawn_background() works */
+int
+main(int argc, char **argv)
+{
+ int i;
+
+ fprintf(stdout, "OUT\n");
+ fprintf(stderr, "ERR\n");
+ for (i = 1; i < argc; i++)
+ fprintf(stdout, "%s\n", argv[i]);
+ fprintf(stdout, "SLEEPING\n");
+ /* We need to flush stdout so that test_util_spawn_background_partial_read()
+ succeed. Otherwise ReadFile() will get the entire output in one */
+ // XXX: Can we make stdio flush on newline?
+ fflush(stdout);
+#ifdef MS_WINDOWS
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ fprintf(stdout, "DONE\n");
+#ifdef MS_WINDOWS
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+
+ return 0;
+}
+
diff --git a/src/test/test.c b/src/test/test.c
index b5b744eba..aca7f10e6 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -119,30 +119,46 @@ get_fname(const char *name)
return buf;
}
-/** Remove all files stored under the temporary directory, and the directory
- * itself. Called by atexit(). */
+/* Remove a directory and all of its subdirectories */
static void
-remove_directory(void)
+rm_rf(const char *dir)
{
+ struct stat st;
smartlist_t *elements;
- if (getpid() != temp_dir_setup_in_pid) {
- /* Only clean out the tempdir when the main process is exiting. */
- return;
- }
- elements = tor_listdir(temp_dir);
+
+ elements = tor_listdir(dir);
if (elements) {
SMARTLIST_FOREACH(elements, const char *, cp,
{
- size_t len = strlen(cp)+strlen(temp_dir)+16;
- char *tmp = tor_malloc(len);
- tor_snprintf(tmp, len, "%s"PATH_SEPARATOR"%s", temp_dir, cp);
- unlink(tmp);
+ char *tmp = NULL;
+ tor_asprintf(&tmp, "%s"PATH_SEPARATOR"%s", dir, cp);
+ if (0 == stat(tmp,&st) && (st.st_mode & S_IFDIR)) {
+ rm_rf(tmp);
+ } else {
+ if (unlink(tmp)) {
+ fprintf(stderr, "Error removing %s: %s\n", tmp, strerror(errno));
+ }
+ }
tor_free(tmp);
});
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements);
}
- rmdir(temp_dir);
+ if (rmdir(dir))
+ fprintf(stderr, "Error removing directory %s: %s\n", dir, strerror(errno));
+}
+
+/** Remove all files stored under the temporary directory, and the directory
+ * itself. Called by atexit(). */
+static void
+remove_directory(void)
+{
+ if (getpid() != temp_dir_setup_in_pid) {
+ /* Only clean out the tempdir when the main process is exiting. */
+ return;
+ }
+
+ rm_rf(temp_dir);
}
/** Define this if unit tests spend too much time generating public keys*/
@@ -187,6 +203,437 @@ free_pregenerated_keys(void)
}
}
+typedef struct socks_test_data_t {
+ socks_request_t *req;
+ buf_t *buf;
+} socks_test_data_t;
+
+static void *
+socks_test_setup(const struct testcase_t *testcase)
+{
+ socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t));
+ (void)testcase;
+ data->buf = buf_new_with_capacity(256);
+ data->req = socks_request_new();
+ config_register_addressmaps(get_options());
+ return data;
+}
+static int
+socks_test_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+ socks_test_data_t *data = ptr;
+ (void)testcase;
+ buf_free(data->buf);
+ socks_request_free(data->req);
+ tor_free(data);
+ return 1;
+}
+
+const struct testcase_setup_t socks_setup = {
+ socks_test_setup, socks_test_cleanup
+};
+
+#define SOCKS_TEST_INIT() \
+ socks_test_data_t *testdata = ptr; \
+ buf_t *buf = testdata->buf; \
+ socks_request_t *socks = testdata->req;
+#define ADD_DATA(buf, s) \
+ write_to_buf(s, sizeof(s)-1, buf)
+
+static void
+socks_request_clear(socks_request_t *socks)
+{
+ tor_free(socks->username);
+ tor_free(socks->password);
+ memset(socks, 0, sizeof(socks_request_t));
+}
+
+/** Perform unsupported SOCKS 4 commands */
+static void
+test_socks_4_unsupported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */
+ ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == -1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+
+ done:
+ ;
+}
+
+/** Perform supported SOCKS 4 commands */
+static void
+test_socks_4_supported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ test_eq(0, buf_datalen(buf));
+
+ /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */
+ ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+ test_eq(SOCKS_COMMAND_CONNECT, socks->command);
+ test_streq("2.2.2.3", socks->address);
+ test_eq(4370, socks->port);
+ test_assert(socks->got_auth == 0);
+ test_assert(! socks->username);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/
+ ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+ test_eq(SOCKS_COMMAND_CONNECT, socks->command);
+ test_streq("2.2.2.4", socks->address);
+ test_eq(4370, socks->port);
+ test_assert(socks->got_auth == 1);
+ test_assert(socks->username);
+ test_eq(2, socks->usernamelen);
+ test_memeq("me", socks->username, 2);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 4a Send RESOLVE [F0] request for torproject.org */
+ ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+ test_streq("torproject.org", socks->address);
+
+ test_eq(0, buf_datalen(buf));
+
+ done:
+ ;
+}
+
+/** Perform unsupported SOCKS 5 commands */
+static void
+test_socks_5_unsupported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Send unsupported BIND [02] command */
+ ADD_DATA(buf, "\x05\x02\x00\x01");
+
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 0);
+ test_eq(0, buf_datalen(buf));
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), -1);
+ /* XXX: shouldn't tor reply 'command not supported' [07]? */
+
+ buf_clear(buf);
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */
+ ADD_DATA(buf, "\x05\x03\x00\x01\x02");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 0);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), -1);
+ /* XXX: shouldn't tor reply 'command not supported' [07]? */
+
+ done:
+ ;
+}
+
+/** Perform supported SOCKS 5 commands */
+static void
+test_socks_5_supported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 0);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 1);
+ test_streq("2.2.2.2", socks->address);
+ test_eq(4369, socks->port);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 1);
+
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ test_streq("torproject.org", socks->address);
+ test_eq(4369, socks->port);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ test_streq("torproject.org", socks->address);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ test_streq("2.2.2.5", socks->address);
+
+ test_eq(0, buf_datalen(buf));
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication */
+static void
+test_socks_5_no_authenticate(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /*SOCKS 5 No Authentication */
+ ADD_DATA(buf,"\x05\x01\x00");
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(SOCKS_NO_AUTH, socks->reply[1]);
+
+ test_eq(0, buf_datalen(buf));
+
+ /*SOCKS 5 Send username/password anyway - pretend to be broken */
+ ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01");
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ test_eq(2, socks->usernamelen);
+ test_eq(2, socks->passwordlen);
+
+ test_memeq("\x01\x01", socks->username, 2);
+ test_memeq("\x01\x01", socks->password, 2);
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication */
+static void
+test_socks_5_authenticate(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Negotiate username/password authentication */
+ ADD_DATA(buf, "\x05\x01\x02");
+
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(SOCKS_USER_PASS, socks->reply[1]);
+ test_eq(5, socks->socks_version);
+
+ test_eq(0, buf_datalen(buf));
+
+ /* SOCKS 5 Send username/password */
+ ADD_DATA(buf, "\x01\x02me\x08mypasswd");
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ test_eq(2, socks->usernamelen);
+ test_eq(8, socks->passwordlen);
+
+ test_memeq("me", socks->username, 2);
+ test_memeq("mypasswd", socks->password, 8);
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication and send data all in one go */
+static void
+test_socks_5_authenticate_with_data(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Negotiate username/password authentication */
+ ADD_DATA(buf, "\x05\x01\x02");
+
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(SOCKS_USER_PASS, socks->reply[1]);
+ test_eq(5, socks->socks_version);
+
+ test_eq(0, buf_datalen(buf));
+
+ /* SOCKS 5 Send username/password */
+ /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
+ ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
+ test_assert(fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ test_streq("2.2.2.2", socks->address);
+ test_eq(4369, socks->port);
+
+ test_eq(2, socks->usernamelen);
+ test_eq(3, socks->passwordlen);
+ test_memeq("me", socks->username, 2);
+ test_memeq("you", socks->password, 3);
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication before method negotiated */
+static void
+test_socks_5_auth_before_negotiation(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Send username/password */
+ ADD_DATA(buf, "\x01\x02me\x02me");
+ test_assert(fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks) == -1);
+ test_eq(0, socks->socks_version);
+ test_eq(0, socks->replylen);
+ test_eq(0, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ done:
+ ;
+}
+
+static void
+test_buffer_copy(void *arg)
+{
+ generic_buffer_t *buf=NULL, *buf2=NULL;
+ const char *s;
+ size_t len;
+ char b[256];
+ int i;
+ (void)arg;
+
+ buf = generic_buffer_new();
+ tt_assert(buf);
+
+ /* Copy an empty buffer. */
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_assert(buf2);
+ tt_int_op(0, ==, generic_buffer_len(buf2));
+
+ /* Now try with a short buffer. */
+ s = "And now comes an act of enormous enormance!";
+ len = strlen(s);
+ generic_buffer_add(buf, s, len);
+ tt_int_op(len, ==, generic_buffer_len(buf));
+ /* Add junk to buf2 so we can test replacing.*/
+ generic_buffer_add(buf2, "BLARG", 5);
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(len, ==, generic_buffer_len(buf2));
+ generic_buffer_get(buf2, b, len);
+ test_mem_op(b, ==, s, len);
+ /* Now free buf2 and retry so we can test allocating */
+ generic_buffer_free(buf2);
+ buf2 = NULL;
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(len, ==, generic_buffer_len(buf2));
+ generic_buffer_get(buf2, b, len);
+ test_mem_op(b, ==, s, len);
+ /* Clear buf for next test */
+ generic_buffer_get(buf, b, len);
+ tt_int_op(generic_buffer_len(buf),==,0);
+
+ /* Okay, now let's try a bigger buffer. */
+ s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
+ "esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
+ "fugiat quo voluptas nulla pariatur?";
+ len = strlen(s);
+ for (i = 0; i < 256; ++i) {
+ b[0]=i;
+ generic_buffer_add(buf, b, 1);
+ generic_buffer_add(buf, s, len);
+ }
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf));
+ for (i = 0; i < 256; ++i) {
+ generic_buffer_get(buf2, b, len+1);
+ tt_int_op((unsigned char)b[0],==,i);
+ test_mem_op(b+1, ==, s, len);
+ }
+
+ done:
+ if (buf)
+ generic_buffer_free(buf);
+ if (buf2)
+ generic_buffer_free(buf2);
+}
+
/** Run unit tests for buffers.c */
static void
test_buffers(void)
@@ -566,6 +1013,7 @@ test_policy_summary_helper(const char *policy_str,
smartlist_t *policy = smartlist_create();
char *summary = NULL;
int r;
+ short_policy_t *short_policy = NULL;
line.key = (char*)"foo";
line.value = (char *)policy_str;
@@ -578,10 +1026,14 @@ test_policy_summary_helper(const char *policy_str,
test_assert(summary != NULL);
test_streq(summary, expected_summary);
+ short_policy = parse_short_policy(summary);
+ tt_assert(short_policy);
+
done:
tor_free(summary);
if (policy)
addr_policy_list_free(policy);
+ short_policy_free(short_policy);
}
/** Run unit tests for generating summary lines of exit policies */
@@ -611,12 +1063,15 @@ test_policies(void)
smartlist_add(policy, p);
+ tor_addr_from_ipv4h(&tar, 0x01020304u);
test_assert(ADDR_POLICY_ACCEPTED ==
- compare_addr_to_addr_policy(0x01020304u, 2, policy));
+ compare_tor_addr_to_addr_policy(&tar, 2, policy));
+ tor_addr_make_unspec(&tar);
test_assert(ADDR_POLICY_PROBABLY_ACCEPTED ==
- compare_addr_to_addr_policy(0, 2, policy));
+ compare_tor_addr_to_addr_policy(&tar, 2, policy));
+ tor_addr_from_ipv4h(&tar, 0xc0a80102);
test_assert(ADDR_POLICY_REJECTED ==
- compare_addr_to_addr_policy(0xc0a80102, 2, policy));
+ compare_tor_addr_to_addr_policy(&tar, 2, policy));
test_assert(0 == policies_parse_exit_policy(NULL, &policy2, 1, NULL, 1));
test_assert(policy2);
@@ -1026,8 +1481,73 @@ static void
test_geoip(void)
{
int i, j;
- time_t now = time(NULL);
+ time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
char *s = NULL;
+ const char *bridge_stats_1 =
+ "bridge-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "bridge-ips zz=24,xy=8\n",
+ *dirreq_stats_1 =
+ "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "dirreq-v3-ips ab=8\n"
+ "dirreq-v2-ips \n"
+ "dirreq-v3-reqs ab=8\n"
+ "dirreq-v2-reqs \n"
+ "dirreq-v3-resp ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "not-modified=0,busy=0\n"
+ "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+ "busy=0\n"
+ "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+ *dirreq_stats_2 =
+ "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "dirreq-v3-ips \n"
+ "dirreq-v2-ips \n"
+ "dirreq-v3-reqs \n"
+ "dirreq-v2-reqs \n"
+ "dirreq-v3-resp ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "not-modified=0,busy=0\n"
+ "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+ "busy=0\n"
+ "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+ *dirreq_stats_3 =
+ "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "dirreq-v3-ips \n"
+ "dirreq-v2-ips \n"
+ "dirreq-v3-reqs \n"
+ "dirreq-v2-reqs \n"
+ "dirreq-v3-resp ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "not-modified=0,busy=0\n"
+ "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+ "busy=0\n"
+ "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+ *dirreq_stats_4 =
+ "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "dirreq-v3-ips \n"
+ "dirreq-v2-ips \n"
+ "dirreq-v3-reqs \n"
+ "dirreq-v2-reqs \n"
+ "dirreq-v3-resp ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "not-modified=0,busy=0\n"
+ "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+ "busy=0\n"
+ "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v3-tunneled-dl complete=0,timeout=0,running=4\n"
+ "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+ *entry_stats_1 =
+ "entry-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "entry-ips ab=8\n",
+ *entry_stats_2 =
+ "entry-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "entry-ips \n";
/* Populate the DB a bit. Add these in order, since we can't do the final
* 'sort' step. These aren't very good IP addresses, but they're perfectly
@@ -1053,8 +1573,8 @@ test_geoip(void)
test_streq("??", NAMEFOR(2000));
#undef NAMEFOR
- get_options()->BridgeRelay = 1;
- get_options()->BridgeRecordUsageByCountry = 1;
+ get_options_mutable()->BridgeRelay = 1;
+ get_options_mutable()->BridgeRecordUsageByCountry = 1;
/* Put 9 observations in AB... */
for (i=32; i < 40; ++i)
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, i, now-7200);
@@ -1077,6 +1597,114 @@ test_geoip(void)
test_assert(s);
test_streq("zz=24,xy=8", s);
+ /* Start testing bridge statistics by making sure that we don't output
+ * bridge stats without initializing them. */
+ s = geoip_format_bridge_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats and generate the bridge-stats history string out of
+ * the connecting clients added above. */
+ geoip_bridge_stats_init(now);
+ s = geoip_format_bridge_stats(now + 86400);
+ test_streq(bridge_stats_1, s);
+ tor_free(s);
+
+ /* Stop collecting bridge stats and make sure we don't write a history
+ * string anymore. */
+ geoip_bridge_stats_term();
+ s = geoip_format_bridge_stats(now + 86400);
+ test_assert(!s);
+
+ /* Stop being a bridge and start being a directory mirror that gathers
+ * directory request statistics. */
+ geoip_bridge_stats_term();
+ get_options_mutable()->BridgeRelay = 0;
+ get_options_mutable()->BridgeRecordUsageByCountry = 0;
+ get_options_mutable()->DirReqStatistics = 1;
+
+ /* Start testing dirreq statistics by making sure that we don't collect
+ * dirreq stats without initializing them. */
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 100, now);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats, note one connecting client, and generate the
+ * dirreq-stats history string. */
+ geoip_dirreq_stats_init(now);
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 100, now);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_streq(dirreq_stats_1, s);
+ tor_free(s);
+
+ /* Stop collecting stats, add another connecting client, and ensure we
+ * don't generate a history string. */
+ geoip_dirreq_stats_term();
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 101, now);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_assert(!s);
+
+ /* Re-start stats, add a connecting client, reset stats, and make sure
+ * that we get an all empty history string. */
+ geoip_dirreq_stats_init(now);
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 100, now);
+ geoip_reset_dirreq_stats(now);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_streq(dirreq_stats_2, s);
+ tor_free(s);
+
+ /* Note a successful network status response and make sure that it
+ * appears in the history string. */
+ geoip_note_ns_response(GEOIP_CLIENT_NETWORKSTATUS, GEOIP_SUCCESS);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_streq(dirreq_stats_3, s);
+ tor_free(s);
+
+ /* Start a tunneled directory request. */
+ geoip_start_dirreq((uint64_t) 1, 1024, GEOIP_CLIENT_NETWORKSTATUS,
+ DIRREQ_TUNNELED);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_streq(dirreq_stats_4, s);
+
+ /* Stop collecting directory request statistics and start gathering
+ * entry stats. */
+ geoip_dirreq_stats_term();
+ get_options_mutable()->DirReqStatistics = 0;
+ get_options_mutable()->EntryStatistics = 1;
+
+ /* Start testing entry statistics by making sure that we don't collect
+ * anything without initializing entry stats. */
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 100, now);
+ s = geoip_format_entry_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats, note one connecting client, and generate the
+ * entry-stats history string. */
+ geoip_entry_stats_init(now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 100, now);
+ s = geoip_format_entry_stats(now + 86400);
+ test_streq(entry_stats_1, s);
+ tor_free(s);
+
+ /* Stop collecting stats, add another connecting client, and ensure we
+ * don't generate a history string. */
+ geoip_entry_stats_term();
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 101, now);
+ s = geoip_format_entry_stats(now + 86400);
+ test_assert(!s);
+
+ /* Re-start stats, add a connecting client, reset stats, and make sure
+ * that we get an all empty history string. */
+ geoip_entry_stats_init(now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 100, now);
+ geoip_reset_entry_stats(now);
+ s = geoip_format_entry_stats(now + 86400);
+ test_streq(entry_stats_2, s);
+ tor_free(s);
+
+ /* Stop collecting entry statistics. */
+ geoip_entry_stats_term();
+ get_options_mutable()->EntryStatistics = 0;
+
done:
tor_free(s);
}
@@ -1089,7 +1717,8 @@ test_stats(void)
char *s = NULL;
int i;
- /* We shouldn't collect exit stats without initializing them. */
+ /* Start with testing exit port statistics; we shouldn't collect exit
+ * stats without initializing them. */
rep_hist_note_exit_stream_opened(80);
rep_hist_note_exit_bytes(80, 100, 10000);
s = rep_hist_format_exit_stats(now + 86400);
@@ -1134,7 +1763,7 @@ test_stats(void)
test_assert(!s);
/* Re-start stats, add some bytes, reset stats, and see what history we
- * get when observing no streams or bytes at all. */
+ * get when observing no streams or bytes at all. */
rep_hist_exit_stats_init(now);
rep_hist_note_exit_stream_opened(80);
rep_hist_note_exit_bytes(80, 100, 10000);
@@ -1144,6 +1773,96 @@ test_stats(void)
"exit-kibibytes-written other=0\n"
"exit-kibibytes-read other=0\n"
"exit-streams-opened other=0\n", s);
+ tor_free(s);
+
+ /* Continue with testing connection statistics; we shouldn't collect
+ * conn stats without initializing them. */
+ rep_hist_note_or_conn_bytes(1, 20, 400, now);
+ s = rep_hist_format_conn_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats, note bytes, and generate history string. */
+ rep_hist_conn_stats_init(now);
+ rep_hist_note_or_conn_bytes(1, 30000, 400000, now);
+ rep_hist_note_or_conn_bytes(1, 30000, 400000, now + 5);
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 10);
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
+ s = rep_hist_format_conn_stats(now + 86400);
+ test_streq("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,1,0\n", s);
+ tor_free(s);
+
+ /* Stop collecting stats, add some bytes, and ensure we don't generate
+ * a history string. */
+ rep_hist_conn_stats_term();
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
+ s = rep_hist_format_conn_stats(now + 86400);
+ test_assert(!s);
+
+ /* Re-start stats, add some bytes, reset stats, and see what history we
+ * get when observing no bytes at all. */
+ rep_hist_conn_stats_init(now);
+ rep_hist_note_or_conn_bytes(1, 30000, 400000, now);
+ rep_hist_note_or_conn_bytes(1, 30000, 400000, now + 5);
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 10);
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
+ rep_hist_reset_conn_stats(now);
+ s = rep_hist_format_conn_stats(now + 86400);
+ test_streq("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n", s);
+ tor_free(s);
+
+ /* Continue with testing buffer statistics; we shouldn't collect buffer
+ * stats without initializing them. */
+ rep_hist_add_buffer_stats(2.0, 2.0, 20);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats, add statistics for a single circuit, and generate
+ * the history string. */
+ rep_hist_buffer_stats_init(now);
+ rep_hist_add_buffer_stats(2.0, 2.0, 20);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_streq("cell-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "cell-processed-cells 20,0,0,0,0,0,0,0,0,0\n"
+ "cell-queued-cells 2.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,"
+ "0.00,0.00\n"
+ "cell-time-in-queue 2,0,0,0,0,0,0,0,0,0\n"
+ "cell-circuits-per-decile 1\n", s);
+ tor_free(s);
+
+ /* Add nineteen more circuit statistics to the one that's already in the
+ * history to see that the math works correctly. */
+ for (i = 21; i < 30; i++)
+ rep_hist_add_buffer_stats(2.0, 2.0, i);
+ for (i = 20; i < 30; i++)
+ rep_hist_add_buffer_stats(3.5, 3.5, i);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_streq("cell-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "cell-processed-cells 29,28,27,26,25,24,23,22,21,20\n"
+ "cell-queued-cells 2.75,2.75,2.75,2.75,2.75,2.75,2.75,2.75,"
+ "2.75,2.75\n"
+ "cell-time-in-queue 3,3,3,3,3,3,3,3,3,3\n"
+ "cell-circuits-per-decile 2\n", s);
+ tor_free(s);
+
+ /* Stop collecting stats, add statistics for one circuit, and ensure we
+ * don't generate a history string. */
+ rep_hist_buffer_stats_term();
+ rep_hist_add_buffer_stats(2.0, 2.0, 20);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_assert(!s);
+
+ /* Re-start stats, add statistics for one circuit, reset stats, and make
+ * sure that the history has all zeros. */
+ rep_hist_buffer_stats_init(now);
+ rep_hist_add_buffer_stats(2.0, 2.0, 20);
+ rep_hist_reset_buffer_stats(now);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_streq("cell-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "cell-processed-cells 0,0,0,0,0,0,0,0,0,0\n"
+ "cell-queued-cells 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,"
+ "0.00,0.00\n"
+ "cell-time-in-queue 0,0,0,0,0,0,0,0,0,0\n"
+ "cell-circuits-per-decile 0\n", s);
done:
tor_free(s);
@@ -1186,6 +1905,7 @@ const struct testcase_setup_t legacy_setup = {
static struct testcase_t test_array[] = {
ENT(buffers),
+ { "buffer_copy", test_buffer_copy, 0, NULL, NULL },
ENT(onion_handshake),
ENT(circuit_timeout),
ENT(policies),
@@ -1198,19 +1918,41 @@ static struct testcase_t test_array[] = {
END_OF_TESTCASES
};
+#define SOCKSENT(name) \
+ { #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
+
+static struct testcase_t socks_tests[] = {
+ SOCKSENT(4_unsupported_commands),
+ SOCKSENT(4_supported_commands),
+
+ SOCKSENT(5_unsupported_commands),
+ SOCKSENT(5_supported_commands),
+ SOCKSENT(5_no_authenticate),
+ SOCKSENT(5_auth_before_negotiation),
+ SOCKSENT(5_authenticate),
+ SOCKSENT(5_authenticate_with_data),
+
+ END_OF_TESTCASES
+};
+
extern struct testcase_t addr_tests[];
extern struct testcase_t crypto_tests[];
extern struct testcase_t container_tests[];
extern struct testcase_t util_tests[];
extern struct testcase_t dir_tests[];
+extern struct testcase_t microdesc_tests[];
+extern struct testcase_t pt_tests[];
static struct testgroup_t testgroups[] = {
{ "", test_array },
+ { "socks/", socks_tests },
{ "addr/", addr_tests },
{ "crypto/", crypto_tests },
{ "container/", container_tests },
{ "util/", util_tests },
{ "dir/", dir_tests },
+ { "dir/md/", microdesc_tests },
+ { "pt/", pt_tests },
END_OF_GROUPS
};
@@ -1259,7 +2001,10 @@ main(int c, const char **v)
}
options->command = CMD_RUN_UNITTESTS;
- crypto_global_init(0, NULL, NULL);
+ if (crypto_global_init(0, NULL, NULL)) {
+ printf("Can't initialize crypto subsystem; exiting.\n");
+ return 1;
+ }
rep_hist_init();
network_init();
setup_directory();
diff --git a/src/test/test.h b/src/test/test.h
index f7ae46ce6..a053a7ac4 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -45,7 +45,8 @@
_print = tor_malloc(printlen); \
base16_encode(_print, printlen, _value, \
(len)); }, \
- { tor_free(_print); } \
+ { tor_free(_print); }, \
+ TT_EXIT_TEST_FUNCTION \
);
#define test_memeq(expr1, expr2, len) test_mem_op((expr1), ==, (expr2), len)
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index 1dab0e011..58e3e3dac 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -14,30 +14,30 @@ test_addr_basic(void)
uint16_t u16;
char *cp;
- /* Test parse_addr_port */
+ /* Test addr_port_lookup */
cp = NULL; u32 = 3; u16 = 3;
- test_assert(!parse_addr_port(LOG_WARN, "1.2.3.4", &cp, &u32, &u16));
+ test_assert(!addr_port_lookup(LOG_WARN, "1.2.3.4", &cp, &u32, &u16));
test_streq(cp, "1.2.3.4");
test_eq(u32, 0x01020304u);
test_eq(u16, 0);
tor_free(cp);
- test_assert(!parse_addr_port(LOG_WARN, "4.3.2.1:99", &cp, &u32, &u16));
+ test_assert(!addr_port_lookup(LOG_WARN, "4.3.2.1:99", &cp, &u32, &u16));
test_streq(cp, "4.3.2.1");
test_eq(u32, 0x04030201u);
test_eq(u16, 99);
tor_free(cp);
- test_assert(!parse_addr_port(LOG_WARN, "nonexistent.address:4040",
+ test_assert(!addr_port_lookup(LOG_WARN, "nonexistent.address:4040",
&cp, NULL, &u16));
test_streq(cp, "nonexistent.address");
test_eq(u16, 4040);
tor_free(cp);
- test_assert(!parse_addr_port(LOG_WARN, "localhost:9999", &cp, &u32, &u16));
+ test_assert(!addr_port_lookup(LOG_WARN, "localhost:9999", &cp, &u32, &u16));
test_streq(cp, "localhost");
test_eq(u32, 0x7f000001u);
test_eq(u16, 9999);
tor_free(cp);
u32 = 3;
- test_assert(!parse_addr_port(LOG_WARN, "localhost", NULL, &u32, &u16));
+ test_assert(!addr_port_lookup(LOG_WARN, "localhost", NULL, &u32, &u16));
test_eq(cp, NULL);
test_eq(u32, 0x7f000001u);
test_eq(u16, 0);
@@ -74,7 +74,9 @@ test_addr_basic(void)
cp += 2; \
if (i != 15) *cp++ = ':'; \
} \
- }, { tor_free(_print); } \
+ }, \
+ { tor_free(_print); }, \
+ TT_EXIT_TEST_FUNCTION \
); \
STMT_END
@@ -362,32 +364,32 @@ test_addr_ip6_helpers(void)
test_addr_compare_masked("0::2:2:1", ==, "0::8000:2:1", 80);
/* Test decorated addr_to_string. */
- test_eq(AF_INET6, tor_addr_from_str(&t1, "[123:45:6789::5005:11]"));
+ test_eq(AF_INET6, tor_addr_parse(&t1, "[123:45:6789::5005:11]"));
p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
test_streq(p1, "[123:45:6789::5005:11]");
- test_eq(AF_INET, tor_addr_from_str(&t1, "18.0.0.1"));
+ test_eq(AF_INET, tor_addr_parse(&t1, "18.0.0.1"));
p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
test_streq(p1, "18.0.0.1");
- /* Test tor_addr_parse_reverse_lookup_name */
- i = tor_addr_parse_reverse_lookup_name(&t1, "Foobar.baz", AF_UNSPEC, 0);
+ /* Test tor_addr_parse_PTR_name */
+ i = tor_addr_parse_PTR_name(&t1, "Foobar.baz", AF_UNSPEC, 0);
test_eq(0, i);
- i = tor_addr_parse_reverse_lookup_name(&t1, "Foobar.baz", AF_UNSPEC, 1);
+ i = tor_addr_parse_PTR_name(&t1, "Foobar.baz", AF_UNSPEC, 1);
test_eq(0, i);
- i = tor_addr_parse_reverse_lookup_name(&t1, "1.0.168.192.in-addr.arpa",
+ i = tor_addr_parse_PTR_name(&t1, "1.0.168.192.in-addr.arpa",
AF_UNSPEC, 1);
test_eq(1, i);
test_eq(tor_addr_family(&t1), AF_INET);
p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
test_streq(p1, "192.168.0.1");
- i = tor_addr_parse_reverse_lookup_name(&t1, "192.168.0.99", AF_UNSPEC, 0);
+ i = tor_addr_parse_PTR_name(&t1, "192.168.0.99", AF_UNSPEC, 0);
test_eq(0, i);
- i = tor_addr_parse_reverse_lookup_name(&t1, "192.168.0.99", AF_UNSPEC, 1);
+ i = tor_addr_parse_PTR_name(&t1, "192.168.0.99", AF_UNSPEC, 1);
test_eq(1, i);
p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
test_streq(p1, "192.168.0.99");
memset(&t1, 0, sizeof(t1));
- i = tor_addr_parse_reverse_lookup_name(&t1,
+ i = tor_addr_parse_PTR_name(&t1,
"0.1.2.3.4.5.6.7.8.9.a.b.c.d.e.f."
"f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
"ip6.ARPA",
@@ -396,37 +398,37 @@ test_addr_ip6_helpers(void)
p1 = tor_addr_to_str(buf, &t1, sizeof(buf), 1);
test_streq(p1, "[9dee:effe:ebe1:beef:fedc:ba98:7654:3210]");
/* Failing cases. */
- i = tor_addr_parse_reverse_lookup_name(&t1,
+ i = tor_addr_parse_PTR_name(&t1,
"6.7.8.9.a.b.c.d.e.f."
"f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
"ip6.ARPA",
AF_UNSPEC, 0);
test_eq(i, -1);
- i = tor_addr_parse_reverse_lookup_name(&t1,
+ i = tor_addr_parse_PTR_name(&t1,
"6.7.8.9.a.b.c.d.e.f.a.b.c.d.e.f.0."
"f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
"ip6.ARPA",
AF_UNSPEC, 0);
test_eq(i, -1);
- i = tor_addr_parse_reverse_lookup_name(&t1,
+ i = tor_addr_parse_PTR_name(&t1,
"6.7.8.9.a.b.c.d.e.f.X.0.0.0.0.9."
"f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
"ip6.ARPA",
AF_UNSPEC, 0);
test_eq(i, -1);
- i = tor_addr_parse_reverse_lookup_name(&t1, "32.1.1.in-addr.arpa",
+ i = tor_addr_parse_PTR_name(&t1, "32.1.1.in-addr.arpa",
AF_UNSPEC, 0);
test_eq(i, -1);
- i = tor_addr_parse_reverse_lookup_name(&t1, ".in-addr.arpa",
+ i = tor_addr_parse_PTR_name(&t1, ".in-addr.arpa",
AF_UNSPEC, 0);
test_eq(i, -1);
- i = tor_addr_parse_reverse_lookup_name(&t1, "1.2.3.4.5.in-addr.arpa",
+ i = tor_addr_parse_PTR_name(&t1, "1.2.3.4.5.in-addr.arpa",
AF_UNSPEC, 0);
test_eq(i, -1);
- i = tor_addr_parse_reverse_lookup_name(&t1, "1.2.3.4.5.in-addr.arpa",
+ i = tor_addr_parse_PTR_name(&t1, "1.2.3.4.5.in-addr.arpa",
AF_INET6, 0);
test_eq(i, -1);
- i = tor_addr_parse_reverse_lookup_name(&t1,
+ i = tor_addr_parse_PTR_name(&t1,
"6.7.8.9.a.b.c.d.e.f.a.b.c.d.e.0."
"f.e.e.b.1.e.b.e.e.f.f.e.e.e.d.9."
"ip6.ARPA",
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 121af279c..1b338a29a 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -231,7 +231,7 @@ test_crypto_sha(void)
{
crypto_digest_env_t *d1 = NULL, *d2 = NULL;
int i;
- char key[80];
+ char key[160];
char digest[32];
char data[50];
char d_out1[DIGEST_LEN], d_out2[DIGEST256_LEN];
@@ -276,6 +276,75 @@ test_crypto_sha(void)
test_streq(hex_str(digest, 20),
"AA4AE5E15272D00E95705637CE8A3B55ED402112");
+ /* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */
+
+ /* Case empty (wikipedia) */
+ crypto_hmac_sha256(digest, "", 0, "", 0);
+ test_streq(hex_str(digest, 32),
+ "B613679A0814D9EC772F95D778C35FC5FF1697C493715653C6C712144292C5AD");
+
+ /* Case quick-brown (wikipedia) */
+ crypto_hmac_sha256(digest, "key", 3,
+ "The quick brown fox jumps over the lazy dog", 43);
+ test_streq(hex_str(digest, 32),
+ "F7BC83F430538424B13298E6AA6FB143EF4D59A14946175997479DBC2D1A3CD8");
+
+ /* "Test Case 1" from RFC 4231 */
+ memset(key, 0x0b, 20);
+ crypto_hmac_sha256(digest, key, 20, "Hi There", 8);
+ test_memeq_hex(digest,
+ "b0344c61d8db38535ca8afceaf0bf12b"
+ "881dc200c9833da726e9376c2e32cff7");
+
+ /* "Test Case 2" from RFC 4231 */
+ memset(key, 0x0b, 20);
+ crypto_hmac_sha256(digest, "Jefe", 4, "what do ya want for nothing?", 28);
+ test_memeq_hex(digest,
+ "5bdcc146bf60754e6a042426089575c7"
+ "5a003f089d2739839dec58b964ec3843");
+
+ /* "Test case 3" from RFC 4231 */
+ memset(key, 0xaa, 20);
+ memset(data, 0xdd, 50);
+ crypto_hmac_sha256(digest, key, 20, data, 50);
+ test_memeq_hex(digest,
+ "773ea91e36800e46854db8ebd09181a7"
+ "2959098b3ef8c122d9635514ced565fe");
+
+ /* "Test case 4" from RFC 4231 */
+ base16_decode(key, 25,
+ "0102030405060708090a0b0c0d0e0f10111213141516171819", 50);
+ memset(data, 0xcd, 50);
+ crypto_hmac_sha256(digest, key, 25, data, 50);
+ test_memeq_hex(digest,
+ "82558a389a443c0ea4cc819899f2083a"
+ "85f0faa3e578f8077a2e3ff46729665b");
+
+ /* "Test case 5" from RFC 4231 */
+ memset(key, 0x0c, 20);
+ crypto_hmac_sha256(digest, key, 20, "Test With Truncation", 20);
+ test_memeq_hex(digest,
+ "a3b6167473100ee06e0c796c2955552b");
+
+ /* "Test case 6" from RFC 4231 */
+ memset(key, 0xaa, 131);
+ crypto_hmac_sha256(digest, key, 131,
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ 54);
+ test_memeq_hex(digest,
+ "60e431591ee0b67f0d8a26aacbf5b77f"
+ "8e0bc6213728c5140546040f0ee37f54");
+
+ /* "Test case 7" from RFC 4231 */
+ memset(key, 0xaa, 131);
+ crypto_hmac_sha256(digest, key, 131,
+ "This is a test using a larger than block-size key and a "
+ "larger than block-size data. The key needs to be hashed "
+ "before being used by the HMAC algorithm.", 152);
+ test_memeq_hex(digest,
+ "9b09ffa71b942fcb27635fbcd5b0e944"
+ "bfdc63644f0713938a7f51535c3a35e2");
+
/* Incremental digest code. */
d1 = crypto_new_digest_env();
test_assert(d1);
@@ -630,7 +699,7 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 4095);
- tor_assert(encrypted_size > 0); /* This is obviously true, since 4111 is
+ tt_assert(encrypted_size > 0); /* This is obviously true, since 4111 is
* greater than 0, but its truth is not
* obvious to all analysis tools. */
cipher = crypto_create_init_cipher(key1, 0);
@@ -639,7 +708,7 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 4095);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain, decrypted1, 4095);
/* Encrypt a second time (with a new random initialization vector). */
cipher = crypto_create_init_cipher(key1, 1);
@@ -648,14 +717,14 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 4095);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted2, 4095,
encrypted2, encrypted_size);
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 4095);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain, decrypted2, 4095);
test_memneq(encrypted1, encrypted2, encrypted_size);
/* Decrypt with the wrong key. */
@@ -680,14 +749,14 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 1);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 1,
encrypted1, encrypted_size);
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 1);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain_1, decrypted1, 1);
/* Special length case: 15. */
cipher = crypto_create_init_cipher(key1, 1);
@@ -696,14 +765,14 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 15);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 15,
encrypted1, encrypted_size);
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 15);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain_15, decrypted1, 15);
/* Special length case: 16. */
cipher = crypto_create_init_cipher(key1, 1);
@@ -712,14 +781,14 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 16);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 16,
encrypted1, encrypted_size);
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 16);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain_16, decrypted1, 16);
/* Special length case: 17. */
cipher = crypto_create_init_cipher(key1, 1);
@@ -728,12 +797,12 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 17);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 17,
encrypted1, encrypted_size);
test_eq(decrypted_size, 17);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain_17, decrypted1, 17);
done:
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 8fd94289a..205d7b577 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -7,10 +7,12 @@
#define DIRSERV_PRIVATE
#define DIRVOTE_PRIVATE
#define ROUTER_PRIVATE
+#define HIBERNATE_PRIVATE
#include "or.h"
#include "directory.h"
#include "dirserv.h"
#include "dirvote.h"
+#include "hibernate.h"
#include "networkstatus.h"
#include "router.h"
#include "routerlist.h"
@@ -85,6 +87,8 @@ test_dir_formats(void)
test_assert(pk1 && pk2 && pk3);
+ hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE);
+
get_platform_str(platform, sizeof(platform));
r1 = tor_malloc_zero(sizeof(routerinfo_t));
r1->address = tor_strdup("18.244.0.1");
@@ -298,7 +302,7 @@ test_dir_versions(void)
#define tt_versionstatus_op(vs1, op, vs2) \
tt_assert_test_type(vs1,vs2,#vs1" "#op" "#vs2,version_status_t, \
- (_val1 op _val2),"%d")
+ (_val1 op _val2),"%d",TT_EXIT_TEST_FUNCTION)
#define test_v_i_o(val, ver, lst) \
tt_versionstatus_op(val, ==, tor_version_is_obsolete(ver, lst))
@@ -803,7 +807,7 @@ test_dir_v3_networkstatus(void)
rs->or_port = 443;
rs->dir_port = 8000;
/* all flags but running cleared */
- rs->is_running = 1;
+ rs->is_flagged_running = 1;
smartlist_add(vote->routerstatus_list, vrs);
test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
@@ -818,7 +822,7 @@ test_dir_v3_networkstatus(void)
rs->addr = 0x99009901;
rs->or_port = 443;
rs->dir_port = 0;
- rs->is_exit = rs->is_stable = rs->is_fast = rs->is_running =
+ rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running =
rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
smartlist_add(vote->routerstatus_list, vrs);
test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
@@ -835,7 +839,8 @@ test_dir_v3_networkstatus(void)
rs->or_port = 400;
rs->dir_port = 9999;
rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
- rs->is_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
+ rs->is_flagged_running = rs->is_valid = rs->is_v2_dir =
+ rs->is_possible_guard = 1;
smartlist_add(vote->routerstatus_list, vrs);
test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
@@ -1075,7 +1080,8 @@ test_dir_v3_networkstatus(void)
test_assert(!rs->is_fast);
test_assert(!rs->is_possible_guard);
test_assert(!rs->is_stable);
- test_assert(rs->is_running); /* If it wasn't running it wouldn't be here */
+ /* (If it wasn't running it wouldn't be here) */
+ test_assert(rs->is_flagged_running);
test_assert(!rs->is_v2_dir);
test_assert(!rs->is_valid);
test_assert(!rs->is_named);
@@ -1097,7 +1103,7 @@ test_dir_v3_networkstatus(void)
test_assert(rs->is_fast);
test_assert(rs->is_possible_guard);
test_assert(rs->is_stable);
- test_assert(rs->is_running);
+ test_assert(rs->is_flagged_running);
test_assert(rs->is_v2_dir);
test_assert(rs->is_valid);
test_assert(!rs->is_named);
@@ -1167,10 +1173,10 @@ test_dir_v3_networkstatus(void)
/* Extract a detached signature from con3. */
detached_text1 = get_detached_sigs(con3, con_md3);
- tor_assert(detached_text1);
+ tt_assert(detached_text1);
/* Try to parse it. */
dsig1 = networkstatus_parse_detached_signatures(detached_text1, NULL);
- tor_assert(dsig1);
+ tt_assert(dsig1);
/* Are parsed values as expected? */
test_eq(dsig1->valid_after, con3->valid_after);
@@ -1301,7 +1307,7 @@ test_dir_v3_networkstatus(void)
}
#define DIR_LEGACY(name) \
- { #name, legacy_test_helper, 0, &legacy_setup, test_dir_ ## name }
+ { #name, legacy_test_helper, TT_FORK, &legacy_setup, test_dir_ ## name }
#define DIR(name) \
{ #name, test_dir_##name, 0, NULL, NULL }
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
new file mode 100644
index 000000000..b807265c8
--- /dev/null
+++ b/src/test/test_microdesc.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+
+#include "config.h"
+#include "microdesc.h"
+
+#include "test.h"
+
+#ifdef MS_WINDOWS
+/* For mkdir() */
+#include <direct.h>
+#else
+#include <dirent.h>
+#endif
+
+static const char test_md1[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMjlHH/daN43cSVRaHBwgUfnszzAhg98EvivJ9Qxfv51mvQUxPjQ07es\n"
+ "gV/3n8fyh3Kqr/ehi9jxkdgSRfSnmF7giaHL1SLZ29kA7KtST+pBvmTpDtHa3ykX\n"
+ "Xorc7hJvIyTZoc1HU+5XSynj3gsBE5IGK1ZRzrNS688LnuZMVp1tAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n";
+
+static const char test_md2[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMIixIowh2DyPmDNMDwBX2DHcYcqdcH1zdIQJZkyV6c6rQHnvbcaDoSg\n"
+ "jgFSLJKpnGmh71FVRqep+yVB0zI1JY43kuEnXry2HbZCD9UDo3d3n7t015X5S7ON\n"
+ "bSSYtQGPwOr6Epf96IF6DoQxy4iDnPUAlejuhAG51s1y6/rZQ3zxAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n";
+
+static const char test_md3[] =
+ "@last-listed 2009-06-22\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMH3340d4ENNGrqx7UxT+lB7x6DNUKOdPEOn4teceE11xlMyZ9TPv41c\n"
+ "qj2fRZzfxlc88G/tmiaHshmdtEpklZ740OFqaaJVj4LjPMKFNE+J7Xc1142BE9Ci\n"
+ "KgsbjGYe2RY261aADRWLetJ8T9QDMm+JngL4288hc8pq1uB/3TAbAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "p accept 1-700,800-1000\n"
+ "family nodeX nodeY nodeZ\n";
+
+static void
+test_md_cache(void *data)
+{
+ or_options_t *options = NULL;
+ microdesc_cache_t *mc = NULL ;
+ smartlist_t *added = NULL, *wanted = NULL;
+ microdesc_t *md1, *md2, *md3;
+ char d1[DIGEST256_LEN], d2[DIGEST256_LEN], d3[DIGEST256_LEN];
+ const char *test_md3_noannotation = strchr(test_md3, '\n')+1;
+ time_t time1, time2, time3;
+ char *fn = NULL, *s = NULL;
+ (void)data;
+
+ options = get_options_mutable();
+ tt_assert(options);
+
+ time1 = time(NULL);
+ time2 = time(NULL) - 2*24*60*60;
+ time3 = time(NULL) - 15*24*60*60;
+
+ /* Possibly, turn this into a test setup/cleanup pair */
+ tor_free(options->DataDirectory);
+ options->DataDirectory = tor_strdup(get_fname("md_datadir_test"));
+#ifdef MS_WINDOWS
+ tt_int_op(0, ==, mkdir(options->DataDirectory));
+#else
+ tt_int_op(0, ==, mkdir(options->DataDirectory, 0700));
+#endif
+
+ tt_assert(!strcmpstart(test_md3_noannotation, "onion-key"));
+
+ crypto_digest256(d1, test_md1, strlen(test_md1), DIGEST_SHA256);
+ crypto_digest256(d2, test_md2, strlen(test_md1), DIGEST_SHA256);
+ crypto_digest256(d3, test_md3_noannotation, strlen(test_md3_noannotation),
+ DIGEST_SHA256);
+
+ mc = get_microdesc_cache();
+
+ added = microdescs_add_to_cache(mc, test_md1, NULL, SAVED_NOWHERE, 0,
+ time1, NULL);
+ tt_int_op(1, ==, smartlist_len(added));
+ md1 = smartlist_get(added, 0);
+ smartlist_free(added);
+ added = NULL;
+
+ wanted = smartlist_create();
+ added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0,
+ time2, wanted);
+ /* Should fail, since we didn't list test_md2's digest in wanted */
+ tt_int_op(0, ==, smartlist_len(added));
+ smartlist_free(added);
+ added = NULL;
+
+ smartlist_add(wanted, tor_memdup(d2, DIGEST256_LEN));
+ smartlist_add(wanted, tor_memdup(d3, DIGEST256_LEN));
+ added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0,
+ time2, wanted);
+ /* Now it can work. md2 should have been added */
+ tt_int_op(1, ==, smartlist_len(added));
+ md2 = smartlist_get(added, 0);
+ /* And it should have gotten removed from 'wanted' */
+ tt_int_op(smartlist_len(wanted), ==, 1);
+ test_mem_op(smartlist_get(wanted, 0), ==, d3, DIGEST256_LEN);
+ smartlist_free(added);
+ added = NULL;
+
+ added = microdescs_add_to_cache(mc, test_md3, NULL,
+ SAVED_NOWHERE, 0, -1, NULL);
+ /* Must fail, since SAVED_NOWHERE precludes annotations */
+ tt_int_op(0, ==, smartlist_len(added));
+ smartlist_free(added);
+ added = NULL;
+
+ added = microdescs_add_to_cache(mc, test_md3_noannotation, NULL,
+ SAVED_NOWHERE, 0, time3, NULL);
+ /* Now it can work */
+ tt_int_op(1, ==, smartlist_len(added));
+ md3 = smartlist_get(added, 0);
+ smartlist_free(added);
+ added = NULL;
+
+ /* Okay. We added 1...3. Let's poke them to see how they look, and make
+ * sure they're really in the journal. */
+ tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
+ tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
+ tt_ptr_op(md3, ==, microdesc_cache_lookup_by_digest256(mc, d3));
+
+ tt_int_op(md1->last_listed, ==, time1);
+ tt_int_op(md2->last_listed, ==, time2);
+ tt_int_op(md3->last_listed, ==, time3);
+
+ tt_int_op(md1->saved_location, ==, SAVED_IN_JOURNAL);
+ tt_int_op(md2->saved_location, ==, SAVED_IN_JOURNAL);
+ tt_int_op(md3->saved_location, ==, SAVED_IN_JOURNAL);
+
+ tt_int_op(md1->bodylen, ==, strlen(test_md1));
+ tt_int_op(md2->bodylen, ==, strlen(test_md2));
+ tt_int_op(md3->bodylen, ==, strlen(test_md3_noannotation));
+ test_mem_op(md1->body, ==, test_md1, strlen(test_md1));
+ test_mem_op(md2->body, ==, test_md2, strlen(test_md2));
+ test_mem_op(md3->body, ==, test_md3_noannotation,
+ strlen(test_md3_noannotation));
+
+ tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs.new",
+ options->DataDirectory);
+ s = read_file_to_str(fn, RFTS_BIN, NULL);
+ tt_assert(s);
+ test_mem_op(md1->body, ==, s + md1->off, md1->bodylen);
+ test_mem_op(md2->body, ==, s + md2->off, md2->bodylen);
+ test_mem_op(md3->body, ==, s + md3->off, md3->bodylen);
+
+ tt_ptr_op(md1->family, ==, NULL);
+ tt_ptr_op(md3->family, !=, NULL);
+ tt_int_op(smartlist_len(md3->family), ==, 3);
+ tt_str_op(smartlist_get(md3->family, 0), ==, "nodeX");
+
+ /* Now rebuild the cache! */
+ tt_int_op(microdesc_cache_rebuild(mc, 1), ==, 0);
+
+ tt_int_op(md1->saved_location, ==, SAVED_IN_CACHE);
+ tt_int_op(md2->saved_location, ==, SAVED_IN_CACHE);
+ tt_int_op(md3->saved_location, ==, SAVED_IN_CACHE);
+
+ /* The journal should be empty now */
+ tor_free(s);
+ s = read_file_to_str(fn, RFTS_BIN, NULL);
+ tt_str_op(s, ==, "");
+ tor_free(s);
+ tor_free(fn);
+
+ /* read the cache. */
+ tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs",
+ options->DataDirectory);
+ s = read_file_to_str(fn, RFTS_BIN, NULL);
+ test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1));
+ test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2));
+ test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation));
+
+ /* Okay, now we are going to forget about the cache entirely, and reload it
+ * from the disk. */
+ microdesc_free_all();
+ mc = get_microdesc_cache();
+ md1 = microdesc_cache_lookup_by_digest256(mc, d1);
+ md2 = microdesc_cache_lookup_by_digest256(mc, d2);
+ md3 = microdesc_cache_lookup_by_digest256(mc, d3);
+ test_assert(md1);
+ test_assert(md2);
+ test_assert(md3);
+ test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1));
+ test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2));
+ test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation));
+
+ tt_int_op(md1->last_listed, ==, time1);
+ tt_int_op(md2->last_listed, ==, time2);
+ tt_int_op(md3->last_listed, ==, time3);
+
+ /* Okay, now we are going to clear out everything older than a week old.
+ * In practice, that means md3 */
+ microdesc_cache_clean(mc, time(NULL)-7*24*60*60, 1/*force*/);
+ tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
+ tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
+ tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3));
+ md3 = NULL; /* it's history now! */
+
+ /* rebuild again, make sure it stays gone. */
+ microdesc_cache_rebuild(mc, 1);
+ tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
+ tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
+ tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3));
+
+ done:
+ if (options)
+ tor_free(options->DataDirectory);
+ microdesc_free_all();
+
+ smartlist_free(added);
+ if (wanted)
+ SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
+ smartlist_free(wanted);
+ tor_free(s);
+ tor_free(fn);
+}
+
+struct testcase_t microdesc_tests[] = {
+ { "cache", test_md_cache, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_pt.c b/src/test/test_pt.c
new file mode 100644
index 000000000..f97b21fa0
--- /dev/null
+++ b/src/test/test_pt.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define PT_PRIVATE
+#include "or.h"
+#include "transports.h"
+#include "circuitbuild.h"
+#include "test.h"
+
+static void
+reset_mp(managed_proxy_t *mp)
+{
+ mp->conf_state = PT_PROTO_LAUNCHED;
+ SMARTLIST_FOREACH(mp->transports, transport_t *, t, transport_free(t));
+ smartlist_clear(mp->transports);
+}
+
+static void
+test_pt_parsing(void)
+{
+ char line[200];
+
+ managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t));
+ mp->conf_state = PT_PROTO_INFANT;
+ mp->transports = smartlist_create();
+
+ /* incomplete cmethod */
+ strcpy(line,"CMETHOD trebuchet");
+ test_assert(parse_cmethod_line(line, mp) < 0);
+
+ reset_mp(mp);
+
+ /* wrong proxy type */
+ strcpy(line,"CMETHOD trebuchet dog 127.0.0.1:1999");
+ test_assert(parse_cmethod_line(line, mp) < 0);
+
+ reset_mp(mp);
+
+ /* wrong addrport */
+ strcpy(line,"CMETHOD trebuchet socks4 abcd");
+ test_assert(parse_cmethod_line(line, mp) < 0);
+
+ reset_mp(mp);
+
+ /* correct line */
+ strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999");
+ test_assert(parse_cmethod_line(line, mp) == 0);
+ test_assert(smartlist_len(mp->transports));
+
+ reset_mp(mp);
+
+ /* incomplete smethod */
+ strcpy(line,"SMETHOD trebuchet");
+ test_assert(parse_smethod_line(line, mp) < 0);
+
+ reset_mp(mp);
+
+ /* wrong addr type */
+ strcpy(line,"SMETHOD trebuchet abcd");
+ test_assert(parse_smethod_line(line, mp) < 0);
+
+ reset_mp(mp);
+
+ /* cowwect */
+ strcpy(line,"SMETHOD trebuchy 127.0.0.1:1999");
+ test_assert(parse_smethod_line(line, mp) == 0);
+
+ reset_mp(mp);
+
+ /* unsupported version */
+ strcpy(line,"VERSION 666");
+ test_assert(parse_version(line, mp) < 0);
+
+ /* incomplete VERSION */
+ strcpy(line,"VERSION ");
+ test_assert(parse_version(line, mp) < 0);
+
+ /* correct VERSION */
+ strcpy(line,"VERSION 1");
+ test_assert(parse_version(line, mp) == 0);
+
+ done:
+ tor_free(mp);
+}
+
+static void
+test_pt_protocol(void)
+{
+ char line[200];
+
+ managed_proxy_t *mp = tor_malloc(sizeof(managed_proxy_t));
+ mp->conf_state = PT_PROTO_LAUNCHED;
+ mp->transports = smartlist_create();
+
+ /* various wrong protocol runs: */
+
+ strcpy(line, "TEST TEST");
+ handle_proxy_line(line, mp);
+ test_assert(mp->conf_state == PT_PROTO_BROKEN);
+
+ reset_mp(mp);
+
+ strcpy(line,"VERSION 1");
+ handle_proxy_line(line, mp);
+ test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
+
+ strcpy(line,"VERSION 1");
+ handle_proxy_line(line, mp);
+ test_assert(mp->conf_state == PT_PROTO_BROKEN);
+
+ reset_mp(mp);
+
+ strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999");
+ handle_proxy_line(line, mp);
+ test_assert(mp->conf_state == PT_PROTO_BROKEN);
+
+ reset_mp(mp);
+
+ /* correct protocol run: */
+ strcpy(line,"VERSION 1");
+ handle_proxy_line(line, mp);
+ test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
+
+ strcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999");
+ handle_proxy_line(line, mp);
+ test_assert(mp->conf_state == PT_PROTO_ACCEPTING_METHODS);
+
+ strcpy(line,"CMETHODS DONE");
+ handle_proxy_line(line, mp);
+ test_assert(mp->conf_state == PT_PROTO_CONFIGURED);
+
+ done:
+ tor_free(mp);
+}
+
+#define PT_LEGACY(name) \
+ { #name, legacy_test_helper, 0, &legacy_setup, test_pt_ ## name }
+
+struct testcase_t pt_tests[] = {
+ PT_LEGACY(parsing),
+ PT_LEGACY(protocol),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 23cd059cf..6603ab00d 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -6,6 +6,7 @@
#include "orconfig.h"
#define CONTROL_PRIVATE
#define MEMPOOL_PRIVATE
+#define UTIL_PRIVATE
#include "or.h"
#include "config.h"
#include "control.h"
@@ -375,7 +376,7 @@ test_util_strmisc(void)
/* Test memmem and memstr */
{
const char *haystack = "abcde";
- tor_assert(!tor_memmem(haystack, 5, "ef", 2));
+ tt_assert(!tor_memmem(haystack, 5, "ef", 2));
test_eq_ptr(tor_memmem(haystack, 5, "cd", 2), haystack + 2);
test_eq_ptr(tor_memmem(haystack, 5, "cde", 3), haystack + 2);
haystack = "ababcad";
@@ -639,26 +640,26 @@ test_util_gzip(void)
tor_strdup("String with low redundancy that won't be compressed much.");
test_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
ZLIB_METHOD));
- tor_assert(len1>16);
+ tt_assert(len1>16);
/* when we allow an incomplete string, we should succeed.*/
- tor_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
+ tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
ZLIB_METHOD, 0, LOG_INFO));
buf3[len2]='\0';
- tor_assert(len2 > 5);
- tor_assert(!strcmpstart(buf1, buf3));
+ tt_assert(len2 > 5);
+ tt_assert(!strcmpstart(buf1, buf3));
/* when we demand a complete string, this must fail. */
tor_free(buf3);
- tor_assert(tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
+ tt_assert(tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
ZLIB_METHOD, 1, LOG_INFO));
- tor_assert(!buf3);
+ tt_assert(!buf3);
/* Now, try streaming compression. */
tor_free(buf1);
tor_free(buf2);
tor_free(buf3);
state = tor_zlib_new(1, ZLIB_METHOD);
- tor_assert(state);
+ tt_assert(state);
cp1 = buf1 = tor_malloc(1024);
len1 = 1024;
ccp2 = "ABCDEFGHIJABCDEFGHIJ";
@@ -675,7 +676,7 @@ test_util_gzip(void)
test_eq(len2, 0);
test_assert(cp1 > cp2); /* Make sure we really added something. */
- tor_assert(!tor_gzip_uncompress(&buf3, &len2, buf1, 1024-len1,
+ tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf1, 1024-len1,
ZLIB_METHOD, 1, LOG_WARN));
test_streq(buf3, "ABCDEFGHIJABCDEFGHIJ"); /*Make sure it compressed right.*/
@@ -832,6 +833,18 @@ test_util_sscanf(void)
test_eq(u2, 3u);
test_eq(u3, 99u);
+ /* %x should work. */
+ r = tor_sscanf("1234 02aBcdEf", "%x %x", &u1, &u2);
+ test_eq(r, 2);
+ test_eq(u1, 0x1234);
+ test_eq(u2, 0x2ABCDEF);
+ /* Width works on %x */
+ r = tor_sscanf("f00dcafe444", "%4x%4x%u", &u1, &u2, &u3);
+ test_eq(r, 3);
+ test_eq(u1, 0xf00d);
+ test_eq(u2, 0xcafe);
+ test_eq(u3, 444);
+
r = tor_sscanf("99% fresh", "%3u%% fresh", &u1); /* percents are scannable.*/
test_eq(r, 1);
test_eq(u1, 99);
@@ -1239,6 +1252,440 @@ test_util_load_win_lib(void *ptr)
#endif
static void
+clear_hex_errno(char *hex_errno)
+{
+ memset(hex_errno, '\0', HEX_ERRNO_SIZE + 1);
+}
+
+static void
+test_util_exit_status(void *ptr)
+{
+ /* Leave an extra byte for a \0 so we can do string comparison */
+ char hex_errno[HEX_ERRNO_SIZE + 1];
+
+ (void)ptr;
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0, 0, hex_errno);
+ tt_str_op(hex_errno, ==, " 0/0\n");
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0, 0x7FFFFFFF, hex_errno);
+ tt_str_op(hex_errno, ==, " 0/7FFFFFFF\n");
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0xFF, -0x80000000, hex_errno);
+ tt_str_op(hex_errno, ==, "FF/-80000000\n");
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0x7F, 0, hex_errno);
+ tt_str_op(hex_errno, ==, " 7F/0\n");
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0x08, -0x242, hex_errno);
+ tt_str_op(hex_errno, ==, " 8/-242\n");
+
+ done:
+ ;
+}
+
+#ifndef MS_WINDOWS
+/** Check that fgets waits until a full line, and not return a partial line, on
+ * a EAGAIN with a non-blocking pipe */
+static void
+test_util_fgets_eagain(void *ptr)
+{
+ int test_pipe[2] = {-1, -1};
+ int retval;
+ ssize_t retlen;
+ char *retptr;
+ FILE *test_stream = NULL;
+ char buf[10];
+
+ (void)ptr;
+
+ /* Set up a pipe to test on */
+ retval = pipe(test_pipe);
+ tt_int_op(retval, >=, 0);
+
+ /* Set up the read-end to be non-blocking */
+ retval = fcntl(test_pipe[0], F_SETFL, O_NONBLOCK);
+ tt_int_op(retval, >=, 0);
+
+ /* Open it as a stdio stream */
+ test_stream = fdopen(test_pipe[0], "r");
+ tt_ptr_op(test_stream, !=, NULL);
+
+ /* Send in a partial line */
+ retlen = write(test_pipe[1], "A", 1);
+ tt_int_op(retlen, ==, 1);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_want(retptr == NULL);
+ tt_int_op(errno, ==, EAGAIN);
+
+ /* Send in the rest */
+ retlen = write(test_pipe[1], "B\n", 2);
+ tt_int_op(retlen, ==, 2);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, buf);
+ tt_str_op(buf, ==, "AB\n");
+
+ /* Send in a full line */
+ retlen = write(test_pipe[1], "CD\n", 3);
+ tt_int_op(retlen, ==, 3);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, buf);
+ tt_str_op(buf, ==, "CD\n");
+
+ /* Send in a partial line */
+ retlen = write(test_pipe[1], "E", 1);
+ tt_int_op(retlen, ==, 1);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, NULL);
+ tt_int_op(errno, ==, EAGAIN);
+
+ /* Send in the rest */
+ retlen = write(test_pipe[1], "F\n", 2);
+ tt_int_op(retlen, ==, 2);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, buf);
+ tt_str_op(buf, ==, "EF\n");
+
+ /* Send in a full line and close */
+ retlen = write(test_pipe[1], "GH", 2);
+ tt_int_op(retlen, ==, 2);
+ retval = close(test_pipe[1]);
+ test_pipe[1] = -1;
+ tt_int_op(retval, ==, 0);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, buf);
+ tt_str_op(buf, ==, "GH");
+
+ /* Check for EOF */
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, NULL);
+ tt_int_op(feof(test_stream), >, 0);
+
+ done:
+ if (test_stream != NULL)
+ fclose(test_stream);
+ if (test_pipe[0] != -1)
+ close(test_pipe[0]);
+ if (test_pipe[1] != -1)
+ close(test_pipe[1]);
+}
+#endif
+
+/** Helper function for testing tor_spawn_background */
+static void
+run_util_spawn_background(const char *argv[], const char *expected_out,
+ const char *expected_err, int expected_exit,
+ int expected_status)
+{
+ int retval, exit_code;
+ ssize_t pos;
+ process_handle_t process_handle;
+ char stdout_buf[100], stderr_buf[100];
+
+ /* Start the program */
+#ifdef MS_WINDOWS
+ tor_spawn_background(NULL, argv, NULL, &process_handle);
+#else
+ tor_spawn_background(argv[0], argv, NULL, &process_handle);
+#endif
+
+ tt_int_op(process_handle.status, ==, expected_status);
+
+ /* If the process failed to start, don't bother continuing */
+ if (process_handle.status == PROCESS_STATUS_ERROR)
+ return;
+
+ tt_int_op(process_handle.stdout_pipe, >, 0);
+ tt_int_op(process_handle.stderr_pipe, >, 0);
+
+ /* Check stdout */
+ pos = tor_read_all_from_process_stdout(&process_handle, stdout_buf,
+ sizeof(stdout_buf) - 1);
+ tt_assert(pos >= 0);
+ stdout_buf[pos] = '\0';
+ tt_str_op(stdout_buf, ==, expected_out);
+ tt_int_op(pos, ==, strlen(expected_out));
+
+ /* Check it terminated correctly */
+ retval = tor_get_exit_code(process_handle, 1, &exit_code);
+ tt_int_op(retval, ==, PROCESS_EXIT_EXITED);
+ tt_int_op(exit_code, ==, expected_exit);
+ // TODO: Make test-child exit with something other than 0
+
+ /* Check stderr */
+ pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf,
+ sizeof(stderr_buf) - 1);
+ tt_assert(pos >= 0);
+ stderr_buf[pos] = '\0';
+ tt_str_op(stderr_buf, ==, expected_err);
+ tt_int_op(pos, ==, strlen(expected_err));
+
+ done:
+ ;
+}
+
+/** Check that we can launch a process and read the output */
+static void
+test_util_spawn_background_ok(void *ptr)
+{
+#ifdef MS_WINDOWS
+ const char *argv[] = {"test-child.exe", "--test", NULL};
+ const char *expected_out = "OUT\r\n--test\r\nSLEEPING\r\nDONE\r\n";
+ const char *expected_err = "ERR\r\n";
+#else
+ const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
+ const char *expected_out = "OUT\n--test\nSLEEPING\nDONE\n";
+ const char *expected_err = "ERR\n";
+#endif
+
+ (void)ptr;
+
+ run_util_spawn_background(argv, expected_out, expected_err, 0,
+ PROCESS_STATUS_RUNNING);
+}
+
+/** Check that failing to find the executable works as expected */
+static void
+test_util_spawn_background_fail(void *ptr)
+{
+#ifdef MS_WINDOWS
+ const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
+ const char *expected_out = "ERR: Failed to spawn background process "
+ "- code 9/2\n";
+ const char *expected_err = "";
+ const int expected_status = PROCESS_STATUS_ERROR;
+#else
+ const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
+ const char *expected_out = "ERR: Failed to spawn background process "
+ "- code 9/2\n";
+ const char *expected_err = "";
+ /* TODO: Once we can signal failure to exec, set this to be
+ * PROCESS_STATUS_ERROR */
+ const int expected_status = PROCESS_STATUS_RUNNING;
+#endif
+
+ (void)ptr;
+
+ run_util_spawn_background(argv, expected_out, expected_err, 255,
+ expected_status);
+}
+
+/** Test that reading from a handle returns a partial read rather than
+ * blocking */
+static void
+test_util_spawn_background_partial_read(void *ptr)
+{
+ const int expected_exit = 0;
+ const int expected_status = PROCESS_STATUS_RUNNING;
+
+ int retval, exit_code;
+ ssize_t pos = -1;
+ process_handle_t process_handle;
+ char stdout_buf[100], stderr_buf[100];
+#ifdef MS_WINDOWS
+ const char *argv[] = {"test-child.exe", "--test", NULL};
+ const char *expected_out[] = { "OUT\r\n--test\r\nSLEEPING\r\n",
+ "DONE\r\n",
+ NULL };
+ const char *expected_err = "ERR\r\n";
+#else
+ const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
+ const char *expected_out[] = { "OUT\n--test\nSLEEPING\n",
+ "DONE\n",
+ NULL };
+ const char *expected_err = "ERR\n";
+ int eof = 0;
+#endif
+ int expected_out_ctr;
+ (void)ptr;
+
+ /* Start the program */
+#ifdef MS_WINDOWS
+ tor_spawn_background(NULL, argv, NULL, &process_handle);
+#else
+ tor_spawn_background(argv[0], argv, NULL, &process_handle);
+#endif
+ tt_int_op(process_handle.status, ==, expected_status);
+
+ /* Check stdout */
+ for (expected_out_ctr =0; expected_out[expected_out_ctr] != NULL;) {
+#ifdef MS_WINDOWS
+ pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf,
+ sizeof(stdout_buf) - 1, NULL);
+#else
+ /* Check that we didn't read the end of file last time */
+ tt_assert(!eof);
+ pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf,
+ sizeof(stdout_buf) - 1, NULL, &eof);
+#endif
+ log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
+
+ /* We would have blocked, keep on trying */
+ if (0 == pos)
+ continue;
+
+ tt_int_op(pos, >, 0);
+ stdout_buf[pos] = '\0';
+ tt_str_op(stdout_buf, ==, expected_out[expected_out_ctr]);
+ tt_int_op(pos, ==, strlen(expected_out[expected_out_ctr]));
+ expected_out_ctr++;
+ }
+
+ /* The process should have exited without writing more */
+#ifdef MS_WINDOWS
+ pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf,
+ sizeof(stdout_buf) - 1,
+ &process_handle);
+ tt_int_op(pos, ==, 0);
+#else
+ if (!eof) {
+ /* We should have got all the data, but maybe not the EOF flag */
+ pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf,
+ sizeof(stdout_buf) - 1,
+ &process_handle, &eof);
+ tt_int_op(pos, ==, 0);
+ tt_assert(eof);
+ }
+ /* Otherwise, we got the EOF on the last read */
+#endif
+
+ /* Check it terminated correctly */
+ retval = tor_get_exit_code(process_handle, 1, &exit_code);
+ tt_int_op(retval, ==, PROCESS_EXIT_EXITED);
+ tt_int_op(exit_code, ==, expected_exit);
+
+ // TODO: Make test-child exit with something other than 0
+
+ /* Check stderr */
+ pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf,
+ sizeof(stderr_buf) - 1);
+ tt_assert(pos >= 0);
+ stderr_buf[pos] = '\0';
+ tt_str_op(stderr_buf, ==, expected_err);
+ tt_int_op(pos, ==, strlen(expected_err));
+
+ done:
+ ;
+}
+
+/**
+ * Test that we can properly format q Windows command line
+ */
+static void
+test_util_join_win_cmdline(void *ptr)
+{
+ /* Based on some test cases from "Parsing C++ Command-Line Arguments" in
+ * MSDN but we don't exercise all quoting rules because tor_join_win_cmdline
+ * will try to only generate simple cases for the child process to parse;
+ * i.e. we never embed quoted strings in arguments. */
+
+ const char *argvs[][4] = {
+ {"a", "bb", "CCC", NULL}, // Normal
+ {NULL, NULL, NULL, NULL}, // Empty argument list
+ {"", NULL, NULL, NULL}, // Empty argument
+ {"\"a", "b\"b", "CCC\"", NULL}, // Quotes
+ {"a\tbc", "dd dd", "E", NULL}, // Whitespace
+ {"a\\\\\\b", "de fg", "H", NULL}, // Backslashes
+ {"a\\\"b", "\\c", "D\\", NULL}, // Backslashes before quote
+ {"a\\\\b c", "d", "E", NULL}, // Backslashes not before quote
+ {} // Terminator
+ };
+
+ const char *cmdlines[] = {
+ "a bb CCC",
+ "",
+ "\"\"",
+ "\\\"a b\\\"b CCC\\\"",
+ "\"a\tbc\" \"dd dd\" E",
+ "a\\\\\\b \"de fg\" H",
+ "a\\\\\\\"b \\c D\\",
+ "\"a\\\\b c\" d E",
+ NULL // Terminator
+ };
+
+ int i;
+ char *joined_argv;
+
+ (void)ptr;
+
+ for (i=0; cmdlines[i]!=NULL; i++) {
+ log_info(LD_GENERAL, "Joining argvs[%d], expecting <%s>", i, cmdlines[i]);
+ joined_argv = tor_join_win_cmdline(argvs[i]);
+ tt_str_op(joined_argv, ==, cmdlines[i]);
+ tor_free(joined_argv);
+ }
+
+ done:
+ ;
+}
+
+#define MAX_SPLIT_LINE_COUNT 3
+struct split_lines_test_t {
+ const char *orig_line; // Line to be split (may contain \0's)
+ int orig_length; // Length of orig_line
+ const char *split_line[MAX_SPLIT_LINE_COUNT]; // Split lines
+};
+
+/**
+ * Test that we properly split a buffer into lines
+ */
+static void
+test_util_split_lines(void *ptr)
+{
+ /* Test cases. orig_line of last test case must be NULL.
+ * The last element of split_line[i] must be NULL. */
+ struct split_lines_test_t tests[] = {
+ {"", 0, {NULL}},
+ {"foo", 3, {"foo", NULL}},
+ {"\n\rfoo\n\rbar\r\n", 12, {"foo", "bar", NULL}},
+ {"fo o\r\nb\tar", 10, {"fo o", "b.ar", NULL}},
+ {"\x0f""f\0o\0\n\x01""b\0r\0\r", 12, {".f.o.", ".b.r.", NULL}},
+ {NULL, 0, {}}
+ };
+
+ int i, j;
+ char *orig_line;
+ smartlist_t *sl;
+
+ (void)ptr;
+
+ for (i=0; tests[i].orig_line; i++) {
+ sl = smartlist_create();
+ /* Allocate space for string and trailing NULL */
+ orig_line = tor_memdup(tests[i].orig_line, tests[i].orig_length + 1);
+ tor_split_lines(sl, orig_line, tests[i].orig_length);
+
+ j = 0;
+ log_info(LD_GENERAL, "Splitting test %d of length %d",
+ i, tests[i].orig_length);
+ SMARTLIST_FOREACH(sl, const char *, line,
+ {
+ /* Check we have not got too many lines */
+ tt_int_op(j, <, MAX_SPLIT_LINE_COUNT);
+ /* Check that there actually should be a line here */
+ tt_assert(tests[i].split_line[j] != NULL);
+ log_info(LD_GENERAL, "Line %d of test %d, should be <%s>",
+ j, i, tests[i].split_line[j]);
+ /* Check that the line is as expected */
+ tt_str_op(tests[i].split_line[j], ==, line);
+ j++;
+ });
+ /* Check that we didn't miss some lines */
+ tt_assert(tests[i].split_line[j] == NULL);
+ tor_free(orig_line);
+ smartlist_free(sl);
+ }
+
+ done:
+ ;
+}
+
+static void
test_util_di_ops(void)
{
#define LT -1
@@ -1319,6 +1766,15 @@ struct testcase_t util_tests[] = {
#ifdef MS_WINDOWS
UTIL_TEST(load_win_lib, 0),
#endif
+ UTIL_TEST(exit_status, 0),
+#ifndef MS_WINDOWS
+ UTIL_TEST(fgets_eagain, TT_SKIP),
+#endif
+ UTIL_TEST(spawn_background_ok, 0),
+ UTIL_TEST(spawn_background_fail, 0),
+ UTIL_TEST(spawn_background_partial_read, 0),
+ UTIL_TEST(join_win_cmdline, 0),
+ UTIL_TEST(split_lines, 0),
END_OF_TESTCASES
};
diff --git a/src/test/tinytest.c b/src/test/tinytest.c
index 11ffc2fe5..8caa4f545 100644
--- a/src/test/tinytest.c
+++ b/src/test/tinytest.c
@@ -28,7 +28,11 @@
#include <string.h>
#include <assert.h>
-#ifdef WIN32
+#ifdef TINYTEST_LOCAL
+#include "tinytest_local.h"
+#endif
+
+#ifdef _WIN32
#include <windows.h>
#else
#include <sys/types.h>
@@ -40,9 +44,6 @@
#define __attribute__(x)
#endif
-#ifdef TINYTEST_LOCAL
-#include "tinytest_local.h"
-#endif
#include "tinytest.h"
#include "tinytest_macros.h"
@@ -64,7 +65,7 @@ const char *cur_test_prefix = NULL; /**< prefix of the current test group */
/** Name of the current test, if we haven't logged is yet. Used for --quiet */
const char *cur_test_name = NULL;
-#ifdef WIN32
+#ifdef _WIN32
/** Pointer to argv[0] for win32. */
static const char *commandname = NULL;
#endif
@@ -103,7 +104,7 @@ static enum outcome
_testcase_run_forked(const struct testgroup_t *group,
const struct testcase_t *testcase)
{
-#ifdef WIN32
+#ifdef _WIN32
/* Fork? On Win32? How primitive! We'll do what the smart kids do:
we'll invoke our own exe (whose name we recall from the command
line) with a command line that tells it to run just the test we
@@ -174,6 +175,7 @@ _testcase_run_forked(const struct testgroup_t *group,
exit(1);
}
exit(0);
+ return FAIL; /* unreachable */
} else {
/* parent */
int status, r;
@@ -239,6 +241,7 @@ testcase_run_one(const struct testgroup_t *group,
if (opt_forked) {
exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
+ return 1; /* unreachable */
} else {
return (int)outcome;
}
@@ -287,7 +290,7 @@ tinytest_main(int c, const char **v, struct testgroup_t *groups)
{
int i, j, n=0;
-#ifdef WIN32
+#ifdef _WIN32
commandname = v[0];
#endif
for (i=1; i<c; ++i) {
diff --git a/src/test/tinytest_demo.c b/src/test/tinytest_demo.c
index 4d2f58843..98cb773d1 100644
--- a/src/test/tinytest_demo.c
+++ b/src/test/tinytest_demo.c
@@ -1,4 +1,4 @@
-/* tinytest_demo.c -- Copyright 2009 Nick Mathewson
+/* tinytest_demo.c -- Copyright 2009-2010 Nick Mathewson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -53,7 +53,7 @@ test_strcmp(void *data)
}
/* Pretty often, calling tt_abort_msg to indicate failure is more
- heavy-weight than you want. Instead, just say: */
+ heavy-weight than you want. Instead, just say: */
tt_assert(strcmp("testcase", "testcase") == 0);
/* Occasionally, you don't want to stop the current testcase just
@@ -91,7 +91,7 @@ test_strcmp(void *data)
/* First you declare a type to hold the environment info, and functions to
set it up and tear it down. */
struct data_buffer {
- /* We're just going to have couple of character buffer. Using
+ /* We're just going to have couple of character buffer. Using
setup/teardown functions is probably overkill for this case.
You could also do file descriptors, complicated handles, temporary
@@ -164,7 +164,7 @@ test_memcpy(void *ptr)
/* ============================================================ */
-/* Now we need to make sure that our tests get invoked. First, you take
+/* Now we need to make sure that our tests get invoked. First, you take
a bunch of related tests and put them into an array of struct testcase_t.
*/
@@ -189,15 +189,15 @@ struct testgroup_t groups[] = {
/* Every group has a 'prefix', and an array of tests. That's it. */
{ "demo/", demo_tests },
- END_OF_GROUPS
+ END_OF_GROUPS
};
int
main(int c, const char **v)
{
- /* Finally, just call tinytest_main(). It lets you specify verbose
- or quiet output with --verbose and --quiet. You can list
+ /* Finally, just call tinytest_main(). It lets you specify verbose
+ or quiet output with --verbose and --quiet. You can list
specific tests:
tinytest-demo demo/memcpy
diff --git a/src/test/tinytest_macros.h b/src/test/tinytest_macros.h
index a7fa64a82..032393ccf 100644
--- a/src/test/tinytest_macros.h
+++ b/src/test/tinytest_macros.h
@@ -90,10 +90,10 @@
TT_STMT_BEGIN \
if (!(b)) { \
_tinytest_set_test_failed(); \
- TT_GRIPE((msg)); \
+ TT_GRIPE(("%s",msg)); \
fail; \
} else { \
- TT_BLATHER((msg)); \
+ TT_BLATHER(("%s",msg)); \
} \
TT_STMT_END
@@ -111,7 +111,7 @@
#define tt_assert(b) tt_assert_msg((b), "assert("#b")")
#define tt_assert_test_fmt_type(a,b,str_test,type,test,printf_type,printf_fmt, \
- setup_block,cleanup_block) \
+ setup_block,cleanup_block,die_on_fail) \
TT_STMT_BEGIN \
type _val1 = (type)(a); \
type _val2 = (type)(b); \
@@ -135,33 +135,50 @@
cleanup_block; \
if (!_tt_status) { \
_tinytest_set_test_failed(); \
- TT_EXIT_TEST_FUNCTION; \
+ die_on_fail ; \
} \
} \
TT_STMT_END
-#define tt_assert_test_type(a,b,str_test,type,test,fmt) \
+#define tt_assert_test_type(a,b,str_test,type,test,fmt,die_on_fail) \
tt_assert_test_fmt_type(a,b,str_test,type,test,type,fmt, \
- {_print=_value;},{})
+ {_print=_value;},{},die_on_fail)
/* Helper: assert that a op b, when cast to type. Format the values with
* printf format fmt on failure. */
#define tt_assert_op_type(a,op,b,type,fmt) \
- tt_assert_test_type(a,b,#a" "#op" "#b,type,(_val1 op _val2),fmt)
+ tt_assert_test_type(a,b,#a" "#op" "#b,type,(_val1 op _val2),fmt, \
+ TT_EXIT_TEST_FUNCTION)
#define tt_int_op(a,op,b) \
- tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2),"%ld")
+ tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2), \
+ "%ld",TT_EXIT_TEST_FUNCTION)
#define tt_uint_op(a,op,b) \
tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \
- (_val1 op _val2),"%lu")
+ (_val1 op _val2),"%lu",TT_EXIT_TEST_FUNCTION)
#define tt_ptr_op(a,op,b) \
tt_assert_test_type(a,b,#a" "#op" "#b,void*, \
- (_val1 op _val2),"%p")
+ (_val1 op _val2),"%p",TT_EXIT_TEST_FUNCTION)
#define tt_str_op(a,op,b) \
tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \
- (strcmp(_val1,_val2) op 0),"<%s>")
+ (strcmp(_val1,_val2) op 0),"<%s>",TT_EXIT_TEST_FUNCTION)
+
+#define tt_want_int_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2),"%ld",(void)0)
+
+#define tt_want_uint_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \
+ (_val1 op _val2),"%lu",(void)0)
+
+#define tt_want_ptr_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,void*, \
+ (_val1 op _val2),"%p",(void)0)
+
+#define tt_want_str_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \
+ (strcmp(_val1,_val2) op 0),"<%s>",(void)0)
#endif
diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
index 1bb507684..a9a619757 100644
--- a/src/tools/Makefile.am
+++ b/src/tools/Makefile.am
@@ -2,17 +2,19 @@ bin_PROGRAMS = tor-resolve tor-gencert
noinst_PROGRAMS = tor-checkkey
tor_resolve_SOURCES = tor-resolve.c
-tor_resolve_LDFLAGS = @TOR_LDFLAGS_libevent@
-tor_resolve_LDADD = ../common/libor.a -lm @TOR_LIBEVENT_LIBS@ @TOR_LIB_WS32@
+tor_resolve_LDFLAGS =
+tor_resolve_LDADD = ../common/libor.a -lm @TOR_LIB_WS32@
tor_gencert_SOURCES = tor-gencert.c
-tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
- @TOR_LDFLAGS_libevent@
+tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
tor_gencert_LDADD = ../common/libor.a ../common/libor-crypto.a \
- -lm @TOR_ZLIB_LIBS@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+ -lm @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
tor_checkkey_SOURCES = tor-checkkey.c
-tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
- @TOR_LDFLAGS_libevent@
+tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
tor_checkkey_LDADD = ../common/libor.a ../common/libor-crypto.a \
- -lm @TOR_ZLIB_LIBS@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+ -lm @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+
+SUBDIRS = tor-fw-helper
+DIST_SUBDIRS = tor-fw-helper
+
diff --git a/src/tools/tor-fw-helper/Makefile.am b/src/tools/tor-fw-helper/Makefile.am
new file mode 100644
index 000000000..8f64ad2ba
--- /dev/null
+++ b/src/tools/tor-fw-helper/Makefile.am
@@ -0,0 +1,38 @@
+if USE_FW_HELPER
+bin_PROGRAMS = tor-fw-helper
+else
+bin_PROGRAMS =
+endif
+
+tor_fw_helper_SOURCES = \
+ tor-fw-helper.c \
+ tor-fw-helper-natpmp.c \
+ tor-fw-helper-upnp.c
+noinst_HEADERS = \
+ tor-fw-helper.h \
+ tor-fw-helper-natpmp.h \
+ tor-fw-helper-upnp.h
+
+if NAT_PMP
+nat_pmp_ldflags = @TOR_LDFLAGS_libnatpmp@
+nat_pmp_ldadd = -lnatpmp
+nat_pmp_cppflags = @TOR_CPPFLAGS_libnatpmp@
+else
+nat_pmp_ldflags =
+nat_pmp_ldadd =
+nat_pmp_cppflags =
+endif
+
+if MINIUPNPC
+miniupnpc_ldflags = @TOR_LDFLAGS_libminiupnpc@
+miniupnpc_ldadd = -lminiupnpc -lm @TOR_LIB_IPHLPAPI@
+miniupnpc_cppflags = @TOR_CPPFLAGS_libminiupnpc@
+else
+miniupnpc_ldflags =
+miniupnpc_ldadd =
+miniupnpc_cppflags =
+endif
+
+tor_fw_helper_LDFLAGS = $(nat_pmp_ldflags) $(miniupnpc_ldflags)
+tor_fw_helper_LDADD = ../../common/libor.a $(nat_pmp_ldadd) $(miniupnpc_ldadd) @TOR_LIB_WS32@
+tor_fw_helper_CPPFLAGS = $(nat_pmp_cppflags) $(miniupnpc_cppflags)
diff --git a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c
new file mode 100644
index 000000000..f9d5d0d58
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper-natpmp.c
+ * \brief The implementation of our NAT-PMP firewall helper.
+ **/
+
+#include "orconfig.h"
+#ifdef NAT_PMP
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+// debugging stuff
+#include <assert.h>
+
+#include "tor-fw-helper.h"
+#include "tor-fw-helper-natpmp.h"
+
+/** This hooks NAT-PMP into our multi-backend API. */
+static tor_fw_backend_t tor_natpmp_backend = {
+ "natpmp",
+ sizeof(struct natpmp_state_t),
+ tor_natpmp_init,
+ tor_natpmp_cleanup,
+ tor_natpmp_fetch_public_ip,
+ tor_natpmp_add_tcp_mapping
+};
+
+/** Return the backend for NAT-PMP. */
+const tor_fw_backend_t *
+tor_fw_get_natpmp_backend(void)
+{
+ return &tor_natpmp_backend;
+}
+
+/** Initialize the NAT-PMP backend and store the results in
+ * <b>backend_state</b>.*/
+int
+tor_natpmp_init(tor_fw_options_t *tor_fw_options, void *backend_state)
+{
+ natpmp_state_t *state = (natpmp_state_t *) backend_state;
+ int r = 0;
+
+ memset(&(state->natpmp), 0, sizeof(natpmp_t));
+ memset(&(state->response), 0, sizeof(natpmpresp_t));
+ state->init = 0;
+ state->protocol = NATPMP_PROTOCOL_TCP;
+ state->lease = NATPMP_DEFAULT_LEASE;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: natpmp init...\n");
+
+ r = initnatpmp(&(state->natpmp), 0, 0);
+ if (r == 0) {
+ state->init = 1;
+ fprintf(stdout, "tor-fw-helper: natpmp initialized...\n");
+ return r;
+ } else {
+ fprintf(stderr, "tor-fw-helper: natpmp failed to initialize...\n");
+ return r;
+ }
+}
+
+/** Tear down the NAT-PMP connection stored in <b>backend_state</b>.*/
+int
+tor_natpmp_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state)
+{
+ natpmp_state_t *state = (natpmp_state_t *) backend_state;
+ int r = 0;
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: natpmp cleanup...\n");
+ r = closenatpmp(&(state->natpmp));
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: closing natpmp socket: %d\n", r);
+ return r;
+}
+
+/** Use select() to wait until we can read on fd. */
+static int
+wait_until_fd_readable(int fd, struct timeval *timeout)
+{
+ int r;
+ fd_set fds;
+ if (fd >= FD_SETSIZE) {
+ fprintf(stderr, "E: NAT-PMP FD_SETSIZE error %d\n", fd);
+ return -1;
+ }
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ r = select(fd+1, &fds, NULL, NULL, timeout);
+ if (r == -1) {
+ fprintf(stdout, "V: select failed in wait_until_fd_readable: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ /* XXXX we should really check to see whether fd was readable, or we timed
+ out. */
+ return 0;
+}
+
+/** Add a TCP port mapping for a single port stored in <b>tor_fw_options</b>
+ * using the <b>natpmp_t</b> stored in <b>backend_state</b>. */
+int
+tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options,
+ void *backend_state)
+{
+ natpmp_state_t *state = (natpmp_state_t *) backend_state;
+ int r = 0;
+ int x = 0;
+ int sav_errno;
+
+ struct timeval timeout;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: sending natpmp portmapping request...\n");
+ r = sendnewportmappingrequest(&(state->natpmp), state->protocol,
+ tor_fw_options->internal_port,
+ tor_fw_options->external_port,
+ state->lease);
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "tor-fw-helper: NAT-PMP sendnewportmappingrequest "
+ "returned %d (%s)\n", r, r==12?"SUCCESS":"FAILED");
+
+ do {
+ getnatpmprequesttimeout(&(state->natpmp), &timeout);
+ x = wait_until_fd_readable(state->natpmp.s, &timeout);
+ if (x == -1)
+ return -1;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: attempting to readnatpmpreponseorretry...\n");
+ r = readnatpmpresponseorretry(&(state->natpmp), &(state->response));
+ sav_errno = errno;
+
+ if (r<0 && r!=NATPMP_TRYAGAIN) {
+ fprintf(stderr, "E: readnatpmpresponseorretry failed %d\n", r);
+ fprintf(stderr, "E: errno=%d '%s'\n", sav_errno,
+ strerror(sav_errno));
+ }
+
+ } while (r == NATPMP_TRYAGAIN);
+
+ if (r != 0) {
+ /* XXX TODO: NATPMP_* should be formatted into useful error strings */
+ fprintf(stderr, "E: NAT-PMP It appears that something went wrong:"
+ " %d\n", r);
+ if (r == -51)
+ fprintf(stderr, "E: NAT-PMP It appears that the request was "
+ "unauthorized\n");
+ return r;
+ }
+
+ if (r == NATPMP_SUCCESS) {
+ fprintf(stdout, "tor-fw-helper: NAT-PMP mapped public port %hu to"
+ " localport %hu liftime %u\n",
+ (state->response).pnu.newportmapping.mappedpublicport,
+ (state->response).pnu.newportmapping.privateport,
+ (state->response).pnu.newportmapping.lifetime);
+ }
+
+ tor_fw_options->nat_pmp_status = 1;
+
+ return r;
+}
+
+/** Fetch our likely public IP from our upstream NAT-PMP enabled NAT device.
+ * Use the connection context stored in <b>backend_state</b>. */
+int
+tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options,
+ void *backend_state)
+{
+ int r = 0;
+ int x = 0;
+ int sav_errno;
+ natpmp_state_t *state = (natpmp_state_t *) backend_state;
+
+ struct timeval timeout;
+
+ r = sendpublicaddressrequest(&(state->natpmp));
+ fprintf(stdout, "tor-fw-helper: NAT-PMP sendpublicaddressrequest returned"
+ " %d (%s)\n", r, r==2?"SUCCESS":"FAILED");
+
+ do {
+ getnatpmprequesttimeout(&(state->natpmp), &timeout);
+
+ x = wait_until_fd_readable(state->natpmp.s, &timeout);
+ if (x == -1)
+ return -1;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: NAT-PMP attempting to read reponse...\n");
+ r = readnatpmpresponseorretry(&(state->natpmp), &(state->response));
+ sav_errno = errno;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: NAT-PMP readnatpmpresponseorretry returned"
+ " %d\n", r);
+
+ if ( r < 0 && r != NATPMP_TRYAGAIN) {
+ fprintf(stderr, "E: NAT-PMP readnatpmpresponseorretry failed %d\n",
+ r);
+ fprintf(stderr, "E: NAT-PMP errno=%d '%s'\n", sav_errno,
+ strerror(sav_errno));
+ }
+
+ } while (r == NATPMP_TRYAGAIN );
+
+ if (r != 0) {
+ fprintf(stderr, "E: NAT-PMP It appears that something went wrong:"
+ " %d\n", r);
+ return r;
+ }
+
+ fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n",
+ inet_ntoa((state->response).pnu.publicaddress.addr));
+ tor_fw_options->public_ip_status = 1;
+
+ if (tor_fw_options->verbose) {
+ fprintf(stdout, "V: result = %u\n", r);
+ fprintf(stdout, "V: type = %u\n", (state->response).type);
+ fprintf(stdout, "V: resultcode = %u\n", (state->response).resultcode);
+ fprintf(stdout, "V: epoch = %u\n", (state->response).epoch);
+ }
+
+ return r;
+}
+#endif
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h
new file mode 100644
index 000000000..0190379a2
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper-natpmp.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper-natpmp.h
+ **/
+
+#ifdef NAT_PMP
+#ifndef _TOR_FW_HELPER_NATPMP_H
+#define _TOR_FW_HELPER_NATPMP_H
+
+#include <natpmp.h>
+
+/** This is the default NAT-PMP lease time in seconds. */
+#define NATPMP_DEFAULT_LEASE 3600
+/** NAT-PMP has many codes for success; this is one of them. */
+#define NATPMP_SUCCESS 0
+
+/** This is our NAT-PMP meta structure - it holds our request data, responses,
+ * various NAT-PMP parameters, and of course the status of the motion in the
+ * NAT-PMP ocean. */
+typedef struct natpmp_state_t {
+ natpmp_t natpmp;
+ natpmpresp_t response;
+ int fetch_public_ip;
+ int status;
+ int init; /**< Have we been initialized? */
+ int protocol; /**< This will only be TCP. */
+ int lease;
+} natpmp_state_t;
+
+const tor_fw_backend_t *tor_fw_get_natpmp_backend(void);
+
+int tor_natpmp_init(tor_fw_options_t *tor_fw_options, void *backend_state);
+
+int tor_natpmp_cleanup(tor_fw_options_t *tor_fw_options, void *backend_state);
+
+int tor_natpmp_add_tcp_mapping(tor_fw_options_t *tor_fw_options,
+ void *backend_state);
+
+int tor_natpmp_fetch_public_ip(tor_fw_options_t *tor_fw_options,
+ void *backend_state);
+
+#endif
+#endif
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper-upnp.c b/src/tools/tor-fw-helper/tor-fw-helper-upnp.c
new file mode 100644
index 000000000..c4b14a84e
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper-upnp.c
@@ -0,0 +1,189 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper-upnp.c
+ * \brief The implementation of our UPnP firewall helper.
+ **/
+
+#include "orconfig.h"
+#ifdef MINIUPNPC
+#ifdef MS_WINDOWS
+#define STATICLIB
+#endif
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <assert.h>
+
+#include "compat.h"
+#include "tor-fw-helper.h"
+#include "tor-fw-helper-upnp.h"
+
+/** UPnP timeout value. */
+#define UPNP_DISCOVER_TIMEOUT 2000
+/** Description of the port mapping in the UPnP table. */
+#define UPNP_DESC "Tor relay"
+
+/* XXX TODO: We should print these as a useful user string when we return the
+ * number to a user */
+/** Magic numbers as miniupnpc return codes. */
+#define UPNP_ERR_SUCCESS 0
+#define UPNP_ERR_NODEVICESFOUND 1
+#define UPNP_ERR_NOIGDFOUND 2
+#define UPNP_ERR_ADDPORTMAPPING 3
+#define UPNP_ERR_GETPORTMAPPING 4
+#define UPNP_ERR_DELPORTMAPPING 5
+#define UPNP_ERR_GETEXTERNALIP 6
+#define UPNP_ERR_INVAL 7
+#define UPNP_ERR_OTHER 8
+#define UPNP_SUCCESS 1
+
+/** This hooks miniupnpc into our multi-backend API. */
+static tor_fw_backend_t tor_miniupnp_backend = {
+ "miniupnp",
+ sizeof(struct miniupnpc_state_t),
+ tor_upnp_init,
+ tor_upnp_cleanup,
+ tor_upnp_fetch_public_ip,
+ tor_upnp_add_tcp_mapping
+};
+
+/** Return the backend for miniupnp. */
+const tor_fw_backend_t *
+tor_fw_get_miniupnp_backend(void)
+{
+ return &tor_miniupnp_backend;
+}
+
+/** Initialize the UPnP backend and store the results in
+ * <b>backend_state</b>.*/
+int
+tor_upnp_init(tor_fw_options_t *options, void *backend_state)
+{
+ /*
+ This leaks the user agent from the client to the router - perhaps we don't
+ want to do that? eg:
+
+ User-Agent: Ubuntu/10.04, UPnP/1.0, MiniUPnPc/1.4
+
+ */
+ miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
+ struct UPNPDev *devlist;
+ int r;
+
+ memset(&(state->urls), 0, sizeof(struct UPNPUrls));
+ memset(&(state->data), 0, sizeof(struct IGDdatas));
+ state->init = 0;
+
+ devlist = upnpDiscover(UPNP_DISCOVER_TIMEOUT, NULL, NULL, 0);
+ if (NULL == devlist) {
+ fprintf(stderr, "E: upnpDiscover returned: NULL\n");
+ return UPNP_ERR_NODEVICESFOUND;
+ }
+
+ assert(options);
+ r = UPNP_GetValidIGD(devlist, &(state->urls), &(state->data),
+ state->lanaddr, UPNP_LANADDR_SZ);
+ fprintf(stdout, "tor-fw-helper: UPnP GetValidIGD returned: %d (%s)\n", r,
+ r==UPNP_SUCCESS?"SUCCESS":"FAILED");
+
+ freeUPNPDevlist(devlist);
+
+ if (r != 1 && r != 2)
+ return UPNP_ERR_NOIGDFOUND;
+
+ state->init = 1;
+ return UPNP_ERR_SUCCESS;
+}
+
+/** Tear down the UPnP connection stored in <b>backend_state</b>.*/
+int
+tor_upnp_cleanup(tor_fw_options_t *options, void *backend_state)
+{
+
+ miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
+ assert(options);
+
+ if (state->init)
+ FreeUPNPUrls(&(state->urls));
+ state->init = 0;
+
+ return UPNP_ERR_SUCCESS;
+}
+
+/** Fetch our likely public IP from our upstream UPnP IGD enabled NAT device.
+ * Use the connection context stored in <b>backend_state</b>. */
+int
+tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state)
+{
+ miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
+ int r;
+ char externalIPAddress[16];
+
+ if (!state->init) {
+ r = tor_upnp_init(options, state);
+ if (r != UPNP_ERR_SUCCESS)
+ return r;
+ }
+
+ r = UPNP_GetExternalIPAddress(state->urls.controlURL,
+ state->data.first.servicetype,
+ externalIPAddress);
+
+ if (r != UPNPCOMMAND_SUCCESS)
+ goto err;
+
+ if (externalIPAddress[0]) {
+ fprintf(stdout, "tor-fw-helper: ExternalIPAddress = %s\n",
+ externalIPAddress); tor_upnp_cleanup(options, state);
+ options->public_ip_status = 1;
+ return UPNP_ERR_SUCCESS;
+ } else {
+ goto err;
+ }
+
+ err:
+ tor_upnp_cleanup(options, state);
+ return UPNP_ERR_GETEXTERNALIP;
+}
+
+/** Add a TCP port mapping for a single port stored in <b>tor_fw_options</b>
+ * and store the results in <b>backend_state</b>. */
+int
+tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state)
+{
+ miniupnpc_state_t *state = (miniupnpc_state_t *) backend_state;
+ int r;
+ char internal_port_str[6];
+ char external_port_str[6];
+
+ if (!state->init) {
+ r = tor_upnp_init(options, state);
+ if (r != UPNP_ERR_SUCCESS)
+ return r;
+ }
+
+ if (options->verbose)
+ fprintf(stdout, "V: internal port: %d, external port: %d\n",
+ (int)options->internal_port, (int)options->external_port);
+
+ tor_snprintf(internal_port_str, sizeof(internal_port_str),
+ "%d", (int)options->internal_port);
+ tor_snprintf(external_port_str, sizeof(external_port_str),
+ "%d", (int)options->external_port);
+
+ r = UPNP_AddPortMapping(state->urls.controlURL,
+ state->data.first.servicetype,
+ external_port_str, internal_port_str,
+ state->lanaddr, UPNP_DESC, "TCP", 0);
+ if (r != UPNPCOMMAND_SUCCESS)
+ return UPNP_ERR_ADDPORTMAPPING;
+
+ options->upnp_status = 1;
+ return UPNP_ERR_SUCCESS;
+}
+#endif
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper-upnp.h b/src/tools/tor-fw-helper/tor-fw-helper-upnp.h
new file mode 100644
index 000000000..021a8e0aa
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper-upnp.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper-upnp.h
+ * \brief The main header for our firewall helper.
+ **/
+
+#ifdef MINIUPNPC
+#ifndef _TOR_FW_HELPER_UPNP_H
+#define _TOR_FW_HELPER_UPNP_H
+
+#include <miniupnpc/miniwget.h>
+#include <miniupnpc/miniupnpc.h>
+#include <miniupnpc/upnpcommands.h>
+#include <miniupnpc/upnperrors.h>
+
+/** This is a magic number for miniupnpc lan address size. */
+#define UPNP_LANADDR_SZ 64
+
+/** This is our miniupnpc meta structure - it holds our request data,
+ * responses, and various miniupnpc parameters. */
+typedef struct miniupnpc_state_t {
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ char lanaddr[UPNP_LANADDR_SZ];
+ int init;
+} miniupnpc_state_t;
+
+const tor_fw_backend_t *tor_fw_get_miniupnp_backend(void);
+
+int tor_upnp_init(tor_fw_options_t *options, void *backend_state);
+
+int tor_upnp_cleanup(tor_fw_options_t *options, void *backend_state);
+
+int tor_upnp_fetch_public_ip(tor_fw_options_t *options, void *backend_state);
+
+int tor_upnp_add_tcp_mapping(tor_fw_options_t *options, void *backend_state);
+
+#endif
+#endif
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper.c b/src/tools/tor-fw-helper/tor-fw-helper.c
new file mode 100644
index 000000000..002239745
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper.c
@@ -0,0 +1,396 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper.c
+ * \brief The main wrapper around our firewall helper logic.
+ **/
+
+/*
+ * tor-fw-helper is a tool for opening firewalls with NAT-PMP and UPnP; this
+ * tool is designed to be called by hand or by Tor by way of a exec() at a
+ * later date.
+ */
+
+#include "orconfig.h"
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <time.h>
+#include <string.h>
+
+#ifdef MS_WINDOWS
+#include <winsock2.h>
+#endif
+
+#include "tor-fw-helper.h"
+#ifdef NAT_PMP
+#include "tor-fw-helper-natpmp.h"
+#endif
+#ifdef MINIUPNPC
+#include "tor-fw-helper-upnp.h"
+#endif
+
+/** This is our meta storage type - it holds information about each helper
+ including the total number of helper backends, function pointers, and helper
+ state. */
+typedef struct backends_t {
+ /** The total number of backends */
+ int n_backends;
+ /** The backend functions as an array */
+ tor_fw_backend_t backend_ops[MAX_BACKENDS];
+ /** The internal backend state */
+ void *backend_state[MAX_BACKENDS];
+} backends_t;
+
+/** Initalize each backend helper with the user input stored in <b>options</b>
+ * and put the results in the <b>backends</b> struct. */
+static int
+init_backends(tor_fw_options_t *options, backends_t *backends)
+{
+ int n_available = 0;
+ int i, r, n;
+ tor_fw_backend_t *backend_ops_list[MAX_BACKENDS];
+ void *data = NULL;
+ /* First, build a list of the working backends. */
+ n = 0;
+#ifdef MINIUPNPC
+ backend_ops_list[n++] = (tor_fw_backend_t *) tor_fw_get_miniupnp_backend();
+#endif
+#ifdef NAT_PMP
+ backend_ops_list[n++] = (tor_fw_backend_t *) tor_fw_get_natpmp_backend();
+#endif
+ n_available = n;
+
+ /* Now, for each backend that might work, try to initialize it.
+ * That's how we roll, initialized.
+ */
+ n = 0;
+ for (i=0; i<n_available; ++i) {
+ data = calloc(1, backend_ops_list[i]->state_len);
+ if (!data) {
+ perror("calloc");
+ exit(1);
+ }
+ r = backend_ops_list[i]->init(options, data);
+ if (r == 0) {
+ backends->backend_ops[n] = *backend_ops_list[i];
+ backends->backend_state[n] = data;
+ n++;
+ } else {
+ free(data);
+ }
+ }
+ backends->n_backends = n;
+
+ return n;
+}
+
+/** Return the proper commandline switches when the user needs information. */
+static void
+usage(void)
+{
+ fprintf(stderr, "tor-fw-helper usage:\n"
+ " [-h|--help]\n"
+ " [-T|--Test]\n"
+ " [-v|--verbose]\n"
+ " [-g|--fetch-public-ip]\n"
+ " -i|--internal-or-port [TCP port]\n"
+ " [-e|--external-or-port [TCP port]]\n"
+ " [-d|--internal-dir-port [TCP port]\n"
+ " [-p|--external-dir-port [TCP port]]]\n");
+}
+
+/** Log commandline options to a hardcoded file <b>tor-fw-helper.log</b> in the
+ * current working directory. */
+static int
+log_commandline_options(int argc, char **argv)
+{
+ int i, retval;
+ FILE *logfile;
+ time_t now;
+
+ /* Open the log file */
+ logfile = fopen("tor-fw-helper.log", "a");
+ if (NULL == logfile)
+ return -1;
+
+ /* Send all commandline arguments to the file */
+ now = time(NULL);
+ retval = fprintf(logfile, "START: %s\n", ctime(&now));
+ for (i = 0; i < argc; i++) {
+ retval = fprintf(logfile, "ARG: %d: %s\n", i, argv[i]);
+ if (retval < 0)
+ goto error;
+
+ retval = fprintf(stdout, "ARG: %d: %s\n", i, argv[i]);
+ if (retval < 0)
+ goto error;
+ }
+ now = time(NULL);
+ retval = fprintf(logfile, "END: %s\n", ctime(&now));
+
+ /* Close and clean up */
+ retval = fclose(logfile);
+ return retval;
+
+ /* If there was an error during writing */
+ error:
+ fclose(logfile);
+ return -1;
+}
+
+/** Iterate over over each of the supported <b>backends</b> and attempt to
+ * fetch the public ip. */
+static void
+tor_fw_fetch_public_ip(tor_fw_options_t *tor_fw_options,
+ backends_t *backends)
+{
+ int i;
+ int r = 0;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: tor_fw_fetch_public_ip\n");
+
+ for (i=0; i<backends->n_backends; ++i) {
+ if (tor_fw_options->verbose) {
+ fprintf(stdout, "V: running backend_state now: %i\n", i);
+ fprintf(stdout, "V: size of backend state: %u\n",
+ (int)(backends->backend_ops)[i].state_len);
+ fprintf(stdout, "V: backend state name: %s\n",
+ (char *)(backends->backend_ops)[i].name);
+ }
+ r = backends->backend_ops[i].fetch_public_ip(tor_fw_options,
+ backends->backend_state[i]);
+ fprintf(stdout, "tor-fw-helper: tor_fw_fetch_public_ip backend %s "
+ " returned: %i\n", (char *)(backends->backend_ops)[i].name, r);
+ }
+}
+
+/** Iterate over each of the supported <b>backends</b> and attempt to add a
+ * port forward for the OR port stored in <b>tor_fw_options</b>. */
+static void
+tor_fw_add_or_port(tor_fw_options_t *tor_fw_options,
+ backends_t *backends)
+{
+ int i;
+ int r = 0;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: tor_fw_add_or_port\n");
+
+ for (i=0; i<backends->n_backends; ++i) {
+ if (tor_fw_options->verbose) {
+ fprintf(stdout, "V: running backend_state now: %i\n", i);
+ fprintf(stdout, "V: size of backend state: %u\n",
+ (int)(backends->backend_ops)[i].state_len);
+ fprintf(stdout, "V: backend state name: %s\n",
+ (const char *) backends->backend_ops[i].name);
+ }
+ r = backends->backend_ops[i].add_tcp_mapping(tor_fw_options,
+ backends->backend_state[i]);
+ fprintf(stdout, "tor-fw-helper: tor_fw_add_or_port backend %s "
+ "returned: %i\n", (const char *) backends->backend_ops[i].name, r);
+ }
+}
+
+/** Iterate over each of the supported <b>backends</b> and attempt to add a
+ * port forward for the Dir port stored in <b>tor_fw_options</b>. */
+static void
+tor_fw_add_dir_port(tor_fw_options_t *tor_fw_options,
+ backends_t *backends)
+{
+ int i;
+ int r = 0;
+
+ if (tor_fw_options->verbose)
+ fprintf(stdout, "V: tor_fw_add_dir_port\n");
+
+ for (i=0; i<backends->n_backends; ++i) {
+ if (tor_fw_options->verbose) {
+ fprintf(stdout, "V: running backend_state now: %i\n", i);
+ fprintf(stdout, "V: size of backend state: %u\n",
+ (int)(backends->backend_ops)[i].state_len);
+ fprintf(stdout, "V: backend state name: %s\n",
+ (char *)(backends->backend_ops)[i].name);
+ }
+ r = backends->backend_ops[i].add_tcp_mapping(tor_fw_options,
+ backends->backend_state[i]);
+ fprintf(stdout, "tor-fw-helper: tor_fw_add_dir_port backend %s "
+ "returned: %i\n", (const char *)backends->backend_ops[i].name, r);
+ }
+}
+
+/** Called before we make any calls to network-related functions.
+ * (Some operating systems require their network libraries to be
+ * initialized.) (from common/compat.c) */
+static int
+network_init(void)
+{
+#ifdef MS_WINDOWS
+ /* This silly exercise is necessary before windows will allow
+ * gethostbyname to work. */
+ WSADATA WSAData;
+ int r;
+ r = WSAStartup(0x101, &WSAData);
+ if (r) {
+ fprintf(stderr, "E: Error initializing Windows network layer "
+ "- code was %d", r);
+ return -1;
+ }
+ /* WSAData.iMaxSockets might show the max sockets we're allowed to use.
+ * We might use it to complain if we're trying to be a server but have
+ * too few sockets available. */
+#endif
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int r = 0;
+ int c = 0;
+
+ tor_fw_options_t tor_fw_options;
+ backends_t backend_state;
+
+ memset(&tor_fw_options, 0, sizeof(tor_fw_options));
+ memset(&backend_state, 0, sizeof(backend_state));
+
+ while (1) {
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"verbose", 0, 0, 'v'},
+ {"help", 0, 0, 'h'},
+ {"internal-or-port", 1, 0, 'i'},
+ {"external-or-port", 1, 0, 'e'},
+ {"internal-dir-port", 1, 0, 'd'},
+ {"external-dir-port", 1, 0, 'p'},
+ {"fetch-public-ip", 0, 0, 'g'},
+ {"test-commandline", 0, 0, 'T'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "vhi:e:d:p:gT",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'v': tor_fw_options.verbose = 1; break;
+ case 'h': tor_fw_options.help = 1; usage(); exit(1); break;
+ case 'i': sscanf(optarg, "%hu", &tor_fw_options.private_or_port);
+ break;
+ case 'e': sscanf(optarg, "%hu", &tor_fw_options.public_or_port);
+ break;
+ case 'd': sscanf(optarg, "%hu", &tor_fw_options.private_dir_port);
+ break;
+ case 'p': sscanf(optarg, "%hu", &tor_fw_options.public_dir_port);
+ break;
+ case 'g': tor_fw_options.fetch_public_ip = 1; break;
+ case 'T': tor_fw_options.test_commandline = 1; break;
+ case '?': break;
+ default : fprintf(stderr, "Unknown option!\n"); usage(); exit(1);
+ }
+ }
+
+ if (tor_fw_options.verbose) {
+ fprintf(stderr, "V: tor-fw-helper version %s\n"
+ "V: We were called with the following arguments:\n"
+ "V: verbose = %d, help = %d, pub or port = %u, "
+ "priv or port = %u\n"
+ "V: pub dir port = %u, priv dir port = %u\n"
+ "V: fetch_public_ip = %u\n",
+ tor_fw_version, tor_fw_options.verbose, tor_fw_options.help,
+ tor_fw_options.private_or_port, tor_fw_options.public_or_port,
+ tor_fw_options.private_dir_port, tor_fw_options.public_dir_port,
+ tor_fw_options.fetch_public_ip);
+ }
+
+ if (tor_fw_options.test_commandline) {
+ return log_commandline_options(argc, argv);
+ }
+
+ /* At the very least, we require an ORPort;
+ Given a private ORPort, we can ask for a mapping that matches the port
+ externally.
+ */
+ if (!tor_fw_options.private_or_port && !tor_fw_options.fetch_public_ip) {
+ fprintf(stderr, "E: We require an ORPort or fetch_public_ip"
+ " request!\n");
+ usage();
+ exit(1);
+ } else {
+ /* When we only have one ORPort, internal/external are
+ set to be the same.*/
+ if (!tor_fw_options.public_or_port && tor_fw_options.private_or_port) {
+ if (tor_fw_options.verbose)
+ fprintf(stdout, "V: We're setting public_or_port = "
+ "private_or_port.\n");
+ tor_fw_options.public_or_port = tor_fw_options.private_or_port;
+ }
+ }
+ if (!tor_fw_options.private_dir_port) {
+ if (tor_fw_options.verbose)
+ fprintf(stdout, "V: We have no DirPort; no hole punching for "
+ "DirPorts\n");
+
+ } else {
+ /* When we only have one DirPort, internal/external are
+ set to be the same.*/
+ if (!tor_fw_options.public_dir_port && tor_fw_options.private_dir_port) {
+ if (tor_fw_options.verbose)
+ fprintf(stdout, "V: We're setting public_or_port = "
+ "private_or_port.\n");
+
+ tor_fw_options.public_dir_port = tor_fw_options.private_dir_port;
+ }
+ }
+
+ if (tor_fw_options.verbose) {
+ fprintf(stdout, "V: pub or port = %u, priv or port = %u\n"
+ "V: pub dir port = %u, priv dir port = %u\n",
+ tor_fw_options.private_or_port, tor_fw_options.public_or_port,
+ tor_fw_options.private_dir_port,
+ tor_fw_options.public_dir_port);
+ }
+
+ // Initialize networking
+ if (network_init())
+ exit(1);
+
+ // Initalize the various fw-helper backend helpers
+ r = init_backends(&tor_fw_options, &backend_state);
+ if (r)
+ printf("tor-fw-helper: %i NAT traversal helper(s) loaded\n", r);
+
+ if (tor_fw_options.fetch_public_ip) {
+ tor_fw_fetch_public_ip(&tor_fw_options, &backend_state);
+ }
+
+ if (tor_fw_options.private_or_port) {
+ tor_fw_options.internal_port = tor_fw_options.private_or_port;
+ tor_fw_options.external_port = tor_fw_options.private_or_port;
+ tor_fw_add_or_port(&tor_fw_options, &backend_state);
+ }
+
+ if (tor_fw_options.private_dir_port) {
+ tor_fw_options.internal_port = tor_fw_options.private_dir_port;
+ tor_fw_options.external_port = tor_fw_options.private_dir_port;
+ tor_fw_add_dir_port(&tor_fw_options, &backend_state);
+ }
+
+ r = (((tor_fw_options.nat_pmp_status | tor_fw_options.upnp_status)
+ |tor_fw_options.public_ip_status));
+ if (r > 0) {
+ fprintf(stdout, "tor-fw-helper: SUCCESS\n");
+ } else {
+ fprintf(stderr, "tor-fw-helper: FAILURE\n");
+ }
+
+ exit(r);
+}
+
diff --git a/src/tools/tor-fw-helper/tor-fw-helper.h b/src/tools/tor-fw-helper/tor-fw-helper.h
new file mode 100644
index 000000000..39d852d21
--- /dev/null
+++ b/src/tools/tor-fw-helper/tor-fw-helper.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2010, Jacob Appelbaum, Steven J. Murdoch.
+ * Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file tor-fw-helper.h
+ * \brief The main header for our firewall helper.
+ **/
+
+#ifndef _TOR_FW_HELPER_H
+#define _TOR_FW_HELPER_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <time.h>
+
+/** The current version of tor-fw-helper. */
+#define tor_fw_version "0.1"
+
+/** This is an arbitrary hard limit - We currently have two (NAT-PMP and UPnP).
+ We're likely going to add the Intel UPnP library but nothing else comes to
+ mind at the moment. */
+#define MAX_BACKENDS 23
+
+/** This is where we store parsed commandline options. */
+typedef struct {
+ int verbose;
+ int help;
+ int test_commandline;
+ uint16_t private_dir_port;
+ uint16_t private_or_port;
+ uint16_t public_dir_port;
+ uint16_t public_or_port;
+ uint16_t internal_port;
+ uint16_t external_port;
+ int fetch_public_ip;
+ int nat_pmp_status;
+ int upnp_status;
+ int public_ip_status;
+} tor_fw_options_t;
+
+/** This is our main structure that defines our backend helper API; each helper
+ * must conform to these public methods if it expects to be handled in a
+ * non-special way. */
+typedef struct tor_fw_backend_t {
+ const char *name;
+ size_t state_len;
+ int (*init)(tor_fw_options_t *options, void *backend_state);
+ int (*cleanup)(tor_fw_options_t *options, void *backend_state);
+ int (*fetch_public_ip)(tor_fw_options_t *options, void *backend_state);
+ int (*add_tcp_mapping)(tor_fw_options_t *options, void *backend_state);
+} tor_fw_backend_t;
+
+#endif
+
diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c
index 7a516b457..974a58bec 100644
--- a/src/tools/tor-gencert.c
+++ b/src/tools/tor-gencert.c
@@ -169,7 +169,7 @@ parse_commandline(int argc, char **argv)
fprintf(stderr, "No argument to -a\n");
return 1;
}
- if (parse_addr_port(LOG_ERR, argv[++i], NULL, &addr, &port)<0)
+ if (addr_port_lookup(LOG_ERR, argv[++i], NULL, &addr, &port)<0)
return 1;
in.s_addr = htonl(addr);
tor_inet_ntoa(&in, b, sizeof(b));
diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c
index 8c4d3f648..f1220d9d8 100644
--- a/src/tools/tor-resolve.c
+++ b/src/tools/tor-resolve.c
@@ -393,7 +393,7 @@ main(int argc, char **argv)
socksport = 9050; /* 9050 */
}
} else if (n_args == 2) {
- if (parse_addr_port(LOG_WARN, arg[1], NULL, &sockshost, &socksport)<0) {
+ if (addr_port_lookup(LOG_WARN, arg[1], NULL, &sockshost, &socksport)<0) {
fprintf(stderr, "Couldn't parse/resolve address %s", arg[1]);
return 1;
}
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 238b3f7ac..df5f2b46f 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -122,6 +122,7 @@
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
+
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H
@@ -233,5 +234,13 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.2.2.34-dev"
+#define VERSION "0.2.3.6-alpha"
+
+
+#define HAVE_STRUCT_SOCKADDR_IN6
+#define HAVE_STRUCT_IN6_ADDR
+#define RSHIFT_DOES_SIGN_EXTEND
+#define FLEXIBLE_ARRAY_MEMBER 0
+#define HAVE_EVENT2_EVENT_H
+#define SHARE_DATADIR ""