aboutsummaryrefslogtreecommitdiff
path: root/src/core/or/connection_edge.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/or/connection_edge.c')
-rw-r--r--src/core/or/connection_edge.c286
1 files changed, 204 insertions, 82 deletions
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index 84b80313ce..1394a41c73 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2019, The Tor Project, Inc. */
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -62,21 +62,24 @@
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/mainloop/mainloop.h"
+#include "core/mainloop/netstatus.h"
#include "core/or/channel.h"
#include "core/or/circuitbuild.h"
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
+#include "core/or/circuitpadding.h"
#include "core/or/connection_edge.h"
#include "core/or/connection_or.h"
#include "core/or/policies.h"
#include "core/or/reasons.h"
#include "core/or/relay.h"
+#include "core/or/sendme.h"
#include "core/proto/proto_http.h"
#include "core/proto/proto_socks.h"
#include "feature/client/addressmap.h"
#include "feature/client/circpathbias.h"
#include "feature/client/dnsserv.h"
-#include "feature/control/control.h"
+#include "feature/control/control_events.h"
#include "feature/dircache/dirserv.h"
#include "feature/dircommon/directory.h"
#include "feature/hibernate/hibernate.h"
@@ -97,7 +100,7 @@
#include "feature/rend/rendservice.h"
#include "feature/stats/predict_ports.h"
#include "feature/stats/rephist.h"
-#include "lib/container/buffers.h"
+#include "lib/buf/buffers.h"
#include "lib/crypt_ops/crypto_util.h"
#include "core/or/cell_st.h"
@@ -300,6 +303,11 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
}
return 0;
case AP_CONN_STATE_OPEN:
+ if (! conn->base_.linked) {
+ note_user_activity(approx_time());
+ }
+
+ FALLTHROUGH;
case EXIT_CONN_STATE_OPEN:
if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) {
/* (We already sent an end cell if possible) */
@@ -424,6 +432,21 @@ warn_if_hs_unreachable(const edge_connection_t *conn, uint8_t reason)
}
}
+/** Given a TTL (in seconds) from a DNS response or from a relay, determine
+ * what TTL clients and relays should actually use for caching it. */
+uint32_t
+clip_dns_ttl(uint32_t ttl)
+{
+ /* This logic is a defense against "DefectTor" DNS-based traffic
+ * confirmation attacks, as in https://nymity.ch/tor-dns/tor-dns.pdf .
+ * We only give two values: a "low" value and a "high" value.
+ */
+ if (ttl < MIN_DNS_TTL)
+ return MIN_DNS_TTL;
+ else
+ return MAX_DNS_TTL;
+}
+
/** Send a relay end cell from stream <b>conn</b> down conn's circuit, and
* remember that we've done so. If this is not a client connection, set the
* relay end cell's reason for closing as <b>reason</b>.
@@ -472,7 +495,7 @@ connection_edge_end(edge_connection_t *conn, uint8_t reason)
memcpy(payload+1, tor_addr_to_in6_addr8(&conn->base_.addr), 16);
addrlen = 16;
}
- set_uint32(payload+1+addrlen, htonl(dns_clip_ttl(conn->address_ttl)));
+ set_uint32(payload+1+addrlen, htonl(clip_dns_ttl(conn->address_ttl)));
payload_len += 4+addrlen;
}
@@ -754,8 +777,13 @@ connection_edge_flushed_some(edge_connection_t *conn)
{
switch (conn->base_.state) {
case AP_CONN_STATE_OPEN:
+ if (! conn->base_.linked) {
+ note_user_activity(approx_time());
+ }
+
+ FALLTHROUGH;
case EXIT_CONN_STATE_OPEN:
- connection_edge_consider_sending_sendme(conn);
+ sendme_connection_edge_consider_sending(conn);
break;
}
return 0;
@@ -779,7 +807,7 @@ connection_edge_finished_flushing(edge_connection_t *conn)
switch (conn->base_.state) {
case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN:
- connection_edge_consider_sending_sendme(conn);
+ sendme_connection_edge_consider_sending(conn);
return 0;
case AP_CONN_STATE_SOCKS_WAIT:
case AP_CONN_STATE_NATD_WAIT:
@@ -832,7 +860,7 @@ connected_cell_format_payload(uint8_t *payload_out,
return -1;
}
- set_uint32(payload_out + connected_payload_len, htonl(dns_clip_ttl(ttl)));
+ set_uint32(payload_out + connected_payload_len, htonl(clip_dns_ttl(ttl)));
connected_payload_len += 4;
tor_assert(connected_payload_len <= MAX_CONNECTED_CELL_PAYLOAD_LEN);
@@ -1211,7 +1239,7 @@ connection_ap_rescan_and_attach_pending(void)
entry_conn->marked_pending_circ_line = 0; \
entry_conn->marked_pending_circ_file = 0; \
} while (0)
-#else /* !(defined(DEBUGGING_17659)) */
+#else /* !defined(DEBUGGING_17659) */
#define UNMARK() do { } while (0)
#endif /* defined(DEBUGGING_17659) */
@@ -1540,6 +1568,107 @@ consider_plaintext_ports(entry_connection_t *conn, uint16_t port)
return 0;
}
+/** Parse the given hostname in address. Returns true if the parsing was
+ * successful and type_out contains the type of the hostname. Else, false is
+ * returned which means it was not recognized and type_out is set to
+ * BAD_HOSTNAME.
+ *
+ * The possible recognized forms are (where true is returned):
+ *
+ * If address is of the form "y.onion" with a well-formed handle y:
+ * Put a NUL after y, lower-case it, and return ONION_V2_HOSTNAME or
+ * ONION_V3_HOSTNAME depending on the HS version.
+ *
+ * If address is of the form "x.y.onion" with a well-formed handle x:
+ * Drop "x.", put a NUL after y, lower-case it, and return
+ * ONION_V2_HOSTNAME or ONION_V3_HOSTNAME depending on the HS version.
+ *
+ * If address is of the form "y.onion" with a badly-formed handle y:
+ * Return BAD_HOSTNAME and log a message.
+ *
+ * If address is of the form "y.exit":
+ * Put a NUL after y and return EXIT_HOSTNAME.
+ *
+ * Otherwise:
+ * Return NORMAL_HOSTNAME and change nothing.
+ */
+STATIC bool
+parse_extended_hostname(char *address, hostname_type_t *type_out)
+{
+ char *s;
+ char *q;
+ char query[HS_SERVICE_ADDR_LEN_BASE32+1];
+
+ s = strrchr(address,'.');
+ if (!s) {
+ *type_out = NORMAL_HOSTNAME; /* no dot, thus normal */
+ goto success;
+ }
+ if (!strcmp(s+1,"exit")) {
+ *s = 0; /* NUL-terminate it */
+ *type_out = EXIT_HOSTNAME; /* .exit */
+ goto success;
+ }
+ if (strcmp(s+1,"onion")) {
+ *type_out = NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */
+ goto success;
+ }
+
+ /* so it is .onion */
+ *s = 0; /* NUL-terminate it */
+ /* locate a 'sub-domain' component, in order to remove it */
+ q = strrchr(address, '.');
+ if (q == address) {
+ *type_out = BAD_HOSTNAME;
+ goto failed; /* reject sub-domain, as DNS does */
+ }
+ q = (NULL == q) ? address : q + 1;
+ if (strlcpy(query, q, HS_SERVICE_ADDR_LEN_BASE32+1) >=
+ HS_SERVICE_ADDR_LEN_BASE32+1) {
+ *type_out = BAD_HOSTNAME;
+ goto failed;
+ }
+ if (q != address) {
+ memmove(address, q, strlen(q) + 1 /* also get \0 */);
+ }
+ /* v2 onion address check. */
+ if (strlen(query) == REND_SERVICE_ID_LEN_BASE32) {
+ *type_out = ONION_V2_HOSTNAME;
+ if (rend_valid_v2_service_id(query)) {
+ goto success;
+ }
+ goto failed;
+ }
+
+ /* v3 onion address check. */
+ if (strlen(query) == HS_SERVICE_ADDR_LEN_BASE32) {
+ *type_out = ONION_V3_HOSTNAME;
+ if (hs_address_is_valid(query)) {
+ goto success;
+ }
+ goto failed;
+ }
+
+ /* Reaching this point, nothing was recognized. */
+ *type_out = BAD_HOSTNAME;
+ goto failed;
+
+ success:
+ return true;
+ failed:
+ /* otherwise, return to previous state and return 0 */
+ *s = '.';
+ const bool is_onion = (*type_out == ONION_V2_HOSTNAME) ||
+ (*type_out == ONION_V3_HOSTNAME);
+ log_warn(LD_APP, "Invalid %shostname %s; rejecting",
+ is_onion ? "onion " : "",
+ safe_str_client(address));
+ if (*type_out == ONION_V3_HOSTNAME) {
+ *type_out = BAD_HOSTNAME;
+ }
+ return false;
+}
+
/** How many times do we try connecting with an exit configured via
* TrackHostExits before concluding that it won't work any more and trying a
* different one? */
@@ -2007,16 +2136,15 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
const int automap = rr.automap;
const addressmap_entry_source_t exit_source = rr.exit_source;
- /* Now, we parse the address to see if it's an .onion or .exit or
- * other special address.
- */
- const hostname_type_t addresstype = parse_extended_hostname(socks->address);
-
/* Now see whether the hostname is bogus. This could happen because of an
* onion hostname whose format we don't recognize. */
- if (addresstype == BAD_HOSTNAME) {
+ hostname_type_t addresstype;
+ if (!parse_extended_hostname(socks->address, &addresstype)) {
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
escaped(socks->address));
+ if (addresstype == BAD_HOSTNAME) {
+ conn->socks_request->socks_extended_error_code = SOCKS5_HS_BAD_ADDRESS;
+ }
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
}
@@ -2803,6 +2931,31 @@ connection_ap_process_natd(entry_connection_t *conn)
return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
}
+static const char HTTP_CONNECT_IS_NOT_AN_HTTP_PROXY_MSG[] =
+ "HTTP/1.0 405 Method Not Allowed\r\n"
+ "Content-Type: text/html; charset=iso-8859-1\r\n\r\n"
+ "<html>\n"
+ "<head>\n"
+ "<title>This is an HTTP CONNECT tunnel, not a full HTTP Proxy</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<h1>This is an HTTP CONNECT tunnel, not an HTTP proxy.</h1>\n"
+ "<p>\n"
+ "It appears you have configured your web browser to use this Tor port as\n"
+ "an HTTP proxy.\n"
+ "</p><p>\n"
+ "This is not correct: This port is configured as a CONNECT tunnel, not\n"
+ "an HTTP proxy. Please configure your client accordingly. You can also\n"
+ "use HTTPS; then the client should automatically use HTTP CONNECT."
+ "</p>\n"
+ "<p>\n"
+ "See <a href=\"https://www.torproject.org/documentation.html\">"
+ "https://www.torproject.org/documentation.html</a> for more "
+ "information.\n"
+ "</p>\n"
+ "</body>\n"
+ "</html>\n";
+
/** Called on an HTTP CONNECT entry connection when some bytes have arrived,
* but we have not yet received a full HTTP CONNECT request. Try to parse an
* HTTP CONNECT request from the connection's inbuf. On success, set up the
@@ -2843,7 +2996,7 @@ connection_ap_process_http_connect(entry_connection_t *conn)
tor_assert(command);
tor_assert(addrport);
if (strcasecmp(command, "connect")) {
- errmsg = "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
+ errmsg = HTTP_CONNECT_IS_NOT_AN_HTTP_PROXY_MSG;
goto err;
}
@@ -3309,8 +3462,9 @@ tell_controller_about_resolved_result(entry_connection_t *conn,
expires = time(NULL) + ttl;
if (answer_type == RESOLVED_TYPE_IPV4 && answer_len >= 4) {
char *cp = tor_dup_ip(ntohl(get_uint32(answer)));
- control_event_address_mapped(conn->socks_request->address,
- cp, expires, NULL, 0);
+ if (cp)
+ control_event_address_mapped(conn->socks_request->address,
+ cp, expires, NULL, 0);
tor_free(cp);
} else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
char *cp = tor_strndup(answer, answer_len);
@@ -3383,7 +3537,7 @@ connection_ap_handshake_socks_resolved,(entry_connection_t *conn,
}
} else if (answer_type == RESOLVED_TYPE_IPV6 && answer_len == 16) {
tor_addr_t a;
- tor_addr_from_ipv6_bytes(&a, (char*)answer);
+ tor_addr_from_ipv6_bytes(&a, answer);
if (! tor_addr_is_null(&a)) {
client_dns_set_addressmap(conn,
conn->socks_request->address, &a,
@@ -3484,11 +3638,17 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
size_t replylen, int endreason)
{
char buf[256];
- socks5_reply_status_t status =
- stream_end_reason_to_socks5_response(endreason);
+ socks5_reply_status_t status;
tor_assert(conn->socks_request); /* make sure it's an AP stream */
+ if (conn->socks_request->socks_use_extended_errors &&
+ conn->socks_request->socks_extended_error_code != 0) {
+ status = conn->socks_request->socks_extended_error_code;
+ } else {
+ status = stream_end_reason_to_socks5_response(endreason);
+ }
+
if (!SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
control_event_stream_status(conn, status==SOCKS5_SUCCEEDED ?
STREAM_EVENT_SUCCEEDED : STREAM_EVENT_FAILED,
@@ -3706,6 +3866,10 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn)
/* Link the circuit and the connection crypt path. */
conn->cpath_layer = origin_circ->cpath->prev;
+ /* If this is the first stream on this circuit, tell circpad */
+ if (!origin_circ->p_streams)
+ circpad_machine_event_circ_has_streams(origin_circ);
+
/* Add it into the linked list of p_streams on this circuit */
conn->next_stream = origin_circ->p_streams;
origin_circ->p_streams = conn;
@@ -3796,6 +3960,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
if (! bcell.is_begindir) {
/* Steal reference */
+ tor_assert(bcell.address);
address = bcell.address;
port = bcell.port;
@@ -4300,68 +4465,6 @@ connection_ap_can_use_exit(const entry_connection_t *conn,
return 1;
}
-/** If address is of the form "y.onion" with a well-formed handle y:
- * Put a NUL after y, lower-case it, and return ONION_V2_HOSTNAME or
- * ONION_V3_HOSTNAME depending on the HS version.
- *
- * If address is of the form "x.y.onion" with a well-formed handle x:
- * Drop "x.", put a NUL after y, lower-case it, and return
- * ONION_V2_HOSTNAME or ONION_V3_HOSTNAME depending on the HS version.
- *
- * If address is of the form "y.onion" with a badly-formed handle y:
- * Return BAD_HOSTNAME and log a message.
- *
- * If address is of the form "y.exit":
- * Put a NUL after y and return EXIT_HOSTNAME.
- *
- * Otherwise:
- * Return NORMAL_HOSTNAME and change nothing.
- */
-hostname_type_t
-parse_extended_hostname(char *address)
-{
- char *s;
- char *q;
- char query[HS_SERVICE_ADDR_LEN_BASE32+1];
-
- s = strrchr(address,'.');
- if (!s)
- return NORMAL_HOSTNAME; /* no dot, thus normal */
- if (!strcmp(s+1,"exit")) {
- *s = 0; /* NUL-terminate it */
- return EXIT_HOSTNAME; /* .exit */
- }
- if (strcmp(s+1,"onion"))
- return NORMAL_HOSTNAME; /* neither .exit nor .onion, thus normal */
-
- /* so it is .onion */
- *s = 0; /* NUL-terminate it */
- /* locate a 'sub-domain' component, in order to remove it */
- q = strrchr(address, '.');
- if (q == address) {
- goto failed; /* reject sub-domain, as DNS does */
- }
- q = (NULL == q) ? address : q + 1;
- if (strlcpy(query, q, HS_SERVICE_ADDR_LEN_BASE32+1) >=
- HS_SERVICE_ADDR_LEN_BASE32+1)
- goto failed;
- if (q != address) {
- memmove(address, q, strlen(q) + 1 /* also get \0 */);
- }
- if (rend_valid_v2_service_id(query)) {
- return ONION_V2_HOSTNAME; /* success */
- }
- if (hs_address_is_valid(query)) {
- return ONION_V3_HOSTNAME;
- }
- failed:
- /* otherwise, return to previous state and return 0 */
- *s = '.';
- log_warn(LD_APP, "Invalid onion hostname %s; rejecting",
- safe_str_client(address));
- 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>. */
@@ -4565,6 +4668,25 @@ circuit_clear_isolation(origin_circuit_t *circ)
circ->socks_username_len = circ->socks_password_len = 0;
}
+/** Send an END and mark for close the given edge connection conn using the
+ * given reason that has to be a stream reason.
+ *
+ * Note: We don't unattached the AP connection (if applicable) because we
+ * don't want to flush the remaining data. This function aims at ending
+ * everything quickly regardless of the connection state.
+ *
+ * This function can't fail and does nothing if conn is NULL. */
+void
+connection_edge_end_close(edge_connection_t *conn, uint8_t reason)
+{
+ if (!conn) {
+ return;
+ }
+
+ connection_edge_end(conn, reason);
+ connection_mark_for_close(TO_CONN(conn));
+}
+
/** Free all storage held in module-scoped variables for connection_edge.c */
void
connection_edge_free_all(void)