2006-02-09 06:46:49 +01:00
|
|
|
/* Copyright (c) 2001 Matej Pfajfar.
|
|
|
|
* Copyright (c) 2001-2004, Roger Dingledine.
|
2007-12-12 22:09:01 +01:00
|
|
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
2016-02-27 18:48:19 +01:00
|
|
|
* Copyright (c) 2007-2016, The Tor Project, Inc. */
|
2004-05-12 21:49:48 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \file relay.c
|
2004-05-13 09:24:49 +02:00
|
|
|
* \brief Handle relay cell encryption/decryption, plus packaging and
|
2009-05-27 23:55:51 +02:00
|
|
|
* receiving from circuits, plus queuing on circuits.
|
2004-05-12 21:49:48 +02:00
|
|
|
**/
|
|
|
|
|
2011-11-11 19:06:17 +01:00
|
|
|
#define RELAY_PRIVATE
|
2004-05-12 21:49:48 +02:00
|
|
|
#include "or.h"
|
2012-10-28 21:13:58 +01:00
|
|
|
#include "addressmap.h"
|
2010-07-22 00:46:18 +02:00
|
|
|
#include "buffers.h"
|
2012-08-25 23:30:01 +02:00
|
|
|
#include "channel.h"
|
2013-10-31 19:33:34 +01:00
|
|
|
#include "circpathbias.h"
|
2010-07-22 01:21:00 +02:00
|
|
|
#include "circuitbuild.h"
|
2010-07-22 09:46:23 +02:00
|
|
|
#include "circuitlist.h"
|
2013-02-20 00:29:17 +01:00
|
|
|
#include "circuituse.h"
|
2010-07-22 10:22:51 +02:00
|
|
|
#include "config.h"
|
2010-07-22 10:32:52 +02:00
|
|
|
#include "connection.h"
|
2010-07-22 10:43:02 +02:00
|
|
|
#include "connection_edge.h"
|
2010-07-22 10:50:34 +02:00
|
|
|
#include "connection_or.h"
|
2010-07-22 11:35:09 +02:00
|
|
|
#include "control.h"
|
2010-07-21 14:38:52 +02:00
|
|
|
#include "geoip.h"
|
2010-07-23 19:58:06 +02:00
|
|
|
#include "main.h"
|
2010-07-23 20:18:55 +02:00
|
|
|
#include "networkstatus.h"
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
#include "nodelist.h"
|
2012-12-06 04:34:49 +01:00
|
|
|
#include "onion.h"
|
2010-07-23 20:51:25 +02:00
|
|
|
#include "policies.h"
|
2010-07-23 21:08:30 +02:00
|
|
|
#include "reasons.h"
|
2010-07-23 21:53:11 +02:00
|
|
|
#include "relay.h"
|
2015-06-18 17:23:08 +02:00
|
|
|
#include "rendcache.h"
|
2010-07-21 17:52:54 +02:00
|
|
|
#include "rendcommon.h"
|
2011-05-16 03:58:46 +02:00
|
|
|
#include "router.h"
|
2010-07-21 17:08:11 +02:00
|
|
|
#include "routerlist.h"
|
2010-07-23 23:23:43 +02:00
|
|
|
#include "routerparse.h"
|
2013-08-25 17:45:07 +02:00
|
|
|
#include "scheduler.h"
|
2004-05-12 21:49:48 +02:00
|
|
|
|
2006-07-26 21:07:26 +02:00
|
|
|
static edge_connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell,
|
2008-12-17 18:20:19 +01:00
|
|
|
cell_direction_t cell_direction,
|
2007-07-12 19:09:19 +02:00
|
|
|
crypt_path_t *layer_hint);
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2010-09-13 23:38:01 +02:00
|
|
|
static int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
|
|
|
|
edge_connection_t *conn,
|
|
|
|
crypt_path_t *layer_hint);
|
|
|
|
static void circuit_consider_sending_sendme(circuit_t *circ,
|
|
|
|
crypt_path_t *layer_hint);
|
|
|
|
static void circuit_resume_edge_reading(circuit_t *circ,
|
|
|
|
crypt_path_t *layer_hint);
|
|
|
|
static int circuit_resume_edge_reading_helper(edge_connection_t *conn,
|
|
|
|
circuit_t *circ,
|
|
|
|
crypt_path_t *layer_hint);
|
|
|
|
static int circuit_consider_stop_edge_reading(circuit_t *circ,
|
|
|
|
crypt_path_t *layer_hint);
|
2010-08-18 20:14:28 +02:00
|
|
|
static int circuit_queue_streams_are_blocked(circuit_t *circ);
|
2013-03-12 04:37:47 +01:00
|
|
|
static void adjust_exit_policy_from_exitpolicy_failure(origin_circuit_t *circ,
|
|
|
|
entry_connection_t *conn,
|
|
|
|
node_t *node,
|
|
|
|
const tor_addr_t *addr);
|
2013-06-15 11:27:23 +02:00
|
|
|
#if 0
|
2013-06-14 05:32:31 +02:00
|
|
|
static int get_max_middle_cells(void);
|
2013-06-15 11:27:23 +02:00
|
|
|
#endif
|
2004-05-12 21:49:48 +02:00
|
|
|
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
/** Stop reading on edge connections when we have this many cells
|
|
|
|
* waiting on the appropriate queue. */
|
|
|
|
#define CELL_QUEUE_HIGHWATER_SIZE 256
|
|
|
|
/** Start reading from edge connections again when we get down to this many
|
|
|
|
* cells. */
|
|
|
|
#define CELL_QUEUE_LOWWATER_SIZE 64
|
|
|
|
|
2004-05-12 21:49:48 +02:00
|
|
|
/** Stats: how many relay cells have originated at this hop, or have
|
|
|
|
* been relayed onward (not recognized at this hop)?
|
|
|
|
*/
|
2005-12-31 09:09:26 +01:00
|
|
|
uint64_t stats_n_relay_cells_relayed = 0;
|
2004-05-12 21:49:48 +02:00
|
|
|
/** Stats: how many relay cells have been delivered to streams at this
|
|
|
|
* hop?
|
|
|
|
*/
|
2005-12-31 09:09:26 +01:00
|
|
|
uint64_t stats_n_relay_cells_delivered = 0;
|
2004-05-12 21:49:48 +02:00
|
|
|
|
2013-02-08 22:28:05 +01:00
|
|
|
/** Used to tell which stream to read from first on a circuit. */
|
|
|
|
static tor_weak_rng_t stream_choice_rng = TOR_WEAK_RNG_INIT;
|
|
|
|
|
2004-05-12 21:49:48 +02:00
|
|
|
/** Update digest from the payload of cell. Assign integrity part to
|
|
|
|
* cell.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static void
|
2012-01-18 21:53:30 +01:00
|
|
|
relay_set_digest(crypto_digest_t *digest, cell_t *cell)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-05-12 21:49:48 +02:00
|
|
|
char integrity[4];
|
|
|
|
relay_header_t rh;
|
|
|
|
|
2010-12-14 01:34:01 +01:00
|
|
|
crypto_digest_add_bytes(digest, (char*)cell->payload, CELL_PAYLOAD_SIZE);
|
2004-05-12 21:49:48 +02:00
|
|
|
crypto_digest_get_digest(digest, integrity, 4);
|
|
|
|
// log_fn(LOG_DEBUG,"Putting digest of %u %u %u %u into relay cell.",
|
|
|
|
// integrity[0], integrity[1], integrity[2], integrity[3]);
|
|
|
|
relay_header_unpack(&rh, cell->payload);
|
|
|
|
memcpy(rh.integrity, integrity, 4);
|
|
|
|
relay_header_pack(cell->payload, &rh);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Does the digest for this circuit indicate that this cell is for us?
|
|
|
|
*
|
|
|
|
* Update digest from the payload of cell (with the integrity part set
|
|
|
|
* to 0). If the integrity part is valid, return 1, else restore digest
|
|
|
|
* and cell to their original state and return 0.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static int
|
2012-01-18 21:53:30 +01:00
|
|
|
relay_digest_matches(crypto_digest_t *digest, cell_t *cell)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2014-06-01 20:02:55 +02:00
|
|
|
uint32_t received_integrity, calculated_integrity;
|
2004-05-12 21:49:48 +02:00
|
|
|
relay_header_t rh;
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_digest_t *backup_digest=NULL;
|
2004-05-12 21:49:48 +02:00
|
|
|
|
|
|
|
backup_digest = crypto_digest_dup(digest);
|
|
|
|
|
|
|
|
relay_header_unpack(&rh, cell->payload);
|
2014-06-01 20:02:55 +02:00
|
|
|
memcpy(&received_integrity, rh.integrity, 4);
|
2004-05-12 21:49:48 +02:00
|
|
|
memset(rh.integrity, 0, 4);
|
|
|
|
relay_header_pack(cell->payload, &rh);
|
|
|
|
|
|
|
|
// log_fn(LOG_DEBUG,"Reading digest of %u %u %u %u from relay cell.",
|
|
|
|
// received_integrity[0], received_integrity[1],
|
|
|
|
// received_integrity[2], received_integrity[3]);
|
|
|
|
|
2010-12-14 01:34:01 +01:00
|
|
|
crypto_digest_add_bytes(digest, (char*) cell->payload, CELL_PAYLOAD_SIZE);
|
2014-06-01 20:02:55 +02:00
|
|
|
crypto_digest_get_digest(digest, (char*) &calculated_integrity, 4);
|
2004-05-12 21:49:48 +02:00
|
|
|
|
2014-06-01 20:02:55 +02:00
|
|
|
if (calculated_integrity != received_integrity) {
|
2004-05-12 21:49:48 +02:00
|
|
|
// log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing.");
|
|
|
|
// (%d vs %d).", received_integrity, calculated_integrity);
|
|
|
|
/* restore digest to its old form */
|
|
|
|
crypto_digest_assign(digest, backup_digest);
|
|
|
|
/* restore the relay header */
|
2014-06-01 20:02:55 +02:00
|
|
|
memcpy(rh.integrity, &received_integrity, 4);
|
2004-05-12 21:49:48 +02:00
|
|
|
relay_header_pack(cell->payload, &rh);
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_digest_free(backup_digest);
|
2004-05-12 21:49:48 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_digest_free(backup_digest);
|
2004-05-12 21:49:48 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Apply <b>cipher</b> to CELL_PAYLOAD_SIZE bytes of <b>in</b>
|
|
|
|
* (in place).
|
|
|
|
*
|
|
|
|
* If <b>encrypt_mode</b> is 1 then encrypt, else decrypt.
|
|
|
|
*
|
2016-02-06 18:14:39 +01:00
|
|
|
* Returns 0.
|
2004-05-12 21:49:48 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static int
|
2012-01-18 21:53:30 +01:00
|
|
|
relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in,
|
2005-06-11 20:52:12 +02:00
|
|
|
int encrypt_mode)
|
|
|
|
{
|
2008-02-07 17:10:33 +01:00
|
|
|
(void)encrypt_mode;
|
2016-02-06 18:14:39 +01:00
|
|
|
crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
|
2005-12-14 21:40:40 +01:00
|
|
|
|
2004-05-12 21:49:48 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Receive a relay cell:
|
2007-03-26 16:08:35 +02:00
|
|
|
* - Crypt it (encrypt if headed toward the origin or if we <b>are</b> the
|
|
|
|
* origin; decrypt if we're headed toward the exit).
|
2004-05-12 21:49:48 +02:00
|
|
|
* - Check if recognized (if exitward).
|
2007-03-26 16:08:35 +02:00
|
|
|
* - If recognized and the digest checks out, then find if there's a stream
|
|
|
|
* that the cell is intended for, and deliver it to the right
|
2004-05-12 21:49:48 +02:00
|
|
|
* connection_edge.
|
2007-03-26 16:08:35 +02:00
|
|
|
* - If not recognized, then we need to relay it: append it to the appropriate
|
|
|
|
* cell_queue on <b>circ</b>.
|
2006-01-05 22:23:03 +01:00
|
|
|
*
|
2007-03-26 16:08:35 +02:00
|
|
|
* Return -<b>reason</b> on failure.
|
2004-05-12 21:49:48 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2008-12-17 18:20:19 +01:00
|
|
|
circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
|
|
|
|
cell_direction_t cell_direction)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2012-08-25 23:30:01 +02:00
|
|
|
channel_t *chan = NULL;
|
2004-05-12 21:49:48 +02:00
|
|
|
crypt_path_t *layer_hint=NULL;
|
|
|
|
char recognized=0;
|
2006-01-05 22:23:03 +01:00
|
|
|
int reason;
|
2004-05-12 21:49:48 +02:00
|
|
|
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(cell);
|
|
|
|
tor_assert(circ);
|
|
|
|
tor_assert(cell_direction == CELL_DIRECTION_OUT ||
|
|
|
|
cell_direction == CELL_DIRECTION_IN);
|
2004-05-12 21:49:48 +02:00
|
|
|
if (circ->marked_for_close)
|
|
|
|
return 0;
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (relay_crypt(circ, cell, cell_direction, &layer_hint, &recognized) < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG,"relay crypt failed. Dropping connection.");
|
2006-10-13 07:27:59 +02:00
|
|
|
return -END_CIRC_REASON_INTERNAL;
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (recognized) {
|
2012-12-12 20:53:18 +01:00
|
|
|
edge_connection_t *conn = NULL;
|
|
|
|
|
|
|
|
if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING) {
|
|
|
|
pathbias_check_probe_response(circ, cell);
|
|
|
|
|
|
|
|
/* We need to drop this cell no matter what to avoid code that expects
|
|
|
|
* a certain purpose (such as the hidserv code). */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-08-17 20:34:50 +02:00
|
|
|
conn = relay_lookup_conn(circ, cell, cell_direction, layer_hint);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (cell_direction == CELL_DIRECTION_OUT) {
|
2004-05-12 21:49:48 +02:00
|
|
|
++stats_n_relay_cells_delivered;
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_OR,"Sending away from origin.");
|
2006-01-05 22:23:03 +01:00
|
|
|
if ((reason=connection_edge_process_relay_cell(cell, circ, conn, NULL))
|
|
|
|
< 0) {
|
2005-10-25 09:04:36 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
2005-12-14 21:40:40 +01:00
|
|
|
"connection_edge_process_relay_cell (away from origin) "
|
|
|
|
"failed.");
|
2006-01-05 22:23:03 +01:00
|
|
|
return reason;
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (cell_direction == CELL_DIRECTION_IN) {
|
2004-05-12 21:49:48 +02:00
|
|
|
++stats_n_relay_cells_delivered;
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_OR,"Sending to origin.");
|
2006-01-05 22:23:03 +01:00
|
|
|
if ((reason = connection_edge_process_relay_cell(cell, circ, conn,
|
|
|
|
layer_hint)) < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_OR,
|
|
|
|
"connection_edge_process_relay_cell (at origin) failed.");
|
2006-01-05 22:23:03 +01:00
|
|
|
return reason;
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not recognized. pass it on. */
|
2004-11-28 10:05:49 +01:00
|
|
|
if (cell_direction == CELL_DIRECTION_OUT) {
|
2004-05-12 21:49:48 +02:00
|
|
|
cell->circ_id = circ->n_circ_id; /* switch it */
|
2012-08-25 23:30:01 +02:00
|
|
|
chan = circ->n_chan;
|
2006-07-23 09:37:35 +02:00
|
|
|
} else if (! CIRCUIT_IS_ORIGIN(circ)) {
|
|
|
|
cell->circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; /* switch it */
|
2012-08-25 23:30:01 +02:00
|
|
|
chan = TO_OR_CIRCUIT(circ)->p_chan;
|
2004-05-12 21:49:48 +02:00
|
|
|
} else {
|
2006-12-29 03:47:51 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_OR,
|
|
|
|
"Dropping unrecognized inbound cell on origin circuit.");
|
2012-12-12 20:53:18 +01:00
|
|
|
/* If we see unrecognized cells on path bias testing circs,
|
|
|
|
* it's bad mojo. Those circuits need to die.
|
|
|
|
* XXX: Shouldn't they always die? */
|
|
|
|
if (circ->purpose == CIRCUIT_PURPOSE_PATH_BIAS_TESTING) {
|
|
|
|
TO_ORIGIN_CIRCUIT(circ)->path_state = PATH_STATE_USE_FAILED;
|
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
if (!chan) {
|
2006-07-23 09:37:35 +02:00
|
|
|
// XXXX Can this splice stuff be done more cleanly?
|
|
|
|
if (! CIRCUIT_IS_ORIGIN(circ) &&
|
|
|
|
TO_OR_CIRCUIT(circ)->rend_splice &&
|
|
|
|
cell_direction == CELL_DIRECTION_OUT) {
|
2016-07-28 16:22:10 +02:00
|
|
|
or_circuit_t *splice_ = TO_OR_CIRCUIT(circ)->rend_splice;
|
2004-05-12 21:49:48 +02:00
|
|
|
tor_assert(circ->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
|
2016-07-28 16:22:10 +02:00
|
|
|
tor_assert(splice_->base_.purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
|
|
|
|
cell->circ_id = splice_->p_circ_id;
|
2009-07-28 03:01:24 +02:00
|
|
|
cell->command = CELL_RELAY; /* can't be relay_early anyway */
|
2016-07-28 16:22:10 +02:00
|
|
|
if ((reason = circuit_receive_relay_cell(cell, TO_CIRCUIT(splice_),
|
2006-01-05 22:23:03 +01:00
|
|
|
CELL_DIRECTION_IN)) < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_REND, "Error relaying cell across rendezvous; closing "
|
|
|
|
"circuits");
|
2005-12-14 21:40:40 +01:00
|
|
|
/* XXXX Do this here, or just return -1? */
|
2006-01-05 22:23:03 +01:00
|
|
|
circuit_mark_for_close(circ, -reason);
|
|
|
|
return reason;
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2005-12-10 10:36:26 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
|
|
|
"Didn't recognize cell, but circ stops here! Closing circ.");
|
2006-01-05 22:23:03 +01:00
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_OR,"Passing on unrecognized cell.");
|
2007-03-26 16:07:59 +02:00
|
|
|
|
|
|
|
++stats_n_relay_cells_relayed; /* XXXX no longer quite accurate {cells}
|
|
|
|
* we might kill the circ before we relay
|
|
|
|
* the cells. */
|
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
append_cell_to_circuit_queue(circ, chan, cell, cell_direction, 0);
|
2004-05-12 21:49:48 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Do the appropriate en/decryptions for <b>cell</b> arriving on
|
|
|
|
* <b>circ</b> in direction <b>cell_direction</b>.
|
|
|
|
*
|
|
|
|
* If cell_direction == CELL_DIRECTION_IN:
|
|
|
|
* - If we're at the origin (we're the OP), for hops 1..N,
|
|
|
|
* decrypt cell. If recognized, stop.
|
|
|
|
* - Else (we're not the OP), encrypt one hop. Cell is not recognized.
|
|
|
|
*
|
|
|
|
* If cell_direction == CELL_DIRECTION_OUT:
|
|
|
|
* - decrypt one hop. Check if recognized.
|
|
|
|
*
|
|
|
|
* If cell is recognized, set *recognized to 1, and set
|
|
|
|
* *layer_hint to the hop that recognized it.
|
|
|
|
*
|
|
|
|
* Return -1 to indicate that we should mark the circuit for close,
|
|
|
|
* else return 0.
|
|
|
|
*/
|
2011-11-11 19:06:17 +01:00
|
|
|
int
|
2008-12-17 18:20:19 +01:00
|
|
|
relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
|
2005-06-11 20:52:12 +02:00
|
|
|
crypt_path_t **layer_hint, char *recognized)
|
|
|
|
{
|
2004-05-12 21:49:48 +02:00
|
|
|
relay_header_t rh;
|
|
|
|
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(circ);
|
|
|
|
tor_assert(cell);
|
|
|
|
tor_assert(recognized);
|
|
|
|
tor_assert(cell_direction == CELL_DIRECTION_IN ||
|
|
|
|
cell_direction == CELL_DIRECTION_OUT);
|
2004-05-12 21:49:48 +02:00
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (cell_direction == CELL_DIRECTION_IN) {
|
|
|
|
if (CIRCUIT_IS_ORIGIN(circ)) { /* We're at the beginning of the circuit.
|
2005-12-14 21:40:40 +01:00
|
|
|
* We'll want to do layered decrypts. */
|
2006-07-23 09:37:35 +02:00
|
|
|
crypt_path_t *thishop, *cpath = TO_ORIGIN_CIRCUIT(circ)->cpath;
|
|
|
|
thishop = cpath;
|
2004-11-28 10:05:49 +01:00
|
|
|
if (thishop->state != CPATH_STATE_OPEN) {
|
2005-12-10 10:36:26 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
|
|
|
"Relay cell before first created cell? Closing.");
|
2004-05-12 21:49:48 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
do { /* Remember: cpath is in forward order, that is, first hop first. */
|
|
|
|
tor_assert(thishop);
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (relay_crypt_one_payload(thishop->b_crypto, cell->payload, 0) < 0)
|
2004-05-12 21:49:48 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
relay_header_unpack(&rh, cell->payload);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (rh.recognized == 0) {
|
2004-05-12 21:49:48 +02:00
|
|
|
/* it's possibly recognized. have to check digest to be sure. */
|
2004-11-28 10:05:49 +01:00
|
|
|
if (relay_digest_matches(thishop->b_digest, cell)) {
|
2004-05-12 21:49:48 +02:00
|
|
|
*recognized = 1;
|
|
|
|
*layer_hint = thishop;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
thishop = thishop->next;
|
2006-07-23 09:37:35 +02:00
|
|
|
} while (thishop != cpath && thishop->state == CPATH_STATE_OPEN);
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_OR,
|
|
|
|
"Incoming cell at client not recognized. Closing.");
|
2004-05-12 21:49:48 +02:00
|
|
|
return -1;
|
|
|
|
} else { /* we're in the middle. Just one crypt. */
|
2006-07-23 09:37:35 +02:00
|
|
|
if (relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->p_crypto,
|
|
|
|
cell->payload, 1) < 0)
|
2004-05-12 21:49:48 +02:00
|
|
|
return -1;
|
2005-12-14 21:40:40 +01:00
|
|
|
// log_fn(LOG_DEBUG,"Skipping recognized check, because we're not "
|
2006-09-07 03:00:37 +02:00
|
|
|
// "the client.");
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
|
|
|
} else /* cell_direction == CELL_DIRECTION_OUT */ {
|
|
|
|
/* we're in the middle. Just one crypt. */
|
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
if (relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->n_crypto,
|
|
|
|
cell->payload, 0) < 0)
|
2004-05-12 21:49:48 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
relay_header_unpack(&rh, cell->payload);
|
|
|
|
if (rh.recognized == 0) {
|
|
|
|
/* it's possibly recognized. have to check digest to be sure. */
|
2006-07-23 09:37:35 +02:00
|
|
|
if (relay_digest_matches(TO_OR_CIRCUIT(circ)->n_digest, cell)) {
|
2004-05-12 21:49:48 +02:00
|
|
|
*recognized = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
/** Package a relay cell from an edge:
|
2004-05-12 21:49:48 +02:00
|
|
|
* - Encrypt it to the right layer
|
2007-04-10 01:15:46 +02:00
|
|
|
* - Append it to the appropriate cell_queue on <b>circ</b>.
|
2004-05-12 21:49:48 +02:00
|
|
|
*/
|
2004-05-15 09:21:25 +02:00
|
|
|
static int
|
2004-05-12 21:49:48 +02:00
|
|
|
circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
|
2008-12-17 18:20:19 +01:00
|
|
|
cell_direction_t cell_direction,
|
2013-03-19 22:00:40 +01:00
|
|
|
crypt_path_t *layer_hint, streamid_t on_stream,
|
|
|
|
const char *filename, int lineno)
|
2004-07-23 01:21:12 +02:00
|
|
|
{
|
2012-08-25 23:30:01 +02:00
|
|
|
channel_t *chan; /* where to send the cell */
|
2004-05-12 21:49:48 +02:00
|
|
|
|
2017-09-29 15:26:16 +02:00
|
|
|
if (circ->marked_for_close) {
|
|
|
|
/* Circuit is marked; send nothing. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (cell_direction == CELL_DIRECTION_OUT) {
|
2006-07-23 09:37:35 +02:00
|
|
|
crypt_path_t *thishop; /* counter for repeated crypts */
|
2012-08-25 23:30:01 +02:00
|
|
|
chan = circ->n_chan;
|
2013-03-19 22:00:40 +01:00
|
|
|
if (!chan) {
|
|
|
|
log_warn(LD_BUG,"outgoing relay cell sent from %s:%d has n_chan==NULL."
|
|
|
|
" Dropping.", filename, lineno);
|
|
|
|
return 0; /* just drop it */
|
|
|
|
}
|
|
|
|
if (!CIRCUIT_IS_ORIGIN(circ)) {
|
|
|
|
log_warn(LD_BUG,"outgoing relay cell sent from %s:%d on non-origin "
|
|
|
|
"circ. Dropping.", filename, lineno);
|
2004-05-12 21:49:48 +02:00
|
|
|
return 0; /* just drop it */
|
|
|
|
}
|
2008-07-23 17:58:38 +02:00
|
|
|
|
2004-05-12 21:49:48 +02:00
|
|
|
relay_set_digest(layer_hint->f_digest, cell);
|
|
|
|
|
|
|
|
thishop = layer_hint;
|
|
|
|
/* moving from farthest to nearest hop */
|
|
|
|
do {
|
|
|
|
tor_assert(thishop);
|
2005-10-25 09:04:36 +02:00
|
|
|
/* XXXX RD This is a bug, right? */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_OR,"crypting a layer of the relay cell.");
|
2004-11-28 10:05:49 +01:00
|
|
|
if (relay_crypt_one_payload(thishop->f_crypto, cell->payload, 1) < 0) {
|
2004-05-12 21:49:48 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
thishop = thishop->prev;
|
2006-07-23 09:37:35 +02:00
|
|
|
} while (thishop != TO_ORIGIN_CIRCUIT(circ)->cpath->prev);
|
2004-05-12 21:49:48 +02:00
|
|
|
|
|
|
|
} else { /* incoming cell */
|
2006-07-23 09:37:35 +02:00
|
|
|
or_circuit_t *or_circ;
|
|
|
|
if (CIRCUIT_IS_ORIGIN(circ)) {
|
2007-05-24 20:12:52 +02:00
|
|
|
/* We should never package an _incoming_ cell from the circuit
|
|
|
|
* origin; that means we messed up somewhere. */
|
2006-07-23 09:37:35 +02:00
|
|
|
log_warn(LD_BUG,"incoming relay cell at origin circuit. Dropping.");
|
2005-11-30 04:01:16 +01:00
|
|
|
assert_circuit_ok(circ);
|
2004-05-12 21:49:48 +02:00
|
|
|
return 0; /* just drop it */
|
|
|
|
}
|
2006-07-23 09:37:35 +02:00
|
|
|
or_circ = TO_OR_CIRCUIT(circ);
|
2012-08-25 23:30:01 +02:00
|
|
|
chan = or_circ->p_chan;
|
2006-07-23 09:37:35 +02:00
|
|
|
relay_set_digest(or_circ->p_digest, cell);
|
|
|
|
if (relay_crypt_one_payload(or_circ->p_crypto, cell->payload, 1) < 0)
|
2004-05-12 21:49:48 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
++stats_n_relay_cells_relayed;
|
2007-03-26 16:07:59 +02:00
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
append_cell_to_circuit_queue(circ, chan, cell, cell_direction, on_stream);
|
2004-05-12 21:49:48 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** If cell's stream_id matches the stream_id of any conn that's
|
|
|
|
* attached to circ, return that conn, else return NULL.
|
|
|
|
*/
|
2006-07-26 21:07:26 +02:00
|
|
|
static edge_connection_t *
|
2008-12-17 18:20:36 +01:00
|
|
|
relay_lookup_conn(circuit_t *circ, cell_t *cell,
|
|
|
|
cell_direction_t cell_direction, crypt_path_t *layer_hint)
|
2004-05-12 21:49:48 +02:00
|
|
|
{
|
2006-07-26 21:07:26 +02:00
|
|
|
edge_connection_t *tmpconn;
|
2004-05-12 21:49:48 +02:00
|
|
|
relay_header_t rh;
|
|
|
|
|
|
|
|
relay_header_unpack(&rh, cell->payload);
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!rh.stream_id)
|
2004-05-12 21:49:48 +02:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* IN or OUT cells could have come from either direction, now
|
|
|
|
* that we allow rendezvous *to* an OP.
|
|
|
|
*/
|
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
if (CIRCUIT_IS_ORIGIN(circ)) {
|
|
|
|
for (tmpconn = TO_ORIGIN_CIRCUIT(circ)->p_streams; tmpconn;
|
|
|
|
tmpconn=tmpconn->next_stream) {
|
2006-07-26 21:07:26 +02:00
|
|
|
if (rh.stream_id == tmpconn->stream_id &&
|
2012-10-12 18:22:13 +02:00
|
|
|
!tmpconn->base_.marked_for_close &&
|
2007-07-12 19:09:19 +02:00
|
|
|
tmpconn->cpath_layer == layer_hint) {
|
2006-07-23 09:37:35 +02:00
|
|
|
log_debug(LD_APP,"found conn for stream %d.", rh.stream_id);
|
2004-05-12 21:49:48 +02:00
|
|
|
return tmpconn;
|
2006-07-23 09:37:35 +02:00
|
|
|
}
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
2006-07-23 09:37:35 +02:00
|
|
|
} else {
|
|
|
|
for (tmpconn = TO_OR_CIRCUIT(circ)->n_streams; tmpconn;
|
|
|
|
tmpconn=tmpconn->next_stream) {
|
2006-07-26 21:07:37 +02:00
|
|
|
if (rh.stream_id == tmpconn->stream_id &&
|
2012-10-12 18:22:13 +02:00
|
|
|
!tmpconn->base_.marked_for_close) {
|
2006-07-23 09:37:35 +02:00
|
|
|
log_debug(LD_EXIT,"found conn for stream %d.", rh.stream_id);
|
|
|
|
if (cell_direction == CELL_DIRECTION_OUT ||
|
|
|
|
connection_edge_is_rendezvous_stream(tmpconn))
|
|
|
|
return tmpconn;
|
|
|
|
}
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
2006-07-23 09:37:35 +02:00
|
|
|
for (tmpconn = TO_OR_CIRCUIT(circ)->resolving_streams; tmpconn;
|
|
|
|
tmpconn=tmpconn->next_stream) {
|
2006-07-26 21:07:37 +02:00
|
|
|
if (rh.stream_id == tmpconn->stream_id &&
|
2012-10-12 18:22:13 +02:00
|
|
|
!tmpconn->base_.marked_for_close) {
|
2006-07-23 09:37:35 +02:00
|
|
|
log_debug(LD_EXIT,"found conn for stream %d.", rh.stream_id);
|
|
|
|
return tmpconn;
|
|
|
|
}
|
2004-05-12 21:49:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL; /* probably a begin relay cell */
|
|
|
|
}
|
|
|
|
|
2004-05-13 09:24:49 +02:00
|
|
|
/** Pack the relay_header_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.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
2010-12-14 01:34:01 +01:00
|
|
|
relay_header_pack(uint8_t *dest, const relay_header_t *src)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2010-12-14 01:34:01 +01:00
|
|
|
set_uint8(dest, src->command);
|
2004-05-13 09:24:49 +02:00
|
|
|
set_uint16(dest+1, htons(src->recognized));
|
|
|
|
set_uint16(dest+3, htons(src->stream_id));
|
|
|
|
memcpy(dest+5, src->integrity, 4);
|
|
|
|
set_uint16(dest+9, htons(src->length));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Unpack the network-order buffer <b>src</b> into a host-order
|
|
|
|
* relay_header_t structure <b>dest</b>.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
2010-12-14 01:34:01 +01:00
|
|
|
relay_header_unpack(relay_header_t *dest, const uint8_t *src)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2010-12-14 01:34:01 +01:00
|
|
|
dest->command = get_uint8(src);
|
2004-05-13 09:24:49 +02:00
|
|
|
dest->recognized = ntohs(get_uint16(src+1));
|
|
|
|
dest->stream_id = ntohs(get_uint16(src+3));
|
|
|
|
memcpy(dest->integrity, src+5, 4);
|
|
|
|
dest->length = ntohs(get_uint16(src+9));
|
|
|
|
}
|
|
|
|
|
2008-12-30 12:43:50 +01:00
|
|
|
/** Convert the relay <b>command</b> into a human-readable string. */
|
|
|
|
static const char *
|
|
|
|
relay_command_to_string(uint8_t command)
|
|
|
|
{
|
2014-08-18 19:19:43 +02:00
|
|
|
static char buf[64];
|
2008-12-30 12:43:50 +01:00
|
|
|
switch (command) {
|
|
|
|
case RELAY_COMMAND_BEGIN: return "BEGIN";
|
|
|
|
case RELAY_COMMAND_DATA: return "DATA";
|
|
|
|
case RELAY_COMMAND_END: return "END";
|
|
|
|
case RELAY_COMMAND_CONNECTED: return "CONNECTED";
|
|
|
|
case RELAY_COMMAND_SENDME: return "SENDME";
|
|
|
|
case RELAY_COMMAND_EXTEND: return "EXTEND";
|
|
|
|
case RELAY_COMMAND_EXTENDED: return "EXTENDED";
|
|
|
|
case RELAY_COMMAND_TRUNCATE: return "TRUNCATE";
|
|
|
|
case RELAY_COMMAND_TRUNCATED: return "TRUNCATED";
|
|
|
|
case RELAY_COMMAND_DROP: return "DROP";
|
|
|
|
case RELAY_COMMAND_RESOLVE: return "RESOLVE";
|
|
|
|
case RELAY_COMMAND_RESOLVED: return "RESOLVED";
|
|
|
|
case RELAY_COMMAND_BEGIN_DIR: return "BEGIN_DIR";
|
|
|
|
case RELAY_COMMAND_ESTABLISH_INTRO: return "ESTABLISH_INTRO";
|
|
|
|
case RELAY_COMMAND_ESTABLISH_RENDEZVOUS: return "ESTABLISH_RENDEZVOUS";
|
|
|
|
case RELAY_COMMAND_INTRODUCE1: return "INTRODUCE1";
|
|
|
|
case RELAY_COMMAND_INTRODUCE2: return "INTRODUCE2";
|
|
|
|
case RELAY_COMMAND_RENDEZVOUS1: return "RENDEZVOUS1";
|
|
|
|
case RELAY_COMMAND_RENDEZVOUS2: return "RENDEZVOUS2";
|
|
|
|
case RELAY_COMMAND_INTRO_ESTABLISHED: return "INTRO_ESTABLISHED";
|
|
|
|
case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED:
|
|
|
|
return "RENDEZVOUS_ESTABLISHED";
|
|
|
|
case RELAY_COMMAND_INTRODUCE_ACK: return "INTRODUCE_ACK";
|
2014-08-18 19:19:43 +02:00
|
|
|
case RELAY_COMMAND_EXTEND2: return "EXTEND2";
|
|
|
|
case RELAY_COMMAND_EXTENDED2: return "EXTENDED2";
|
|
|
|
default:
|
|
|
|
tor_snprintf(buf, sizeof(buf), "Unrecognized relay command %u",
|
|
|
|
(unsigned)command);
|
|
|
|
return buf;
|
2008-12-30 12:43:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-20 16:57:46 +02:00
|
|
|
/** Make a relay cell out of <b>relay_command</b> and <b>payload</b>, and send
|
|
|
|
* it onto the open circuit <b>circ</b>. <b>stream_id</b> is the ID on
|
|
|
|
* <b>circ</b> for the stream that's sending the relay cell, or 0 if it's a
|
|
|
|
* control cell. <b>cpath_layer</b> is NULL for OR->OP cells, or the
|
|
|
|
* destination hop for OP->OR cells.
|
2004-05-13 09:24:49 +02:00
|
|
|
*
|
2006-10-20 16:57:46 +02:00
|
|
|
* If you can't send the cell, mark the circuit for close and return -1. Else
|
|
|
|
* return 0.
|
2004-05-13 09:24:49 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2013-03-19 22:00:40 +01:00
|
|
|
relay_send_command_from_edge_(streamid_t stream_id, circuit_t *circ,
|
|
|
|
uint8_t relay_command, const char *payload,
|
|
|
|
size_t payload_len, crypt_path_t *cpath_layer,
|
|
|
|
const char *filename, int lineno)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-05-13 09:24:49 +02:00
|
|
|
cell_t cell;
|
|
|
|
relay_header_t rh;
|
2008-12-17 18:20:19 +01:00
|
|
|
cell_direction_t cell_direction;
|
2006-07-23 09:37:35 +02:00
|
|
|
/* XXXX NM Split this function into a separate versions per circuit type? */
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2006-10-20 16:57:46 +02:00
|
|
|
tor_assert(circ);
|
2008-02-20 00:54:17 +01:00
|
|
|
tor_assert(payload_len <= RELAY_PAYLOAD_SIZE);
|
2004-05-13 09:24:49 +02:00
|
|
|
|
|
|
|
memset(&cell, 0, sizeof(cell_t));
|
|
|
|
cell.command = CELL_RELAY;
|
2004-11-28 10:05:49 +01:00
|
|
|
if (cpath_layer) {
|
2004-05-13 09:24:49 +02:00
|
|
|
cell.circ_id = circ->n_circ_id;
|
|
|
|
cell_direction = CELL_DIRECTION_OUT;
|
2006-07-23 09:37:35 +02:00
|
|
|
} else if (! CIRCUIT_IS_ORIGIN(circ)) {
|
|
|
|
cell.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id;
|
2004-05-13 09:24:49 +02:00
|
|
|
cell_direction = CELL_DIRECTION_IN;
|
2006-07-23 09:37:35 +02:00
|
|
|
} else {
|
|
|
|
return -1;
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(&rh, 0, sizeof(rh));
|
|
|
|
rh.command = relay_command;
|
2006-10-20 16:57:46 +02:00
|
|
|
rh.stream_id = stream_id;
|
2004-05-13 09:24:49 +02:00
|
|
|
rh.length = payload_len;
|
|
|
|
relay_header_pack(cell.payload, &rh);
|
2008-02-20 00:54:17 +01:00
|
|
|
if (payload_len)
|
2004-05-13 09:24:49 +02:00
|
|
|
memcpy(cell.payload+RELAY_HEADER_SIZE, payload, payload_len);
|
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_OR,"delivering %d cell %s.", relay_command,
|
|
|
|
cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2009-07-12 16:33:31 +02:00
|
|
|
/* If we are sending an END cell and this circuit is used for a tunneled
|
|
|
|
* directory request, advance its state. */
|
2009-07-14 22:24:50 +02:00
|
|
|
if (relay_command == RELAY_COMMAND_END && circ->dirreq_id)
|
|
|
|
geoip_change_dirreq_state(circ->dirreq_id, DIRREQ_TUNNELED,
|
|
|
|
DIRREQ_END_CELL_SENT);
|
2009-07-12 16:33:31 +02:00
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
if (cell_direction == CELL_DIRECTION_OUT && circ->n_chan) {
|
2007-07-30 00:13:44 +02:00
|
|
|
/* if we're using relaybandwidthrate, this conn wants priority */
|
2012-08-25 23:30:01 +02:00
|
|
|
channel_timestamp_client(circ->n_chan);
|
2007-07-30 00:13:44 +02:00
|
|
|
}
|
|
|
|
|
2008-07-23 17:58:38 +02:00
|
|
|
if (cell_direction == CELL_DIRECTION_OUT) {
|
|
|
|
origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
|
2008-12-29 20:55:17 +01:00
|
|
|
if (origin_circ->remaining_relay_early_cells > 0 &&
|
|
|
|
(relay_command == RELAY_COMMAND_EXTEND ||
|
2012-12-06 07:53:29 +01:00
|
|
|
relay_command == RELAY_COMMAND_EXTEND2 ||
|
2010-10-18 04:23:31 +02:00
|
|
|
cpath_layer != origin_circ->cpath)) {
|
|
|
|
/* If we've got any relay_early cells left and (we're sending
|
|
|
|
* an extend cell or we're not talking to the first hop), use
|
|
|
|
* one of them. Don't worry about the conn protocol version:
|
2009-07-28 03:01:24 +02:00
|
|
|
* append_cell_to_circuit_queue will fix it up. */
|
2008-07-23 17:58:38 +02:00
|
|
|
cell.command = CELL_RELAY_EARLY;
|
|
|
|
--origin_circ->remaining_relay_early_cells;
|
|
|
|
log_debug(LD_OR, "Sending a RELAY_EARLY cell; %d remaining.",
|
|
|
|
(int)origin_circ->remaining_relay_early_cells);
|
2008-12-30 12:43:50 +01:00
|
|
|
/* Memorize the command that is sent as RELAY_EARLY cell; helps debug
|
|
|
|
* task 878. */
|
|
|
|
origin_circ->relay_early_commands[
|
|
|
|
origin_circ->relay_early_cells_sent++] = relay_command;
|
2012-12-06 07:53:29 +01:00
|
|
|
} else if (relay_command == RELAY_COMMAND_EXTEND ||
|
|
|
|
relay_command == RELAY_COMMAND_EXTEND2) {
|
2008-12-30 12:43:50 +01:00
|
|
|
/* If no RELAY_EARLY cells can be sent over this circuit, log which
|
|
|
|
* commands have been sent as RELAY_EARLY cells before; helps debug
|
|
|
|
* task 878. */
|
2012-01-18 21:53:30 +01:00
|
|
|
smartlist_t *commands_list = smartlist_new();
|
2008-12-30 12:43:50 +01:00
|
|
|
int i = 0;
|
|
|
|
char *commands = NULL;
|
|
|
|
for (; i < origin_circ->relay_early_cells_sent; i++)
|
|
|
|
smartlist_add(commands_list, (char *)
|
|
|
|
relay_command_to_string(origin_circ->relay_early_commands[i]));
|
|
|
|
commands = smartlist_join_strings(commands_list, ",", 0, NULL);
|
2008-07-23 17:58:38 +02:00
|
|
|
log_warn(LD_BUG, "Uh-oh. We're sending a RELAY_COMMAND_EXTEND cell, "
|
2008-12-30 12:43:50 +01:00
|
|
|
"but we have run out of RELAY_EARLY cells on that circuit. "
|
|
|
|
"Commands sent before: %s", commands);
|
|
|
|
tor_free(commands);
|
|
|
|
smartlist_free(commands_list);
|
2008-07-23 17:58:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-03 16:26:50 +02:00
|
|
|
if (circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer,
|
2013-03-19 22:00:40 +01:00
|
|
|
stream_id, filename, lineno) < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG,"circuit_package_relay_cell failed. Closing.");
|
2006-01-05 22:23:03 +01:00
|
|
|
circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
|
2004-05-13 09:24:49 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-10-20 16:57:46 +02:00
|
|
|
/** Make a relay cell out of <b>relay_command</b> and <b>payload</b>, and
|
|
|
|
* send it onto the open circuit <b>circ</b>. <b>fromconn</b> is the stream
|
|
|
|
* that's sending the relay cell, or NULL if it's a control cell.
|
|
|
|
* <b>cpath_layer</b> is NULL for OR->OP cells, or the destination hop
|
|
|
|
* for OP->OR cells.
|
|
|
|
*
|
|
|
|
* If you can't send the cell, mark the circuit for close and
|
|
|
|
* return -1. Else return 0.
|
|
|
|
*/
|
|
|
|
int
|
2007-03-24 16:58:11 +01:00
|
|
|
connection_edge_send_command(edge_connection_t *fromconn,
|
2008-02-20 01:57:37 +01:00
|
|
|
uint8_t relay_command, const char *payload,
|
2007-03-24 16:57:51 +01:00
|
|
|
size_t payload_len)
|
2006-10-20 16:57:46 +02:00
|
|
|
{
|
|
|
|
/* XXXX NM Split this function into a separate versions per circuit type? */
|
2007-03-24 16:58:11 +01:00
|
|
|
circuit_t *circ;
|
2011-07-20 18:55:42 +02:00
|
|
|
crypt_path_t *cpath_layer = fromconn->cpath_layer;
|
2007-03-24 16:58:11 +01:00
|
|
|
tor_assert(fromconn);
|
|
|
|
circ = fromconn->on_circuit;
|
2006-10-20 16:57:46 +02:00
|
|
|
|
2012-10-12 18:22:13 +02:00
|
|
|
if (fromconn->base_.marked_for_close) {
|
2006-10-20 16:57:46 +02:00
|
|
|
log_warn(LD_BUG,
|
2007-03-04 21:11:46 +01:00
|
|
|
"called on conn that's already marked for close at %s:%d.",
|
2012-10-12 18:22:13 +02:00
|
|
|
fromconn->base_.marked_for_close_file,
|
|
|
|
fromconn->base_.marked_for_close);
|
2006-10-20 16:57:46 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!circ) {
|
2012-10-12 18:22:13 +02:00
|
|
|
if (fromconn->base_.type == CONN_TYPE_AP) {
|
2006-10-20 16:57:46 +02:00
|
|
|
log_info(LD_APP,"no circ. Closing conn.");
|
2011-07-20 18:55:42 +02:00
|
|
|
connection_mark_unattached_ap(EDGE_TO_ENTRY_CONN(fromconn),
|
|
|
|
END_STREAM_REASON_INTERNAL);
|
2006-10-20 16:57:46 +02:00
|
|
|
} else {
|
|
|
|
log_info(LD_EXIT,"no circ. Closing conn.");
|
2008-12-17 15:59:28 +01:00
|
|
|
fromconn->edge_has_sent_end = 1; /* no circ to send to */
|
2006-10-20 19:54:48 +02:00
|
|
|
fromconn->end_reason = END_STREAM_REASON_INTERNAL;
|
2006-10-20 16:57:46 +02:00
|
|
|
connection_mark_for_close(TO_CONN(fromconn));
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-09-29 15:26:16 +02:00
|
|
|
if (circ->marked_for_close) {
|
|
|
|
/* The circuit has been marked, but not freed yet. When it's freed, it
|
|
|
|
* will mark this connection for close. */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-03-24 16:58:11 +01:00
|
|
|
return relay_send_command_from_edge(fromconn->stream_id, circ,
|
|
|
|
relay_command, payload,
|
2011-07-20 18:55:42 +02:00
|
|
|
payload_len, cpath_layer);
|
2006-10-20 16:57:46 +02:00
|
|
|
}
|
|
|
|
|
2004-05-13 09:24:49 +02:00
|
|
|
/** How many times will I retry a stream that fails due to DNS
|
2005-08-13 02:31:41 +02:00
|
|
|
* resolve failure or misc error?
|
2004-05-13 09:24:49 +02:00
|
|
|
*/
|
|
|
|
#define MAX_RESOLVE_FAILURES 3
|
|
|
|
|
2005-04-04 05:30:49 +02:00
|
|
|
/** Return 1 if reason is something that you should retry if you
|
|
|
|
* get the end cell before you've connected; else return 0. */
|
2004-05-13 09:24:49 +02:00
|
|
|
static int
|
2005-06-11 20:52:12 +02:00
|
|
|
edge_reason_is_retriable(int reason)
|
|
|
|
{
|
2005-04-04 05:30:49 +02:00
|
|
|
return reason == END_STREAM_REASON_HIBERNATING ||
|
|
|
|
reason == END_STREAM_REASON_RESOURCELIMIT ||
|
|
|
|
reason == END_STREAM_REASON_EXITPOLICY ||
|
2005-08-13 02:31:41 +02:00
|
|
|
reason == END_STREAM_REASON_RESOLVEFAILED ||
|
2010-08-16 06:04:49 +02:00
|
|
|
reason == END_STREAM_REASON_MISC ||
|
|
|
|
reason == END_STREAM_REASON_NOROUTE;
|
2005-04-04 05:30:49 +02:00
|
|
|
}
|
|
|
|
|
2008-12-14 20:40:56 +01:00
|
|
|
/** Called when we receive an END cell on a stream that isn't open yet,
|
|
|
|
* from the client side.
|
2005-06-11 20:52:12 +02:00
|
|
|
* Arguments are as for connection_edge_process_relay_cell().
|
|
|
|
*/
|
2005-04-04 05:30:49 +02:00
|
|
|
static int
|
2008-12-14 20:40:56 +01:00
|
|
|
connection_ap_process_end_not_open(
|
2006-07-23 09:37:35 +02:00
|
|
|
relay_header_t *rh, cell_t *cell, origin_circuit_t *circ,
|
2011-07-20 18:55:42 +02:00
|
|
|
entry_connection_t *conn, crypt_path_t *layer_hint)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
node_t *exitrouter;
|
2005-04-04 05:30:49 +02:00
|
|
|
int reason = *(cell->payload+RELAY_HEADER_SIZE);
|
2013-01-16 18:52:15 +01:00
|
|
|
int control_reason;
|
2011-07-20 18:55:42 +02:00
|
|
|
edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
|
2006-06-05 00:42:13 +02:00
|
|
|
(void) layer_hint; /* unused */
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2012-12-09 01:37:22 +01:00
|
|
|
if (rh->length > 0) {
|
2012-12-12 02:49:12 +01:00
|
|
|
if (reason == END_STREAM_REASON_TORPROTOCOL ||
|
|
|
|
reason == END_STREAM_REASON_DESTROY) {
|
2014-02-10 03:35:14 +01:00
|
|
|
/* Both of these reasons could mean a failed tag
|
2012-12-18 21:39:03 +01:00
|
|
|
* hit the exit and it complained. Do not probe.
|
2012-12-12 02:49:12 +01:00
|
|
|
* Fail the circuit. */
|
|
|
|
circ->path_state = PATH_STATE_USE_FAILED;
|
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
2014-02-10 03:35:14 +01:00
|
|
|
} else if (reason == END_STREAM_REASON_INTERNAL) {
|
|
|
|
/* We can't infer success or failure, since older Tors report
|
|
|
|
* ENETUNREACH as END_STREAM_REASON_INTERNAL. */
|
2012-12-12 02:49:12 +01:00
|
|
|
} else {
|
|
|
|
/* Path bias: If we get a valid reason code from the exit,
|
2012-12-18 21:39:03 +01:00
|
|
|
* it wasn't due to tagging.
|
|
|
|
*
|
|
|
|
* We rely on recognized+digest being strong enough to make
|
|
|
|
* tags unlikely to allow us to get tagged, yet 'recognized'
|
|
|
|
* reason codes here. */
|
2013-01-19 04:37:16 +01:00
|
|
|
pathbias_mark_use_success(circ);
|
2012-12-12 02:49:12 +01:00
|
|
|
}
|
2012-12-09 01:37:22 +01:00
|
|
|
}
|
|
|
|
|
2013-01-16 18:08:10 +01:00
|
|
|
if (rh->length == 0) {
|
|
|
|
reason = END_STREAM_REASON_MISC;
|
|
|
|
}
|
|
|
|
|
2013-01-16 18:52:15 +01:00
|
|
|
control_reason = reason | END_STREAM_REASON_FLAG_REMOTE;
|
|
|
|
|
2013-01-16 18:08:10 +01:00
|
|
|
if (edge_reason_is_retriable(reason) &&
|
2011-09-08 02:26:58 +02:00
|
|
|
/* avoid retry if rend */
|
|
|
|
!connection_edge_is_rendezvous_stream(edge_conn)) {
|
2010-09-29 06:38:32 +02:00
|
|
|
const char *chosen_exit_digest =
|
|
|
|
circ->build_state->chosen_exit->identity_digest;
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_APP,"Address '%s' refused due to '%s'. Considering retrying.",
|
|
|
|
safe_str(conn->socks_request->address),
|
2008-06-11 02:17:02 +02:00
|
|
|
stream_end_reason_to_string(reason));
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
exitrouter = node_get_mutable_by_id(chosen_exit_digest);
|
2005-04-04 05:30:49 +02:00
|
|
|
switch (reason) {
|
2013-03-12 04:37:47 +01:00
|
|
|
case END_STREAM_REASON_EXITPOLICY: {
|
|
|
|
tor_addr_t addr;
|
|
|
|
tor_addr_make_unspec(&addr);
|
2005-04-04 05:30:49 +02:00
|
|
|
if (rh->length >= 5) {
|
2012-10-31 23:27:48 +01:00
|
|
|
int ttl = -1;
|
|
|
|
tor_addr_make_unspec(&addr);
|
|
|
|
if (rh->length == 5 || rh->length == 9) {
|
|
|
|
tor_addr_from_ipv4n(&addr,
|
|
|
|
get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
|
|
|
|
if (rh->length == 9)
|
|
|
|
ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+5));
|
|
|
|
} else if (rh->length == 17 || rh->length == 21) {
|
|
|
|
tor_addr_from_ipv6_bytes(&addr,
|
|
|
|
(char*)(cell->payload+RELAY_HEADER_SIZE+1));
|
|
|
|
if (rh->length == 21)
|
|
|
|
ttl = (int)ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+17));
|
|
|
|
}
|
|
|
|
if (tor_addr_is_null(&addr)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_APP,"Address '%s' resolved to 0.0.0.0. Closing,",
|
|
|
|
safe_str(conn->socks_request->address));
|
2005-04-04 05:30:49 +02:00
|
|
|
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-10-18 00:08:49 +02:00
|
|
|
|
2015-01-19 17:56:03 +01:00
|
|
|
if ((tor_addr_family(&addr) == AF_INET &&
|
|
|
|
!conn->entry_cfg.ipv4_traffic) ||
|
|
|
|
(tor_addr_family(&addr) == AF_INET6 &&
|
|
|
|
!conn->entry_cfg.ipv6_traffic)) {
|
2012-10-31 23:27:48 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_APP,
|
|
|
|
"Got an EXITPOLICY failure on a connection with a "
|
|
|
|
"mismatched family. Closing.");
|
|
|
|
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
|
|
|
|
return 0;
|
|
|
|
}
|
2009-01-05 21:52:14 +01:00
|
|
|
if (get_options()->ClientDNSRejectInternalAddresses &&
|
2012-10-31 23:27:48 +01:00
|
|
|
tor_addr_is_internal(&addr, 0)) {
|
2009-01-05 21:52:14 +01:00
|
|
|
log_info(LD_APP,"Address '%s' resolved to internal. Closing,",
|
|
|
|
safe_str(conn->socks_request->address));
|
|
|
|
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-28 22:13:37 +01:00
|
|
|
|
2012-11-25 19:45:36 +01:00
|
|
|
client_dns_set_addressmap(conn,
|
2012-10-31 23:27:48 +01:00
|
|
|
conn->socks_request->address, &addr,
|
2009-01-05 21:52:14 +01:00
|
|
|
conn->chosen_exit_name, ttl);
|
2012-11-28 02:53:09 +01:00
|
|
|
|
|
|
|
{
|
|
|
|
char new_addr[TOR_ADDR_BUF_LEN];
|
|
|
|
tor_addr_to_str(new_addr, &addr, sizeof(new_addr), 1);
|
|
|
|
if (strcmp(conn->socks_request->address, new_addr)) {
|
|
|
|
strlcpy(conn->socks_request->address, new_addr,
|
|
|
|
sizeof(conn->socks_request->address));
|
|
|
|
control_event_stream_status(conn, STREAM_EVENT_REMAP, 0);
|
|
|
|
}
|
|
|
|
}
|
2005-04-04 05:30:49 +02:00
|
|
|
}
|
2016-01-26 03:45:01 +01:00
|
|
|
/* check if the exit *ought* to have allowed it */
|
2013-03-12 04:37:47 +01:00
|
|
|
|
|
|
|
adjust_exit_policy_from_exitpolicy_failure(circ,
|
|
|
|
conn,
|
|
|
|
exitrouter,
|
|
|
|
&addr);
|
2012-11-28 02:53:09 +01:00
|
|
|
|
2008-12-17 15:59:28 +01:00
|
|
|
if (conn->chosen_exit_optional ||
|
|
|
|
conn->chosen_exit_retries) {
|
2006-07-27 07:03:57 +02:00
|
|
|
/* stop wanting a specific exit */
|
2008-12-17 15:59:28 +01:00
|
|
|
conn->chosen_exit_optional = 0;
|
2008-02-21 10:00:54 +01:00
|
|
|
/* A non-zero chosen_exit_retries can happen if we set a
|
|
|
|
* TrackHostExits for this address under a port that the exit
|
|
|
|
* relay allows, but then try the same address with a different
|
|
|
|
* port that it doesn't allow to exit. We shouldn't unregister
|
|
|
|
* the mapping, since it is probably still wanted on the
|
|
|
|
* original port. But now we give away to the exit relay that
|
|
|
|
* we probably have a TrackHostExits on it. So be it. */
|
2008-12-17 15:59:28 +01:00
|
|
|
conn->chosen_exit_retries = 0;
|
2006-10-01 08:41:13 +02:00
|
|
|
tor_free(conn->chosen_exit_name); /* clears it */
|
2006-07-18 02:59:46 +02:00
|
|
|
}
|
2006-10-20 19:54:43 +02:00
|
|
|
if (connection_ap_detach_retriable(conn, circ, control_reason) >= 0)
|
2005-04-04 05:30:49 +02:00
|
|
|
return 0;
|
|
|
|
/* else, conn will get closed below */
|
|
|
|
break;
|
2013-03-12 04:37:47 +01:00
|
|
|
}
|
2006-07-18 02:59:46 +02:00
|
|
|
case END_STREAM_REASON_CONNECTREFUSED:
|
2008-12-17 15:59:28 +01:00
|
|
|
if (!conn->chosen_exit_optional)
|
2006-07-18 02:59:46 +02:00
|
|
|
break; /* break means it'll close, below */
|
|
|
|
/* Else fall through: expire this circuit, clear the
|
|
|
|
* chosen_exit_name field, and try again. */
|
2017-05-28 23:49:31 +02:00
|
|
|
/* Falls through. */
|
2005-04-04 05:30:49 +02:00
|
|
|
case END_STREAM_REASON_RESOLVEFAILED:
|
2006-07-18 02:59:46 +02:00
|
|
|
case END_STREAM_REASON_TIMEOUT:
|
2005-08-13 02:31:41 +02:00
|
|
|
case END_STREAM_REASON_MISC:
|
2010-08-04 00:51:39 +02:00
|
|
|
case END_STREAM_REASON_NOROUTE:
|
2005-04-04 05:30:49 +02:00
|
|
|
if (client_dns_incr_failures(conn->socks_request->address)
|
|
|
|
< MAX_RESOLVE_FAILURES) {
|
|
|
|
/* We haven't retried too many times; reattach the connection. */
|
2005-10-24 21:39:45 +02:00
|
|
|
circuit_log_path(LOG_INFO,LD_APP,circ);
|
2011-03-25 22:49:44 +01:00
|
|
|
/* Mark this circuit "unusable for new streams". */
|
2013-02-20 00:29:17 +01:00
|
|
|
mark_circuit_unusable_for_new_conns(circ);
|
2005-04-04 05:30:49 +02:00
|
|
|
|
2008-12-17 15:59:28 +01:00
|
|
|
if (conn->chosen_exit_optional) {
|
2006-07-27 07:03:57 +02:00
|
|
|
/* stop wanting a specific exit */
|
2008-12-17 15:59:28 +01:00
|
|
|
conn->chosen_exit_optional = 0;
|
2006-10-01 08:41:13 +02:00
|
|
|
tor_free(conn->chosen_exit_name); /* clears it */
|
2006-07-18 02:59:46 +02:00
|
|
|
}
|
2006-10-20 19:54:43 +02:00
|
|
|
if (connection_ap_detach_retriable(conn, circ, control_reason) >= 0)
|
2005-04-04 05:30:49 +02:00
|
|
|
return 0;
|
|
|
|
/* else, conn will get closed below */
|
|
|
|
} else {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_notice(LD_APP,
|
|
|
|
"Have tried resolving or connecting to address '%s' "
|
|
|
|
"at %d different places. Giving up.",
|
|
|
|
safe_str(conn->socks_request->address),
|
|
|
|
MAX_RESOLVE_FAILURES);
|
2005-08-15 05:35:15 +02:00
|
|
|
/* clear the failures, so it will have a full try next time */
|
|
|
|
client_dns_clear_failures(conn->socks_request->address);
|
2005-04-04 05:30:49 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case END_STREAM_REASON_HIBERNATING:
|
|
|
|
case END_STREAM_REASON_RESOURCELIMIT:
|
2005-06-29 23:46:55 +02:00
|
|
|
if (exitrouter) {
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
policies_set_node_exitpolicy_to_reject_all(exitrouter);
|
2005-06-29 23:46:55 +02:00
|
|
|
}
|
2008-12-17 15:59:28 +01:00
|
|
|
if (conn->chosen_exit_optional) {
|
2006-07-27 07:03:57 +02:00
|
|
|
/* stop wanting a specific exit */
|
2008-12-17 15:59:28 +01:00
|
|
|
conn->chosen_exit_optional = 0;
|
2006-10-01 08:41:13 +02:00
|
|
|
tor_free(conn->chosen_exit_name); /* clears it */
|
2006-07-18 02:59:46 +02:00
|
|
|
}
|
2006-10-20 19:54:43 +02:00
|
|
|
if (connection_ap_detach_retriable(conn, circ, control_reason) >= 0)
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
2005-04-04 05:30:49 +02:00
|
|
|
/* else, will close below */
|
|
|
|
break;
|
|
|
|
} /* end switch */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_APP,"Giving up on retrying; conn can't be handled.");
|
2005-04-04 05:30:49 +02:00
|
|
|
}
|
2005-03-14 04:12:59 +01:00
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_APP,
|
|
|
|
"Edge got end (%s) before we're connected. Marking for close.",
|
2008-06-11 02:17:02 +02:00
|
|
|
stream_end_reason_to_string(rh->length > 0 ? reason : -1));
|
2008-12-14 20:40:56 +01:00
|
|
|
circuit_log_path(LOG_INFO,LD_APP,circ);
|
2009-05-28 18:26:17 +02:00
|
|
|
/* need to test because of detach_retriable */
|
2011-07-20 18:55:42 +02:00
|
|
|
if (!ENTRY_TO_CONN(conn)->marked_for_close)
|
2008-12-14 20:40:56 +01:00
|
|
|
connection_mark_unattached_ap(conn, control_reason);
|
2005-04-04 05:30:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-12 04:37:47 +01:00
|
|
|
/** Called when we have gotten an END_REASON_EXITPOLICY failure on <b>circ</b>
|
|
|
|
* for <b>conn</b>, while attempting to connect via <b>node</b>. If the node
|
|
|
|
* told us which address it rejected, then <b>addr</b> is that address;
|
|
|
|
* otherwise it is AF_UNSPEC.
|
|
|
|
*
|
|
|
|
* If we are sure the node should have allowed this address, mark the node as
|
|
|
|
* having a reject *:* exit policy. Otherwise, mark the circuit as unusable
|
|
|
|
* for this particular address.
|
|
|
|
**/
|
|
|
|
static void
|
|
|
|
adjust_exit_policy_from_exitpolicy_failure(origin_circuit_t *circ,
|
|
|
|
entry_connection_t *conn,
|
|
|
|
node_t *node,
|
|
|
|
const tor_addr_t *addr)
|
|
|
|
{
|
|
|
|
int make_reject_all = 0;
|
|
|
|
const sa_family_t family = tor_addr_family(addr);
|
|
|
|
|
|
|
|
if (node) {
|
|
|
|
tor_addr_t tmp;
|
|
|
|
int asked_for_family = tor_addr_parse(&tmp, conn->socks_request->address);
|
|
|
|
if (family == AF_UNSPEC) {
|
|
|
|
make_reject_all = 1;
|
|
|
|
} else if (node_exit_policy_is_exact(node, family) &&
|
|
|
|
asked_for_family != -1 && !conn->chosen_exit_name) {
|
|
|
|
make_reject_all = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (make_reject_all) {
|
|
|
|
log_info(LD_APP,
|
|
|
|
"Exitrouter %s seems to be more restrictive than its exit "
|
|
|
|
"policy. Not using this router as exit for now.",
|
|
|
|
node_describe(node));
|
|
|
|
policies_set_node_exitpolicy_to_reject_all(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (family != AF_UNSPEC)
|
|
|
|
addr_policy_append_reject_addr(&circ->prepend_policy, addr);
|
|
|
|
}
|
|
|
|
|
2007-02-26 06:36:02 +01:00
|
|
|
/** Helper: change the socks_request->address field on conn to the
|
2012-10-31 23:27:48 +01:00
|
|
|
* dotted-quad representation of <b>new_addr</b>,
|
2007-02-26 06:36:02 +01:00
|
|
|
* and send an appropriate REMAP event. */
|
2007-02-23 21:13:02 +01:00
|
|
|
static void
|
2012-10-31 23:27:48 +01:00
|
|
|
remap_event_helper(entry_connection_t *conn, const tor_addr_t *new_addr)
|
2007-02-23 21:13:02 +01:00
|
|
|
{
|
2012-10-31 23:27:48 +01:00
|
|
|
tor_addr_to_str(conn->socks_request->address, new_addr,
|
|
|
|
sizeof(conn->socks_request->address),
|
2012-11-01 03:18:55 +01:00
|
|
|
1);
|
2007-02-23 21:13:02 +01:00
|
|
|
control_event_stream_status(conn, STREAM_EVENT_REMAP,
|
|
|
|
REMAP_STREAM_SOURCE_EXIT);
|
|
|
|
}
|
|
|
|
|
2012-11-15 04:55:23 +01:00
|
|
|
/** Extract the contents of a connected cell in <b>cell</b>, whose relay
|
|
|
|
* header has already been parsed into <b>rh</b>. On success, set
|
|
|
|
* <b>addr_out</b> to the address we're connected to, and <b>ttl_out</b> to
|
|
|
|
* the ttl of that address, in seconds, and return 0. On failure, return
|
|
|
|
* -1. */
|
2013-06-06 23:58:28 +02:00
|
|
|
STATIC int
|
2012-10-31 23:27:48 +01:00
|
|
|
connected_cell_parse(const relay_header_t *rh, const cell_t *cell,
|
|
|
|
tor_addr_t *addr_out, int *ttl_out)
|
|
|
|
{
|
|
|
|
uint32_t bytes;
|
|
|
|
const uint8_t *payload = cell->payload + RELAY_HEADER_SIZE;
|
|
|
|
|
|
|
|
tor_addr_make_unspec(addr_out);
|
|
|
|
*ttl_out = -1;
|
|
|
|
if (rh->length == 0)
|
|
|
|
return 0;
|
|
|
|
if (rh->length < 4)
|
|
|
|
return -1;
|
|
|
|
bytes = ntohl(get_uint32(payload));
|
|
|
|
|
|
|
|
/* If bytes is 0, this is maybe a v6 address. Otherwise it's a v4 address */
|
|
|
|
if (bytes != 0) {
|
|
|
|
/* v4 address */
|
|
|
|
tor_addr_from_ipv4h(addr_out, bytes);
|
|
|
|
if (rh->length >= 8) {
|
|
|
|
bytes = ntohl(get_uint32(payload + 4));
|
|
|
|
if (bytes <= INT32_MAX)
|
|
|
|
*ttl_out = bytes;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (rh->length < 25) /* 4 bytes of 0s, 1 addr, 16 ipv4, 4 ttl. */
|
|
|
|
return -1;
|
|
|
|
if (get_uint8(payload + 4) != 6)
|
|
|
|
return -1;
|
|
|
|
tor_addr_from_ipv6_bytes(addr_out, (char*)(payload + 5));
|
|
|
|
bytes = ntohl(get_uint32(payload + 21));
|
|
|
|
if (bytes <= INT32_MAX)
|
|
|
|
*ttl_out = (int) bytes;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-28 03:24:48 +01:00
|
|
|
/** Drop all storage held by <b>addr</b>. */
|
|
|
|
STATIC void
|
|
|
|
address_ttl_free(address_ttl_t *addr)
|
|
|
|
{
|
|
|
|
if (!addr)
|
|
|
|
return;
|
|
|
|
tor_free(addr->hostname);
|
|
|
|
tor_free(addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Parse a resolved cell in <b>cell</b>, with parsed header in <b>rh</b>.
|
|
|
|
* Return -1 on parse error. On success, add one or more newly allocated
|
|
|
|
* address_ttl_t to <b>addresses_out</b>; set *<b>errcode_out</b> to
|
|
|
|
* one of 0, RESOLVED_TYPE_ERROR, or RESOLVED_TYPE_ERROR_TRANSIENT, and
|
|
|
|
* return 0. */
|
|
|
|
STATIC int
|
|
|
|
resolved_cell_parse(const cell_t *cell, const relay_header_t *rh,
|
|
|
|
smartlist_t *addresses_out, int *errcode_out)
|
|
|
|
{
|
|
|
|
const uint8_t *cp;
|
|
|
|
uint8_t answer_type;
|
|
|
|
size_t answer_len;
|
|
|
|
address_ttl_t *addr;
|
|
|
|
size_t remaining;
|
|
|
|
int errcode = 0;
|
|
|
|
smartlist_t *addrs;
|
|
|
|
|
|
|
|
tor_assert(cell);
|
|
|
|
tor_assert(rh);
|
|
|
|
tor_assert(addresses_out);
|
|
|
|
tor_assert(errcode_out);
|
|
|
|
|
|
|
|
*errcode_out = 0;
|
|
|
|
|
|
|
|
if (rh->length > RELAY_PAYLOAD_SIZE)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
addrs = smartlist_new();
|
|
|
|
|
|
|
|
cp = cell->payload + RELAY_HEADER_SIZE;
|
|
|
|
|
|
|
|
remaining = rh->length;
|
|
|
|
while (remaining) {
|
|
|
|
const uint8_t *cp_orig = cp;
|
|
|
|
if (remaining < 2)
|
|
|
|
goto err;
|
|
|
|
answer_type = *cp++;
|
|
|
|
answer_len = *cp++;
|
|
|
|
if (remaining < 2 + answer_len + 4) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (answer_type == RESOLVED_TYPE_IPV4) {
|
|
|
|
if (answer_len != 4) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
addr = tor_malloc_zero(sizeof(*addr));
|
|
|
|
tor_addr_from_ipv4n(&addr->addr, get_uint32(cp));
|
|
|
|
cp += 4;
|
|
|
|
addr->ttl = ntohl(get_uint32(cp));
|
|
|
|
cp += 4;
|
|
|
|
smartlist_add(addrs, addr);
|
|
|
|
} else if (answer_type == RESOLVED_TYPE_IPV6) {
|
|
|
|
if (answer_len != 16)
|
|
|
|
goto err;
|
|
|
|
addr = tor_malloc_zero(sizeof(*addr));
|
|
|
|
tor_addr_from_ipv6_bytes(&addr->addr, (const char*) cp);
|
|
|
|
cp += 16;
|
|
|
|
addr->ttl = ntohl(get_uint32(cp));
|
|
|
|
cp += 4;
|
|
|
|
smartlist_add(addrs, addr);
|
|
|
|
} else if (answer_type == RESOLVED_TYPE_HOSTNAME) {
|
|
|
|
if (answer_len == 0) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
addr = tor_malloc_zero(sizeof(*addr));
|
|
|
|
addr->hostname = tor_memdup_nulterm(cp, answer_len);
|
|
|
|
cp += answer_len;
|
|
|
|
addr->ttl = ntohl(get_uint32(cp));
|
|
|
|
cp += 4;
|
|
|
|
smartlist_add(addrs, addr);
|
|
|
|
} else if (answer_type == RESOLVED_TYPE_ERROR_TRANSIENT ||
|
|
|
|
answer_type == RESOLVED_TYPE_ERROR) {
|
|
|
|
errcode = answer_type;
|
|
|
|
/* Ignore the error contents */
|
|
|
|
cp += answer_len + 4;
|
|
|
|
} else {
|
|
|
|
cp += answer_len + 4;
|
|
|
|
}
|
|
|
|
tor_assert(((ssize_t)remaining) >= (cp - cp_orig));
|
|
|
|
remaining -= (cp - cp_orig);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errcode && smartlist_len(addrs) == 0) {
|
|
|
|
/* Report an error only if there were no results. */
|
|
|
|
*errcode_out = errcode;
|
|
|
|
}
|
|
|
|
|
|
|
|
smartlist_add_all(addresses_out, addrs);
|
|
|
|
smartlist_free(addrs);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
/* On parse error, don't report any results */
|
|
|
|
SMARTLIST_FOREACH(addrs, address_ttl_t *, a, address_ttl_free(a));
|
|
|
|
smartlist_free(addrs);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Helper for connection_edge_process_resolved_cell: given an error code,
|
|
|
|
* an entry_connection, and a list of address_ttl_t *, report the best answer
|
|
|
|
* to the entry_connection. */
|
2014-04-02 19:38:50 +02:00
|
|
|
static void
|
|
|
|
connection_ap_handshake_socks_got_resolved_cell(entry_connection_t *conn,
|
|
|
|
int error_code,
|
|
|
|
smartlist_t *results)
|
2014-03-28 03:24:48 +01:00
|
|
|
{
|
|
|
|
address_ttl_t *addr_ipv4 = NULL;
|
|
|
|
address_ttl_t *addr_ipv6 = NULL;
|
|
|
|
address_ttl_t *addr_hostname = NULL;
|
|
|
|
address_ttl_t *addr_best = NULL;
|
|
|
|
|
|
|
|
/* If it's an error code, that's easy. */
|
|
|
|
if (error_code) {
|
|
|
|
tor_assert(error_code == RESOLVED_TYPE_ERROR ||
|
|
|
|
error_code == RESOLVED_TYPE_ERROR_TRANSIENT);
|
|
|
|
connection_ap_handshake_socks_resolved(conn,
|
|
|
|
error_code,0,NULL,-1,-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the first answer of each type. */
|
|
|
|
SMARTLIST_FOREACH_BEGIN(results, address_ttl_t *, addr) {
|
|
|
|
if (addr->hostname) {
|
|
|
|
if (!addr_hostname) {
|
|
|
|
addr_hostname = addr;
|
|
|
|
}
|
|
|
|
} else if (tor_addr_family(&addr->addr) == AF_INET) {
|
2015-01-03 22:11:23 +01:00
|
|
|
if (!addr_ipv4 && conn->entry_cfg.ipv4_traffic) {
|
2014-03-28 03:24:48 +01:00
|
|
|
addr_ipv4 = addr;
|
|
|
|
}
|
|
|
|
} else if (tor_addr_family(&addr->addr) == AF_INET6) {
|
2015-01-03 22:11:23 +01:00
|
|
|
if (!addr_ipv6 && conn->entry_cfg.ipv6_traffic) {
|
2014-03-28 03:24:48 +01:00
|
|
|
addr_ipv6 = addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(addr);
|
|
|
|
|
|
|
|
/* Now figure out which type we wanted to deliver. */
|
|
|
|
if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR) {
|
|
|
|
if (addr_hostname) {
|
|
|
|
connection_ap_handshake_socks_resolved(conn,
|
|
|
|
RESOLVED_TYPE_HOSTNAME,
|
|
|
|
strlen(addr_hostname->hostname),
|
|
|
|
(uint8_t*)addr_hostname->hostname,
|
|
|
|
addr_hostname->ttl,-1);
|
|
|
|
} else {
|
|
|
|
connection_ap_handshake_socks_resolved(conn,
|
|
|
|
RESOLVED_TYPE_ERROR,0,NULL,-1,-1);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-03 22:11:23 +01:00
|
|
|
if (conn->entry_cfg.prefer_ipv6) {
|
2014-03-28 03:24:48 +01:00
|
|
|
addr_best = addr_ipv6 ? addr_ipv6 : addr_ipv4;
|
|
|
|
} else {
|
|
|
|
addr_best = addr_ipv4 ? addr_ipv4 : addr_ipv6;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now convert it to the ugly old interface */
|
|
|
|
if (! addr_best) {
|
|
|
|
connection_ap_handshake_socks_resolved(conn,
|
|
|
|
RESOLVED_TYPE_ERROR,0,NULL,-1,-1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
connection_ap_handshake_socks_resolved_addr(conn,
|
|
|
|
&addr_best->addr,
|
|
|
|
addr_best->ttl,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
remap_event_helper(conn, &addr_best->addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Handle a RELAY_COMMAND_RESOLVED cell that we received on a non-open AP
|
|
|
|
* stream. */
|
2014-04-02 19:38:50 +02:00
|
|
|
STATIC int
|
2014-03-28 00:58:06 +01:00
|
|
|
connection_edge_process_resolved_cell(edge_connection_t *conn,
|
|
|
|
const cell_t *cell,
|
|
|
|
const relay_header_t *rh)
|
|
|
|
{
|
|
|
|
entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
|
2014-03-28 03:24:48 +01:00
|
|
|
smartlist_t *resolved_addresses = NULL;
|
|
|
|
int errcode = 0;
|
|
|
|
|
2014-03-28 00:58:06 +01:00
|
|
|
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.");
|
2014-03-28 03:24:48 +01:00
|
|
|
return 0;
|
2014-03-28 00:58:06 +01:00
|
|
|
}
|
|
|
|
tor_assert(SOCKS_COMMAND_IS_RESOLVE(entry_conn->socks_request->command));
|
2014-03-28 03:24:48 +01:00
|
|
|
|
|
|
|
resolved_addresses = smartlist_new();
|
|
|
|
if (resolved_cell_parse(cell, rh, resolved_addresses, &errcode)) {
|
2014-03-28 00:58:06 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
|
|
|
"Dropping malformed 'resolved' cell");
|
|
|
|
connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TORPROTOCOL);
|
2014-03-28 03:24:48 +01:00
|
|
|
goto done;
|
2014-03-28 00:58:06 +01:00
|
|
|
}
|
|
|
|
|
2014-03-28 03:24:48 +01:00
|
|
|
if (get_options()->ClientDNSRejectInternalAddresses) {
|
|
|
|
int orig_len = smartlist_len(resolved_addresses);
|
|
|
|
SMARTLIST_FOREACH_BEGIN(resolved_addresses, address_ttl_t *, addr) {
|
|
|
|
if (addr->hostname == NULL && tor_addr_is_internal(&addr->addr, 0)) {
|
|
|
|
log_info(LD_APP, "Got a resolved cell with answer %s; dropping that "
|
|
|
|
"answer.",
|
|
|
|
safe_str_client(fmt_addr(&addr->addr)));
|
|
|
|
address_ttl_free(addr);
|
|
|
|
SMARTLIST_DEL_CURRENT(resolved_addresses, addr);
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(addr);
|
|
|
|
if (orig_len && smartlist_len(resolved_addresses) == 0) {
|
|
|
|
log_info(LD_APP, "Got a resolved cell with only private addresses; "
|
|
|
|
"dropping it.");
|
2014-03-28 00:58:06 +01:00
|
|
|
connection_ap_handshake_socks_resolved(entry_conn,
|
|
|
|
RESOLVED_TYPE_ERROR_TRANSIENT,
|
|
|
|
0, NULL, 0, TIME_MAX);
|
|
|
|
connection_mark_unattached_ap(entry_conn,
|
|
|
|
END_STREAM_REASON_TORPROTOCOL);
|
2014-03-28 03:24:48 +01:00
|
|
|
goto done;
|
2014-03-28 00:58:06 +01:00
|
|
|
}
|
|
|
|
}
|
2014-03-28 03:24:48 +01:00
|
|
|
|
2014-04-02 19:38:50 +02:00
|
|
|
connection_ap_handshake_socks_got_resolved_cell(entry_conn,
|
|
|
|
errcode,
|
|
|
|
resolved_addresses);
|
2014-03-28 03:24:48 +01:00
|
|
|
|
2014-03-28 00:58:06 +01:00
|
|
|
connection_mark_unattached_ap(entry_conn,
|
|
|
|
END_STREAM_REASON_DONE |
|
|
|
|
END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
|
|
|
|
|
2014-03-28 03:24:48 +01:00
|
|
|
done:
|
|
|
|
SMARTLIST_FOREACH(resolved_addresses, address_ttl_t *, addr,
|
|
|
|
address_ttl_free(addr));
|
|
|
|
smartlist_free(resolved_addresses);
|
|
|
|
return 0;
|
2014-03-28 00:58:06 +01:00
|
|
|
}
|
|
|
|
|
2005-04-04 05:30:49 +02:00
|
|
|
/** An incoming relay cell has arrived from circuit <b>circ</b> to
|
|
|
|
* stream <b>conn</b>.
|
|
|
|
*
|
|
|
|
* The arguments here are the same as in
|
|
|
|
* connection_edge_process_relay_cell() below; this function is called
|
|
|
|
* from there when <b>conn</b> is defined and not in an open state.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
connection_edge_process_relay_cell_not_open(
|
|
|
|
relay_header_t *rh, cell_t *cell, circuit_t *circ,
|
2006-07-26 21:07:26 +02:00
|
|
|
edge_connection_t *conn, crypt_path_t *layer_hint)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2006-07-23 09:37:35 +02:00
|
|
|
if (rh->command == RELAY_COMMAND_END) {
|
2012-10-12 18:22:13 +02:00
|
|
|
if (CIRCUIT_IS_ORIGIN(circ) && conn->base_.type == CONN_TYPE_AP) {
|
2008-12-14 20:40:56 +01:00
|
|
|
return connection_ap_process_end_not_open(rh, cell,
|
2011-07-20 18:55:42 +02:00
|
|
|
TO_ORIGIN_CIRCUIT(circ),
|
|
|
|
EDGE_TO_ENTRY_CONN(conn),
|
2008-12-14 20:40:56 +01:00
|
|
|
layer_hint);
|
|
|
|
} else {
|
|
|
|
/* we just got an 'end', don't need to send one */
|
2008-12-17 15:59:28 +01:00
|
|
|
conn->edge_has_sent_end = 1;
|
2008-12-14 20:40:56 +01:00
|
|
|
conn->end_reason = *(cell->payload+RELAY_HEADER_SIZE) |
|
|
|
|
END_STREAM_REASON_FLAG_REMOTE;
|
|
|
|
connection_mark_for_close(TO_CONN(conn));
|
2006-07-23 09:37:35 +02:00
|
|
|
return 0;
|
2008-12-14 20:40:56 +01:00
|
|
|
}
|
2006-07-23 09:37:35 +02:00
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2012-10-12 18:22:13 +02:00
|
|
|
if (conn->base_.type == CONN_TYPE_AP &&
|
2006-07-26 21:07:37 +02:00
|
|
|
rh->command == RELAY_COMMAND_CONNECTED) {
|
2012-10-31 23:27:48 +01:00
|
|
|
tor_addr_t addr;
|
|
|
|
int ttl;
|
2011-07-20 18:55:42 +02:00
|
|
|
entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
|
2006-07-23 09:37:35 +02:00
|
|
|
tor_assert(CIRCUIT_IS_ORIGIN(circ));
|
2012-10-12 18:22:13 +02:00
|
|
|
if (conn->base_.state != AP_CONN_STATE_CONNECT_WAIT) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_APP,
|
|
|
|
"Got 'connected' while not in state connect_wait. Dropping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2015-11-27 18:54:57 +01:00
|
|
|
CONNECTION_AP_EXPECT_NONPENDING(entry_conn);
|
2012-10-12 18:22:13 +02:00
|
|
|
conn->base_.state = AP_CONN_STATE_OPEN;
|
2015-08-17 20:34:50 +02:00
|
|
|
log_info(LD_APP,"'connected' received for circid %u streamid %d "
|
|
|
|
"after %d seconds.",
|
|
|
|
(unsigned)circ->n_circ_id,
|
|
|
|
rh->stream_id,
|
2012-10-12 18:22:13 +02:00
|
|
|
(int)(time(NULL) - conn->base_.timestamp_lastread));
|
2012-10-31 23:27:48 +01:00
|
|
|
if (connected_cell_parse(rh, cell, &addr, &ttl) < 0) {
|
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_APP,
|
|
|
|
"Got a badly formatted connected cell. Closing.");
|
|
|
|
connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
|
|
|
|
connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TORPROTOCOL);
|
2018-05-14 21:54:48 +02:00
|
|
|
return 0;
|
2012-10-31 23:27:48 +01:00
|
|
|
}
|
|
|
|
if (tor_addr_family(&addr) != AF_UNSPEC) {
|
|
|
|
const sa_family_t family = tor_addr_family(&addr);
|
|
|
|
if (tor_addr_is_null(&addr) ||
|
|
|
|
(get_options()->ClientDNSRejectInternalAddresses &&
|
|
|
|
tor_addr_is_internal(&addr, 0))) {
|
2010-10-02 03:31:09 +02:00
|
|
|
log_info(LD_APP, "...but it claims the IP address was %s. Closing.",
|
2012-10-31 23:27:48 +01:00
|
|
|
fmt_addr(&addr));
|
2007-03-24 16:57:51 +01:00
|
|
|
connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
|
2011-07-20 18:55:42 +02:00
|
|
|
connection_mark_unattached_ap(entry_conn,
|
|
|
|
END_STREAM_REASON_TORPROTOCOL);
|
2004-08-15 22:05:35 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2012-10-31 23:27:48 +01:00
|
|
|
|
2015-01-03 22:11:23 +01:00
|
|
|
if ((family == AF_INET && ! entry_conn->entry_cfg.ipv4_traffic) ||
|
|
|
|
(family == AF_INET6 && ! entry_conn->entry_cfg.ipv6_traffic)) {
|
2012-10-31 23:27:48 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_APP,
|
|
|
|
"Got a connected cell to %s with unsupported address family."
|
|
|
|
" Closing.", fmt_addr(&addr));
|
|
|
|
connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
|
|
|
|
connection_mark_unattached_ap(entry_conn,
|
|
|
|
END_STREAM_REASON_TORPROTOCOL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-25 19:45:36 +01:00
|
|
|
client_dns_set_addressmap(entry_conn,
|
2012-10-31 23:27:48 +01:00
|
|
|
entry_conn->socks_request->address, &addr,
|
2011-07-20 18:55:42 +02:00
|
|
|
entry_conn->chosen_exit_name, ttl);
|
2007-02-23 21:13:02 +01:00
|
|
|
|
2012-10-31 23:27:48 +01:00
|
|
|
remap_event_helper(entry_conn, &addr);
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
2006-07-23 09:37:35 +02:00
|
|
|
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
|
2006-08-10 11:01:37 +02:00
|
|
|
/* don't send a socks reply to transparent conns */
|
2011-11-30 05:34:33 +01:00
|
|
|
tor_assert(entry_conn->socks_request != NULL);
|
2011-07-20 18:55:42 +02:00
|
|
|
if (!entry_conn->socks_request->has_finished)
|
|
|
|
connection_ap_handshake_socks_reply(entry_conn, NULL, 0, 0);
|
2008-06-07 07:27:34 +02:00
|
|
|
|
|
|
|
/* Was it a linked dir conn? If so, a dir request just started to
|
|
|
|
* fetch something; this could be a bootstrap status milestone. */
|
|
|
|
log_debug(LD_APP, "considering");
|
|
|
|
if (TO_CONN(conn)->linked_conn &&
|
|
|
|
TO_CONN(conn)->linked_conn->type == CONN_TYPE_DIR) {
|
|
|
|
connection_t *dirconn = TO_CONN(conn)->linked_conn;
|
|
|
|
log_debug(LD_APP, "it is! %d", dirconn->purpose);
|
|
|
|
switch (dirconn->purpose) {
|
|
|
|
case DIR_PURPOSE_FETCH_CERTIFICATE:
|
|
|
|
if (consensus_is_waiting_for_certs())
|
|
|
|
control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_KEYS, 0);
|
|
|
|
break;
|
|
|
|
case DIR_PURPOSE_FETCH_CONSENSUS:
|
|
|
|
control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_STATUS, 0);
|
|
|
|
break;
|
|
|
|
case DIR_PURPOSE_FETCH_SERVERDESC:
|
2013-10-08 17:31:08 +02:00
|
|
|
case DIR_PURPOSE_FETCH_MICRODESC:
|
2013-10-08 17:50:53 +02:00
|
|
|
if (TO_DIR_CONN(dirconn)->router_purpose == ROUTER_PURPOSE_GENERAL)
|
|
|
|
control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
|
|
|
|
count_loading_descriptors_progress());
|
2008-06-07 07:27:34 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-07-18 21:38:05 +02:00
|
|
|
/* This is definitely a success, so forget about any pending data we
|
|
|
|
* had sent. */
|
2011-07-20 18:55:42 +02:00
|
|
|
if (entry_conn->pending_optimistic_data) {
|
2016-08-02 19:40:19 +02:00
|
|
|
buf_free(entry_conn->pending_optimistic_data);
|
2011-07-20 18:55:42 +02:00
|
|
|
entry_conn->pending_optimistic_data = NULL;
|
2011-07-18 21:38:05 +02:00
|
|
|
}
|
2008-06-07 07:27:34 +02:00
|
|
|
|
2004-05-13 09:24:49 +02:00
|
|
|
/* handle anything that might have queued */
|
2010-09-13 22:05:22 +02:00
|
|
|
if (connection_edge_package_raw_inbuf(conn, 1, NULL) < 0) {
|
2005-03-02 04:13:05 +01:00
|
|
|
/* (We already sent an end cell if possible) */
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_mark_for_close(TO_CONN(conn));
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-12 18:22:13 +02:00
|
|
|
if (conn->base_.type == CONN_TYPE_AP &&
|
2006-07-26 21:07:37 +02:00
|
|
|
rh->command == RELAY_COMMAND_RESOLVED) {
|
2014-03-28 03:24:48 +01:00
|
|
|
return connection_edge_process_resolved_cell(conn, cell, rh);
|
2004-06-17 20:13:09 +02:00
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2005-11-15 21:40:32 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
2005-12-10 10:36:26 +01:00
|
|
|
"Got an unexpected relay command %d, in state %d (%s). Dropping.",
|
2012-10-12 18:22:13 +02:00
|
|
|
rh->command, conn->base_.state,
|
|
|
|
conn_state_to_string(conn->base_.type, conn->base_.state));
|
2005-11-17 04:40:20 +01:00
|
|
|
return 0; /* for forward compatibility, don't kill the circuit */
|
2007-03-24 16:57:51 +01:00
|
|
|
// connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
|
2005-11-17 04:40:20 +01:00
|
|
|
// connection_mark_for_close(conn);
|
|
|
|
// return -1;
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** An incoming relay cell has arrived on circuit <b>circ</b>. If
|
|
|
|
* <b>conn</b> is NULL this is a control cell, else <b>cell</b> is
|
|
|
|
* destined for <b>conn</b>.
|
|
|
|
*
|
|
|
|
* If <b>layer_hint</b> is defined, then we're the origin of the
|
|
|
|
* circuit, and it specifies the hop that packaged <b>cell</b>.
|
|
|
|
*
|
2006-01-05 22:23:03 +01:00
|
|
|
* Return -reason if you want to warn and tear down the circuit, else 0.
|
2004-05-13 09:24:49 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
|
2006-07-26 21:07:26 +02:00
|
|
|
edge_connection_t *conn,
|
2004-05-13 09:24:49 +02:00
|
|
|
crypt_path_t *layer_hint)
|
|
|
|
{
|
|
|
|
static int num_seen=0;
|
|
|
|
relay_header_t rh;
|
2005-10-25 09:04:36 +02:00
|
|
|
unsigned domain = layer_hint?LD_APP:LD_EXIT;
|
2006-01-05 22:23:03 +01:00
|
|
|
int reason;
|
2011-03-15 21:59:58 +01:00
|
|
|
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. */
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(cell);
|
|
|
|
tor_assert(circ);
|
2004-05-13 09:24:49 +02:00
|
|
|
|
|
|
|
relay_header_unpack(&rh, cell->payload);
|
|
|
|
// log_fn(LOG_DEBUG,"command %d stream %d", rh.command, rh.stream_id);
|
|
|
|
num_seen++;
|
2009-06-20 09:21:52 +02:00
|
|
|
log_debug(domain, "Now seen %d relay cells here (command %d, stream %d).",
|
|
|
|
num_seen, rh.command, rh.stream_id);
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2005-05-17 22:00:24 +02:00
|
|
|
if (rh.length > RELAY_PAYLOAD_SIZE) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
|
|
|
"Relay cell length field too long. Closing circuit.");
|
2006-01-05 22:23:03 +01:00
|
|
|
return - END_CIRC_REASON_TORPROTOCOL;
|
2005-05-17 22:00:24 +02:00
|
|
|
}
|
|
|
|
|
2013-01-14 20:02:13 +01:00
|
|
|
if (rh.stream_id == 0) {
|
|
|
|
switch (rh.command) {
|
|
|
|
case RELAY_COMMAND_BEGIN:
|
|
|
|
case RELAY_COMMAND_CONNECTED:
|
|
|
|
case RELAY_COMMAND_END:
|
|
|
|
case RELAY_COMMAND_RESOLVE:
|
|
|
|
case RELAY_COMMAND_RESOLVED:
|
|
|
|
case RELAY_COMMAND_BEGIN_DIR:
|
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Relay command %d with zero "
|
|
|
|
"stream_id. Dropping.", (int)rh.command);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-13 09:24:49 +02:00
|
|
|
/* either conn is NULL, in which case we've got a control cell, or else
|
|
|
|
* conn points to the recognized stream. */
|
|
|
|
|
Implement server side of Proposal 174 (optimistic data)
Ian's original message:
The current code actually correctly handles queued data at the
Exit; if there is queued data in a EXIT_CONN_STATE_CONNECTING
stream, that data will be immediately sent when the connection
succeeds. If the connection fails, the data will be correctly
ignored and freed. The problem with the current server code is
that the server currently drops DATA cells on streams in the
EXIT_CONN_STATE_CONNECTING state. Also, if you try to queue data
in the EXIT_CONN_STATE_RESOLVING state, bad things happen because
streams in that state don't yet have conn->write_event set, and so
some existing sanity checks (any stream with queued data is at
least potentially writable) are no longer sound.
The solution is to simply not drop received DATA cells while in
the EXIT_CONN_STATE_CONNECTING state. Also do not send SENDME
cells in this state, so that the OP cannot send more than one
window's worth of data to be queued at the Exit. Finally, patch
the sanity checks so that streams in the EXIT_CONN_STATE_RESOLVING
state that have buffered data can pass.
[...] Here is a simple patch. It seems to work with both regular
streams and hidden services, but there may be other corner cases
I'm not aware of. (Do streams used for directory fetches, hidden
services, etc. take a different code path?)
2011-03-14 23:05:33 +01:00
|
|
|
if (conn && !connection_state_is_open(TO_CONN(conn))) {
|
2012-10-12 18:22:13 +02:00
|
|
|
if (conn->base_.type == CONN_TYPE_EXIT &&
|
|
|
|
(conn->base_.state == EXIT_CONN_STATE_CONNECTING ||
|
|
|
|
conn->base_.state == EXIT_CONN_STATE_RESOLVING) &&
|
2011-03-15 21:59:58 +01:00
|
|
|
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;
|
2014-06-04 18:27:10 +02:00
|
|
|
} else if (rh.stream_id == 0 && rh.command == RELAY_COMMAND_DATA) {
|
|
|
|
log_warn(LD_BUG, "Somehow I had a connection that matched a "
|
|
|
|
"data cell with stream ID 0.");
|
Implement server side of Proposal 174 (optimistic data)
Ian's original message:
The current code actually correctly handles queued data at the
Exit; if there is queued data in a EXIT_CONN_STATE_CONNECTING
stream, that data will be immediately sent when the connection
succeeds. If the connection fails, the data will be correctly
ignored and freed. The problem with the current server code is
that the server currently drops DATA cells on streams in the
EXIT_CONN_STATE_CONNECTING state. Also, if you try to queue data
in the EXIT_CONN_STATE_RESOLVING state, bad things happen because
streams in that state don't yet have conn->write_event set, and so
some existing sanity checks (any stream with queued data is at
least potentially writable) are no longer sound.
The solution is to simply not drop received DATA cells while in
the EXIT_CONN_STATE_CONNECTING state. Also do not send SENDME
cells in this state, so that the OP cannot send more than one
window's worth of data to be queued at the Exit. Finally, patch
the sanity checks so that streams in the EXIT_CONN_STATE_RESOLVING
state that have buffered data can pass.
[...] Here is a simple patch. It seems to work with both regular
streams and hidden services, but there may be other corner cases
I'm not aware of. (Do streams used for directory fetches, hidden
services, etc. take a different code path?)
2011-03-14 23:05:33 +01:00
|
|
|
} else {
|
2011-03-15 21:59:58 +01:00
|
|
|
return connection_edge_process_relay_cell_not_open(
|
|
|
|
&rh, cell, circ, conn, layer_hint);
|
Implement server side of Proposal 174 (optimistic data)
Ian's original message:
The current code actually correctly handles queued data at the
Exit; if there is queued data in a EXIT_CONN_STATE_CONNECTING
stream, that data will be immediately sent when the connection
succeeds. If the connection fails, the data will be correctly
ignored and freed. The problem with the current server code is
that the server currently drops DATA cells on streams in the
EXIT_CONN_STATE_CONNECTING state. Also, if you try to queue data
in the EXIT_CONN_STATE_RESOLVING state, bad things happen because
streams in that state don't yet have conn->write_event set, and so
some existing sanity checks (any stream with queued data is at
least potentially writable) are no longer sound.
The solution is to simply not drop received DATA cells while in
the EXIT_CONN_STATE_CONNECTING state. Also do not send SENDME
cells in this state, so that the OP cannot send more than one
window's worth of data to be queued at the Exit. Finally, patch
the sanity checks so that streams in the EXIT_CONN_STATE_RESOLVING
state that have buffered data can pass.
[...] Here is a simple patch. It seems to work with both regular
streams and hidden services, but there may be other corner cases
I'm not aware of. (Do streams used for directory fetches, hidden
services, etc. take a different code path?)
2011-03-14 23:05:33 +01:00
|
|
|
}
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
switch (rh.command) {
|
2004-05-13 09:24:49 +02:00
|
|
|
case RELAY_COMMAND_DROP:
|
2006-09-22 02:24:27 +02:00
|
|
|
// log_info(domain,"Got a relay-level padding cell. Dropping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
case RELAY_COMMAND_BEGIN:
|
2006-09-29 05:50:11 +02:00
|
|
|
case RELAY_COMMAND_BEGIN_DIR:
|
2004-05-13 09:24:49 +02:00
|
|
|
if (layer_hint &&
|
|
|
|
circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_APP,
|
|
|
|
"Relay begin request unsupported at AP. Dropping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2009-02-21 20:07:01 +01:00
|
|
|
if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED &&
|
|
|
|
layer_hint != TO_ORIGIN_CIRCUIT(circ)->cpath->prev) {
|
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_APP,
|
|
|
|
"Relay begin request to Hidden Service "
|
|
|
|
"from intermediary node. Dropping.");
|
|
|
|
return 0;
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (conn) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, domain,
|
|
|
|
"Begin cell for known stream. Dropping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2017-06-05 17:11:42 +02:00
|
|
|
if (rh.command == RELAY_COMMAND_BEGIN_DIR &&
|
|
|
|
circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) {
|
2009-07-12 16:33:31 +02:00
|
|
|
/* Assign this circuit and its app-ward OR connection a unique ID,
|
|
|
|
* so that we can measure download times. The local edge and dir
|
|
|
|
* connection will be assigned the same ID when they are created
|
|
|
|
* and linked. */
|
|
|
|
static uint64_t next_id = 0;
|
2009-07-14 22:24:50 +02:00
|
|
|
circ->dirreq_id = ++next_id;
|
2012-10-09 09:51:33 +02:00
|
|
|
TO_OR_CIRCUIT(circ)->p_chan->dirreq_id = circ->dirreq_id;
|
2009-07-12 16:33:31 +02:00
|
|
|
}
|
|
|
|
|
2006-10-20 17:26:02 +02:00
|
|
|
return connection_exit_begin_conn(cell, circ);
|
2004-05-13 09:24:49 +02:00
|
|
|
case RELAY_COMMAND_DATA:
|
|
|
|
++stats_n_data_cells_received;
|
2004-11-28 12:39:53 +01:00
|
|
|
if (( layer_hint && --layer_hint->deliver_window < 0) ||
|
|
|
|
(!layer_hint && --circ->deliver_window < 0)) {
|
2005-10-25 09:04:36 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
2005-10-17 03:29:28 +02:00
|
|
|
"(relay data) circ deliver_window below 0. Killing.");
|
2012-03-31 20:17:41 +02:00
|
|
|
if (conn) {
|
|
|
|
/* XXXX Do we actually need to do this? Will killing the circuit
|
|
|
|
* not send an END and mark the stream for close as appropriate? */
|
|
|
|
connection_edge_end(conn, END_STREAM_REASON_TORPROTOCOL);
|
|
|
|
connection_mark_for_close(TO_CONN(conn));
|
|
|
|
}
|
2006-01-05 22:23:03 +01:00
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(domain,"circ deliver_window now %d.", layer_hint ?
|
|
|
|
layer_hint->deliver_window : circ->deliver_window);
|
2004-05-13 09:24:49 +02:00
|
|
|
|
|
|
|
circuit_consider_sending_sendme(circ, layer_hint);
|
|
|
|
|
2013-03-22 19:57:58 +01:00
|
|
|
if (rh.stream_id == 0) {
|
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Relay data cell with zero "
|
|
|
|
"stream_id. Dropping.");
|
|
|
|
return 0;
|
|
|
|
} else if (!conn) {
|
2009-03-11 21:51:47 +01:00
|
|
|
log_info(domain,"data cell dropped, unknown stream (streamid %d).",
|
|
|
|
rh.stream_id);
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (--conn->deliver_window < 0) { /* is it below 0 after decrement? */
|
2005-10-25 09:04:36 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
2005-10-17 03:29:28 +02:00
|
|
|
"(relay data) conn deliver_window below 0. Killing.");
|
2006-01-05 22:23:03 +01:00
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
stats_n_data_bytes_received += rh.length;
|
2010-12-14 01:34:01 +01:00
|
|
|
connection_write_to_buf((char*)(cell->payload + RELAY_HEADER_SIZE),
|
2006-07-26 21:07:26 +02:00
|
|
|
rh.length, TO_CONN(conn));
|
2011-03-14 23:08:27 +01:00
|
|
|
|
Implement server side of Proposal 174 (optimistic data)
Ian's original message:
The current code actually correctly handles queued data at the
Exit; if there is queued data in a EXIT_CONN_STATE_CONNECTING
stream, that data will be immediately sent when the connection
succeeds. If the connection fails, the data will be correctly
ignored and freed. The problem with the current server code is
that the server currently drops DATA cells on streams in the
EXIT_CONN_STATE_CONNECTING state. Also, if you try to queue data
in the EXIT_CONN_STATE_RESOLVING state, bad things happen because
streams in that state don't yet have conn->write_event set, and so
some existing sanity checks (any stream with queued data is at
least potentially writable) are no longer sound.
The solution is to simply not drop received DATA cells while in
the EXIT_CONN_STATE_CONNECTING state. Also do not send SENDME
cells in this state, so that the OP cannot send more than one
window's worth of data to be queued at the Exit. Finally, patch
the sanity checks so that streams in the EXIT_CONN_STATE_RESOLVING
state that have buffered data can pass.
[...] Here is a simple patch. It seems to work with both regular
streams and hidden services, but there may be other corner cases
I'm not aware of. (Do streams used for directory fetches, hidden
services, etc. take a different code path?)
2011-03-14 23:05:33 +01:00
|
|
|
if (!optimistic_data) {
|
2011-03-14 23:08:27 +01:00
|
|
|
/* 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);
|
Implement server side of Proposal 174 (optimistic data)
Ian's original message:
The current code actually correctly handles queued data at the
Exit; if there is queued data in a EXIT_CONN_STATE_CONNECTING
stream, that data will be immediately sent when the connection
succeeds. If the connection fails, the data will be correctly
ignored and freed. The problem with the current server code is
that the server currently drops DATA cells on streams in the
EXIT_CONN_STATE_CONNECTING state. Also, if you try to queue data
in the EXIT_CONN_STATE_RESOLVING state, bad things happen because
streams in that state don't yet have conn->write_event set, and so
some existing sanity checks (any stream with queued data is at
least potentially writable) are no longer sound.
The solution is to simply not drop received DATA cells while in
the EXIT_CONN_STATE_CONNECTING state. Also do not send SENDME
cells in this state, so that the OP cannot send more than one
window's worth of data to be queued at the Exit. Finally, patch
the sanity checks so that streams in the EXIT_CONN_STATE_RESOLVING
state that have buffered data can pass.
[...] Here is a simple patch. It seems to work with both regular
streams and hidden services, but there may be other corner cases
I'm not aware of. (Do streams used for directory fetches, hidden
services, etc. take a different code path?)
2011-03-14 23:05:33 +01:00
|
|
|
}
|
2011-03-14 23:08:27 +01:00
|
|
|
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
case RELAY_COMMAND_END:
|
2006-10-20 19:54:48 +02:00
|
|
|
reason = rh.length > 0 ?
|
2010-12-14 01:34:01 +01:00
|
|
|
get_uint8(cell->payload+RELAY_HEADER_SIZE) : END_STREAM_REASON_MISC;
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!conn) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(domain,"end cell (%s) dropped, unknown stream.",
|
2008-06-11 02:17:02 +02:00
|
|
|
stream_end_reason_to_string(reason));
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* XXX add to this log_fn the exit node's nickname? */
|
2012-11-02 19:22:21 +01:00
|
|
|
log_info(domain,TOR_SOCKET_T_FORMAT": end cell (%s) for stream %d. "
|
|
|
|
"Removing stream.",
|
2012-10-12 18:22:13 +02:00
|
|
|
conn->base_.s,
|
2008-06-11 02:17:02 +02:00
|
|
|
stream_end_reason_to_string(reason),
|
2006-02-13 11:33:00 +01:00
|
|
|
conn->stream_id);
|
2012-10-12 18:22:13 +02:00
|
|
|
if (conn->base_.type == CONN_TYPE_AP) {
|
2011-07-20 18:55:42 +02:00
|
|
|
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.");
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
/* We just *got* an end; no reason to send one. */
|
2008-12-17 15:59:28 +01:00
|
|
|
conn->edge_has_sent_end = 1;
|
2006-10-20 19:54:48 +02:00
|
|
|
if (!conn->end_reason)
|
|
|
|
conn->end_reason = reason | END_STREAM_REASON_FLAG_REMOTE;
|
2012-10-12 18:22:13 +02:00
|
|
|
if (!conn->base_.marked_for_close) {
|
2004-08-07 04:19:49 +02:00
|
|
|
/* only mark it if not already marked. it's possible to
|
|
|
|
* get the 'end' right around when the client hangs up on us. */
|
2009-08-09 03:53:24 +02:00
|
|
|
connection_mark_and_flush(TO_CONN(conn));
|
2004-08-07 04:19:49 +02:00
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
2012-12-06 06:28:01 +01:00
|
|
|
case RELAY_COMMAND_EXTEND:
|
|
|
|
case RELAY_COMMAND_EXTEND2: {
|
2011-11-21 23:51:46 +01:00
|
|
|
static uint64_t total_n_extend=0, total_nonearly=0;
|
|
|
|
total_n_extend++;
|
2012-08-15 19:16:41 +02:00
|
|
|
if (rh.stream_id) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, domain,
|
|
|
|
"'extend' cell received for non-zero stream. Dropping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2011-12-22 16:12:49 +01:00
|
|
|
if (cell->command != CELL_RELAY_EARLY &&
|
|
|
|
!networkstatus_get_param(NULL,"AllowNonearlyExtend",0,0,1)) {
|
2011-12-27 00:11:41 +01:00
|
|
|
#define EARLY_WARNING_INTERVAL 3600
|
2011-10-28 16:51:21 +02:00
|
|
|
static ratelim_t early_warning_limit =
|
|
|
|
RATELIM_INIT(EARLY_WARNING_INTERVAL);
|
|
|
|
char *m;
|
|
|
|
if (cell->command == CELL_RELAY) {
|
2011-11-21 23:51:46 +01:00
|
|
|
++total_nonearly;
|
2011-10-28 16:51:21 +02:00
|
|
|
if ((m = rate_limit_log(&early_warning_limit, approx_time()))) {
|
2011-11-21 23:51:46 +01:00
|
|
|
double percentage = ((double)total_nonearly)/total_n_extend;
|
|
|
|
percentage *= 100;
|
2011-12-27 00:11:41 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, domain, "EXTEND cell received, "
|
2011-10-28 16:51:21 +02:00
|
|
|
"but not via RELAY_EARLY. Dropping.%s", m);
|
2011-12-27 00:11:41 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, domain, " (We have dropped %.02f%% of "
|
|
|
|
"all EXTEND cells for this reason)", percentage);
|
2011-10-28 16:51:21 +02:00
|
|
|
tor_free(m);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_fn(LOG_WARN, domain,
|
|
|
|
"EXTEND cell received, in a cell with type %d! Dropping.",
|
|
|
|
cell->command);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
return circuit_extend(cell, circ);
|
2011-11-21 23:51:46 +01:00
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
case RELAY_COMMAND_EXTENDED:
|
2012-12-06 06:28:01 +01:00
|
|
|
case RELAY_COMMAND_EXTENDED2:
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!layer_hint) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
|
|
|
"'extended' unsupported at non-origin. Dropping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(domain,"Got an extended cell! Yay.");
|
2012-12-06 04:34:49 +01:00
|
|
|
{
|
|
|
|
extended_cell_t extended_cell;
|
|
|
|
if (extended_cell_parse(&extended_cell, rh.command,
|
|
|
|
(const uint8_t*)cell->payload+RELAY_HEADER_SIZE,
|
|
|
|
rh.length)<0) {
|
|
|
|
log_warn(LD_PROTOCOL,
|
|
|
|
"Can't parse EXTENDED cell; killing circuit.");
|
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
|
|
|
}
|
|
|
|
if ((reason = circuit_finish_handshake(TO_ORIGIN_CIRCUIT(circ),
|
|
|
|
&extended_cell.created_cell)) < 0) {
|
2014-10-16 19:26:42 +02:00
|
|
|
circuit_mark_for_close(circ, -reason);
|
|
|
|
return 0; /* We don't want to cause a warning, so we mark the circuit
|
|
|
|
* here. */
|
2012-12-06 04:34:49 +01:00
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
2006-07-23 09:37:35 +02:00
|
|
|
if ((reason=circuit_send_next_onion_skin(TO_ORIGIN_CIRCUIT(circ)))<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(domain,"circuit_send_next_onion_skin() failed.");
|
2006-01-05 22:23:03 +01:00
|
|
|
return reason;
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
case RELAY_COMMAND_TRUNCATE:
|
2004-11-28 10:05:49 +01:00
|
|
|
if (layer_hint) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_APP,
|
|
|
|
"'truncate' unsupported at origin. Dropping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2013-03-18 20:55:55 +01:00
|
|
|
if (circ->n_hop) {
|
|
|
|
if (circ->n_chan)
|
|
|
|
log_warn(LD_BUG, "n_chan and n_hop set on the same circuit!");
|
|
|
|
extend_info_free(circ->n_hop);
|
|
|
|
circ->n_hop = NULL;
|
|
|
|
tor_free(circ->n_chan_create_cell);
|
|
|
|
circuit_set_state(circ, CIRCUIT_STATE_OPEN);
|
|
|
|
}
|
2012-08-25 23:30:01 +02:00
|
|
|
if (circ->n_chan) {
|
2012-10-03 02:39:58 +02:00
|
|
|
uint8_t trunc_reason = get_uint8(cell->payload + RELAY_HEADER_SIZE);
|
2012-08-25 23:30:01 +02:00
|
|
|
circuit_clear_cell_queue(circ, circ->n_chan);
|
|
|
|
channel_send_destroy(circ->n_circ_id, circ->n_chan,
|
|
|
|
trunc_reason);
|
|
|
|
circuit_set_n_circid_chan(circ, 0, NULL);
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_EXIT, "Processed 'truncate', replying.");
|
2006-01-05 22:23:03 +01:00
|
|
|
{
|
|
|
|
char payload[1];
|
|
|
|
payload[0] = (char)END_CIRC_REASON_REQUESTED;
|
2007-03-24 16:58:11 +01:00
|
|
|
relay_send_command_from_edge(0, circ, RELAY_COMMAND_TRUNCATED,
|
|
|
|
payload, sizeof(payload), NULL);
|
2006-01-05 22:23:03 +01:00
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
case RELAY_COMMAND_TRUNCATED:
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!layer_hint) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_EXIT,
|
|
|
|
"'truncated' unsupported at non-origin. Dropping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2012-10-04 05:56:34 +02:00
|
|
|
circuit_truncated(TO_ORIGIN_CIRCUIT(circ), layer_hint,
|
|
|
|
get_uint8(cell->payload + RELAY_HEADER_SIZE));
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
case RELAY_COMMAND_CONNECTED:
|
2004-11-28 10:05:49 +01:00
|
|
|
if (conn) {
|
2005-10-25 09:04:36 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
2005-10-17 03:29:28 +02:00
|
|
|
"'connected' unsupported while open. Closing circ.");
|
2006-01-05 22:23:03 +01:00
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(domain,
|
2015-08-17 20:34:50 +02:00
|
|
|
"'connected' received on circid %u for streamid %d, "
|
|
|
|
"no conn attached anymore. Ignoring.",
|
|
|
|
(unsigned)circ->n_circ_id, rh.stream_id);
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
case RELAY_COMMAND_SENDME:
|
2012-07-06 13:29:54 +02:00
|
|
|
if (!rh.stream_id) {
|
2004-11-28 10:05:49 +01:00
|
|
|
if (layer_hint) {
|
2012-06-28 20:00:01 +02:00
|
|
|
if (layer_hint->package_window + CIRCWINDOW_INCREMENT >
|
|
|
|
CIRCWINDOW_START_MAX) {
|
2013-05-21 20:00:30 +02:00
|
|
|
static struct ratelim_t exit_warn_ratelim = RATELIM_INIT(600);
|
|
|
|
log_fn_ratelim(&exit_warn_ratelim, LOG_WARN, LD_PROTOCOL,
|
2013-05-21 19:45:21 +02:00
|
|
|
"Unexpected sendme cell from exit relay. "
|
2012-06-28 20:00:01 +02:00
|
|
|
"Closing circ.");
|
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
layer_hint->package_window += CIRCWINDOW_INCREMENT;
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_APP,"circ-level sendme at origin, packagewindow %d.",
|
|
|
|
layer_hint->package_window);
|
2004-05-13 09:24:49 +02:00
|
|
|
circuit_resume_edge_reading(circ, layer_hint);
|
|
|
|
} else {
|
2012-06-28 20:00:01 +02:00
|
|
|
if (circ->package_window + CIRCWINDOW_INCREMENT >
|
|
|
|
CIRCWINDOW_START_MAX) {
|
2013-05-21 20:00:30 +02:00
|
|
|
static struct ratelim_t client_warn_ratelim = RATELIM_INIT(600);
|
2014-10-16 19:04:11 +02:00
|
|
|
log_fn_ratelim(&client_warn_ratelim,LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
2013-05-21 19:45:21 +02:00
|
|
|
"Unexpected sendme cell from client. "
|
2013-02-01 22:22:34 +01:00
|
|
|
"Closing circ (window %d).",
|
|
|
|
circ->package_window);
|
2012-06-28 20:00:01 +02:00
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
circ->package_window += CIRCWINDOW_INCREMENT;
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_APP,
|
|
|
|
"circ-level sendme at non-origin, packagewindow %d.",
|
|
|
|
circ->package_window);
|
2004-05-13 09:24:49 +02:00
|
|
|
circuit_resume_edge_reading(circ, layer_hint);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2012-07-06 13:29:54 +02:00
|
|
|
if (!conn) {
|
|
|
|
log_info(domain,"sendme cell dropped, unknown stream (streamid %d).",
|
|
|
|
rh.stream_id);
|
|
|
|
return 0;
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
conn->package_window += STREAMWINDOW_INCREMENT;
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(domain,"stream-level sendme, packagewindow now %d.",
|
|
|
|
conn->package_window);
|
2010-08-18 20:14:28 +02:00
|
|
|
if (circuit_queue_streams_are_blocked(circ)) {
|
|
|
|
/* Still waiting for queue to flush; don't touch conn */
|
2010-08-18 19:55:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_start_reading(TO_CONN(conn));
|
2005-03-02 04:13:05 +01:00
|
|
|
/* handle whatever might still be on the inbuf */
|
2010-09-13 22:05:22 +02:00
|
|
|
if (connection_edge_package_raw_inbuf(conn, 1, NULL) < 0) {
|
2005-03-02 04:13:05 +01:00
|
|
|
/* (We already sent an end cell if possible) */
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_mark_for_close(TO_CONN(conn));
|
2005-03-02 04:13:05 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
2004-06-17 20:13:09 +02:00
|
|
|
case RELAY_COMMAND_RESOLVE:
|
|
|
|
if (layer_hint) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_APP,
|
|
|
|
"resolve request unsupported at AP; dropping.");
|
2004-07-23 01:21:12 +02:00
|
|
|
return 0;
|
2004-06-17 20:13:09 +02:00
|
|
|
} else if (conn) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, domain,
|
|
|
|
"resolve request for known stream; dropping.");
|
2004-07-23 01:21:12 +02:00
|
|
|
return 0;
|
2004-06-17 20:13:09 +02:00
|
|
|
} else if (circ->purpose != CIRCUIT_PURPOSE_OR) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, domain,
|
|
|
|
"resolve request on circ with purpose %d; dropping",
|
|
|
|
circ->purpose);
|
2004-07-23 01:21:12 +02:00
|
|
|
return 0;
|
2004-06-17 20:13:09 +02:00
|
|
|
}
|
2006-07-23 09:37:35 +02:00
|
|
|
connection_exit_begin_resolve(cell, TO_OR_CIRCUIT(circ));
|
2004-06-17 20:13:09 +02:00
|
|
|
return 0;
|
|
|
|
case RELAY_COMMAND_RESOLVED:
|
2004-11-28 10:05:49 +01:00
|
|
|
if (conn) {
|
2006-10-15 09:42:51 +02:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, domain,
|
|
|
|
"'resolved' unsupported while open. Closing circ.");
|
2006-01-05 22:23:03 +01:00
|
|
|
return -END_CIRC_REASON_TORPROTOCOL;
|
2004-06-17 20:13:09 +02:00
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(domain,
|
|
|
|
"'resolved' received, no conn attached anymore. Ignoring.");
|
2004-06-17 20:13:09 +02:00
|
|
|
return 0;
|
2004-05-13 09:24:49 +02:00
|
|
|
case RELAY_COMMAND_ESTABLISH_INTRO:
|
|
|
|
case RELAY_COMMAND_ESTABLISH_RENDEZVOUS:
|
|
|
|
case RELAY_COMMAND_INTRODUCE1:
|
|
|
|
case RELAY_COMMAND_INTRODUCE2:
|
|
|
|
case RELAY_COMMAND_INTRODUCE_ACK:
|
|
|
|
case RELAY_COMMAND_RENDEZVOUS1:
|
|
|
|
case RELAY_COMMAND_RENDEZVOUS2:
|
|
|
|
case RELAY_COMMAND_INTRO_ESTABLISHED:
|
|
|
|
case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED:
|
2008-10-27 17:46:45 +01:00
|
|
|
rend_process_relay_cell(circ, layer_hint,
|
|
|
|
rh.command, rh.length,
|
2004-05-13 09:24:49 +02:00
|
|
|
cell->payload+RELAY_HEADER_SIZE);
|
|
|
|
return 0;
|
|
|
|
}
|
2005-11-17 04:40:20 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
|
2005-12-14 21:40:40 +01:00
|
|
|
"Received unknown relay command %d. Perhaps the other side is using "
|
|
|
|
"a newer version of Tor? Dropping.",
|
2005-11-17 04:40:20 +01:00
|
|
|
rh.command);
|
|
|
|
return 0; /* for forward compatibility, don't kill the circuit */
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
|
2008-12-23 18:56:31 +01:00
|
|
|
/** How many relay_data cells have we built, ever? */
|
2004-05-13 09:24:49 +02:00
|
|
|
uint64_t stats_n_data_cells_packaged = 0;
|
2008-12-23 18:56:31 +01:00
|
|
|
/** How many bytes of data have we put in relay_data cells have we built,
|
|
|
|
* ever? This would be RELAY_PAYLOAD_SIZE*stats_n_data_cells_packaged if
|
|
|
|
* every relay cell we ever sent were completely full of data. */
|
2004-05-13 09:24:49 +02:00
|
|
|
uint64_t stats_n_data_bytes_packaged = 0;
|
2008-12-23 18:56:31 +01:00
|
|
|
/** How many relay_data cells have we received, ever? */
|
2004-05-13 09:24:49 +02:00
|
|
|
uint64_t stats_n_data_cells_received = 0;
|
2008-12-23 18:56:31 +01:00
|
|
|
/** How many bytes of data have we received relay_data cells, ever? This would
|
|
|
|
* be RELAY_PAYLOAD_SIZE*stats_n_data_cells_packaged if every relay cell we
|
|
|
|
* ever received were completely full of data. */
|
2004-05-13 09:24:49 +02:00
|
|
|
uint64_t stats_n_data_bytes_received = 0;
|
|
|
|
|
2010-09-13 22:05:22 +02:00
|
|
|
/** If <b>conn</b> has an entire relay payload of bytes on its inbuf (or
|
|
|
|
* <b>package_partial</b> is true), and the appropriate package windows aren't
|
|
|
|
* empty, grab a cell and send it down the circuit.
|
|
|
|
*
|
|
|
|
* If *<b>max_cells</b> is given, package no more than max_cells. Decrement
|
|
|
|
* *<b>max_cells</b> by the number of cells packaged.
|
2004-05-13 09:24:49 +02:00
|
|
|
*
|
2008-06-11 01:00:11 +02:00
|
|
|
* Return -1 (and send a RELAY_COMMAND_END cell if necessary) if conn should
|
2005-03-02 04:13:05 +01:00
|
|
|
* be marked for close, else return 0.
|
2004-05-13 09:24:49 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2010-09-13 22:05:22 +02:00
|
|
|
connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
|
|
|
|
int *max_cells)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2011-09-08 02:26:58 +02:00
|
|
|
size_t bytes_to_process, length;
|
2004-05-13 09:24:49 +02:00
|
|
|
char payload[CELL_PAYLOAD_SIZE];
|
|
|
|
circuit_t *circ;
|
2012-10-12 18:22:13 +02:00
|
|
|
const unsigned domain = conn->base_.type == CONN_TYPE_AP ? LD_APP : LD_EXIT;
|
2011-07-18 21:38:05 +02:00
|
|
|
int sending_from_optimistic = 0;
|
2013-02-11 21:55:50 +01:00
|
|
|
entry_connection_t *entry_conn =
|
|
|
|
conn->base_.type == CONN_TYPE_AP ? EDGE_TO_ENTRY_CONN(conn) : NULL;
|
2011-07-18 21:38:05 +02:00
|
|
|
const int sending_optimistically =
|
2013-02-11 21:55:50 +01:00
|
|
|
entry_conn &&
|
2012-10-12 18:22:13 +02:00
|
|
|
conn->base_.type == CONN_TYPE_AP &&
|
|
|
|
conn->base_.state != AP_CONN_STATE_OPEN;
|
2011-07-20 18:55:42 +02:00
|
|
|
crypt_path_t *cpath_layer = conn->cpath_layer;
|
2004-05-13 09:24:49 +02:00
|
|
|
|
|
|
|
tor_assert(conn);
|
2006-07-26 21:07:26 +02:00
|
|
|
|
2012-10-12 18:22:13 +02:00
|
|
|
if (conn->base_.marked_for_close) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG,
|
2007-03-04 21:11:46 +01:00
|
|
|
"called on conn that's already marked for close at %s:%d.",
|
2012-10-12 18:22:13 +02:00
|
|
|
conn->base_.marked_for_close_file, conn->base_.marked_for_close);
|
2005-03-23 03:52:55 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2010-09-13 22:05:22 +02:00
|
|
|
if (max_cells && *max_cells <= 0)
|
|
|
|
return 0;
|
|
|
|
|
2010-08-16 00:29:27 +02:00
|
|
|
repeat_connection_edge_package_raw_inbuf:
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2005-04-06 08:43:21 +02:00
|
|
|
circ = circuit_get_by_edge_conn(conn);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!circ) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(domain,"conn has no circuit! Closing.");
|
2006-10-20 19:54:48 +02:00
|
|
|
conn->end_reason = END_STREAM_REASON_CANT_ATTACH;
|
2004-05-13 09:24:49 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-07-20 18:55:42 +02:00
|
|
|
if (circuit_consider_stop_edge_reading(circ, cpath_layer))
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (conn->package_window <= 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(domain,"called with package_window %d. Skipping.",
|
|
|
|
conn->package_window);
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_stop_reading(TO_CONN(conn));
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-07-20 18:55:42 +02:00
|
|
|
sending_from_optimistic = entry_conn &&
|
|
|
|
entry_conn->sending_optimistic_data != NULL;
|
2011-07-18 21:38:05 +02:00
|
|
|
|
|
|
|
if (PREDICT_UNLIKELY(sending_from_optimistic)) {
|
2016-08-02 19:40:19 +02:00
|
|
|
bytes_to_process = buf_datalen(entry_conn->sending_optimistic_data);
|
2011-09-08 02:26:58 +02:00
|
|
|
if (PREDICT_UNLIKELY(!bytes_to_process)) {
|
2011-07-18 21:38:05 +02:00
|
|
|
log_warn(LD_BUG, "sending_optimistic_data was non-NULL but empty");
|
2011-09-08 02:26:58 +02:00
|
|
|
bytes_to_process = connection_get_inbuf_len(TO_CONN(conn));
|
2011-07-18 21:38:05 +02:00
|
|
|
sending_from_optimistic = 0;
|
|
|
|
}
|
|
|
|
} else {
|
2011-09-08 02:26:58 +02:00
|
|
|
bytes_to_process = connection_get_inbuf_len(TO_CONN(conn));
|
2011-07-18 21:38:05 +02:00
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2011-09-08 02:26:58 +02:00
|
|
|
if (!bytes_to_process)
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
|
2011-09-08 02:26:58 +02:00
|
|
|
if (!package_partial && bytes_to_process < RELAY_PAYLOAD_SIZE)
|
2004-11-21 08:43:12 +01:00
|
|
|
return 0;
|
|
|
|
|
2011-09-08 02:26:58 +02:00
|
|
|
if (bytes_to_process > RELAY_PAYLOAD_SIZE) {
|
2004-05-13 09:24:49 +02:00
|
|
|
length = RELAY_PAYLOAD_SIZE;
|
|
|
|
} else {
|
2011-09-08 02:26:58 +02:00
|
|
|
length = bytes_to_process;
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
stats_n_data_bytes_packaged += length;
|
|
|
|
stats_n_data_cells_packaged += 1;
|
|
|
|
|
2011-07-18 21:38:05 +02:00
|
|
|
if (PREDICT_UNLIKELY(sending_from_optimistic)) {
|
2012-06-15 15:37:40 +02:00
|
|
|
/* XXXX We could be more efficient here by sometimes packing
|
2011-07-18 21:38:05 +02:00
|
|
|
* previously-sent optimistic data in the same cell with data
|
|
|
|
* from the inbuf. */
|
2016-08-02 19:40:19 +02:00
|
|
|
fetch_from_buf(payload, length, entry_conn->sending_optimistic_data);
|
|
|
|
if (!buf_datalen(entry_conn->sending_optimistic_data)) {
|
|
|
|
buf_free(entry_conn->sending_optimistic_data);
|
2011-07-20 18:55:42 +02:00
|
|
|
entry_conn->sending_optimistic_data = NULL;
|
2011-07-18 21:38:05 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
connection_fetch_from_buf(payload, length, TO_CONN(conn));
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2012-11-02 19:22:21 +01:00
|
|
|
log_debug(domain,TOR_SOCKET_T_FORMAT": Packaging %d bytes (%d waiting).",
|
|
|
|
conn->base_.s,
|
2009-07-31 17:39:31 +02:00
|
|
|
(int)length, (int)connection_get_inbuf_len(TO_CONN(conn)));
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2011-07-18 21:38:05 +02:00
|
|
|
if (sending_optimistically && !sending_from_optimistic) {
|
|
|
|
/* This is new optimistic data; remember it in case we need to detach and
|
|
|
|
retry */
|
2011-07-20 18:55:42 +02:00
|
|
|
if (!entry_conn->pending_optimistic_data)
|
2016-08-02 19:40:19 +02:00
|
|
|
entry_conn->pending_optimistic_data = buf_new();
|
|
|
|
write_to_buf(payload, length, entry_conn->pending_optimistic_data);
|
2011-07-18 21:38:05 +02:00
|
|
|
}
|
|
|
|
|
2007-03-24 16:58:11 +01:00
|
|
|
if (connection_edge_send_command(conn, RELAY_COMMAND_DATA,
|
2007-03-24 16:57:51 +01:00
|
|
|
payload, length) < 0 )
|
2005-03-02 04:13:05 +01:00
|
|
|
/* circuit got marked for close, don't continue, don't need to mark conn */
|
|
|
|
return 0;
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2011-07-20 18:55:42 +02:00
|
|
|
if (!cpath_layer) { /* non-rendezvous exit */
|
2004-05-13 09:24:49 +02:00
|
|
|
tor_assert(circ->package_window > 0);
|
|
|
|
circ->package_window--;
|
|
|
|
} else { /* we're an AP, or an exit on a rendezvous circ */
|
2011-07-20 18:55:42 +02:00
|
|
|
tor_assert(cpath_layer->package_window > 0);
|
|
|
|
cpath_layer->package_window--;
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (--conn->package_window <= 0) { /* is it 0 after decrement? */
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_stop_reading(TO_CONN(conn));
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(domain,"conn->package_window reached 0.");
|
2011-07-20 18:55:42 +02:00
|
|
|
circuit_consider_stop_edge_reading(circ, cpath_layer);
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0; /* don't process the inbuf any more */
|
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(domain,"conn->package_window is now %d",conn->package_window);
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2010-09-13 22:05:22 +02:00
|
|
|
if (max_cells) {
|
|
|
|
*max_cells -= 1;
|
|
|
|
if (*max_cells <= 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-05-13 09:24:49 +02:00
|
|
|
/* handle more if there's more, or return 0 if there isn't */
|
|
|
|
goto repeat_connection_edge_package_raw_inbuf;
|
|
|
|
}
|
|
|
|
|
2011-03-15 20:35:11 +01:00
|
|
|
/** Called when we've just received a relay data cell, when
|
|
|
|
* we've just finished flushing all bytes to stream <b>conn</b>,
|
|
|
|
* or when we've flushed *some* bytes to the stream <b>conn</b>.
|
2004-05-13 09:24:49 +02:00
|
|
|
*
|
|
|
|
* If conn->outbuf is not too full, and our deliver window is
|
|
|
|
* low, send back a suitable number of stream-level sendme cells.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
void
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_edge_consider_sending_sendme(edge_connection_t *conn)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-05-13 09:24:49 +02:00
|
|
|
circuit_t *circ;
|
|
|
|
|
2006-07-26 21:07:26 +02:00
|
|
|
if (connection_outbuf_too_full(TO_CONN(conn)))
|
2004-05-13 09:24:49 +02:00
|
|
|
return;
|
|
|
|
|
2005-04-06 08:43:21 +02:00
|
|
|
circ = circuit_get_by_edge_conn(conn);
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!circ) {
|
2004-05-13 09:24:49 +02:00
|
|
|
/* this can legitimately happen if the destroy has already
|
|
|
|
* arrived and torn down the circuit */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_APP,"No circuit associated with conn. Skipping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-08-10 10:13:18 +02:00
|
|
|
while (conn->deliver_window <= STREAMWINDOW_START - STREAMWINDOW_INCREMENT) {
|
2012-10-12 18:22:13 +02:00
|
|
|
log_debug(conn->base_.type == CONN_TYPE_AP ?LD_APP:LD_EXIT,
|
2009-05-27 23:55:51 +02:00
|
|
|
"Outbuf %d, Queuing stream sendme.",
|
2012-10-12 18:22:13 +02:00
|
|
|
(int)conn->base_.outbuf_flushlen);
|
2004-05-13 09:24:49 +02:00
|
|
|
conn->deliver_window += STREAMWINDOW_INCREMENT;
|
2007-03-24 16:58:11 +01:00
|
|
|
if (connection_edge_send_command(conn, RELAY_COMMAND_SENDME,
|
2007-03-24 16:57:51 +01:00
|
|
|
NULL, 0) < 0) {
|
2008-02-06 13:46:46 +01:00
|
|
|
log_warn(LD_APP,"connection_edge_send_command failed. Skipping.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return; /* the circuit's closed, don't continue */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** The circuit <b>circ</b> has received a circuit-level sendme
|
|
|
|
* (on hop <b>layer_hint</b>, if we're the OP). Go through all the
|
|
|
|
* attached streams and let them resume reading and packaging, if
|
|
|
|
* their stream windows allow it.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
|
|
|
|
{
|
2010-08-18 20:14:28 +02:00
|
|
|
if (circuit_queue_streams_are_blocked(circ)) {
|
2010-08-18 19:55:01 +02:00
|
|
|
log_debug(layer_hint?LD_APP:LD_EXIT,"Too big queue, no resuming");
|
|
|
|
return;
|
|
|
|
}
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(layer_hint?LD_APP:LD_EXIT,"resuming");
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2006-07-23 09:37:35 +02:00
|
|
|
if (CIRCUIT_IS_ORIGIN(circ))
|
|
|
|
circuit_resume_edge_reading_helper(TO_ORIGIN_CIRCUIT(circ)->p_streams,
|
|
|
|
circ, layer_hint);
|
|
|
|
else
|
|
|
|
circuit_resume_edge_reading_helper(TO_OR_CIRCUIT(circ)->n_streams,
|
|
|
|
circ, layer_hint);
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
|
2013-02-08 22:28:05 +01:00
|
|
|
void
|
|
|
|
stream_choice_seed_weak_rng(void)
|
|
|
|
{
|
|
|
|
crypto_seed_weak_rng(&stream_choice_rng);
|
|
|
|
}
|
|
|
|
|
2004-05-13 09:24:49 +02:00
|
|
|
/** A helper function for circuit_resume_edge_reading() above.
|
|
|
|
* The arguments are the same, except that <b>conn</b> is the head
|
|
|
|
* of a linked list of edge streams that should each be considered.
|
|
|
|
*/
|
|
|
|
static int
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
circuit_resume_edge_reading_helper(edge_connection_t *first_conn,
|
2004-05-13 09:24:49 +02:00
|
|
|
circuit_t *circ,
|
2005-06-11 20:52:12 +02:00
|
|
|
crypt_path_t *layer_hint)
|
|
|
|
{
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
edge_connection_t *conn;
|
2010-11-29 21:59:59 +01:00
|
|
|
int n_packaging_streams, n_streams_left;
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
int packaged_this_round;
|
|
|
|
int cells_on_queue;
|
|
|
|
int cells_per_conn;
|
Improve fairness when activating streams in circuit_resume_edge_reading_helper
The reason the "streams problem" occurs is due to the complicated
interaction between Tor's congestion control and libevent. At some point
during the experiment, the circuit window is exhausted, which blocks all
edge streams. When a circuit level sendme is received at Exit, it
resumes edge reading by looping over linked list of edge streams, and
calling connection_start_reading() to inform libevent to resume reading.
When the streams are activated again, Tor gets the chance to service the
first three streams activated before the circuit window is exhausted
again, which causes all streams to be blocked again. As an experiment,
we reversed the order in which the streams are activated, and indeed the
first three streams, rather than the last three, got service, while the
others starved.
Our solution is to change the order in which streams are activated. We
choose a random edge connection from the linked list, and then we
activate streams starting from that chosen stream. When we reach the end
of the list, then we continue from the head of the list until our chosen
stream (treating the linked list as a circular linked list). It would
probably be better to actually remember which streams have received
service recently, but this way is simple and effective.
2010-11-29 21:34:21 +01:00
|
|
|
edge_connection_t *chosen_stream = NULL;
|
2012-12-27 22:38:33 +01:00
|
|
|
int max_to_package;
|
|
|
|
|
|
|
|
if (first_conn == NULL) {
|
|
|
|
/* Don't bother to try to do the rest of this if there are no connections
|
|
|
|
* to resume. */
|
|
|
|
return 0;
|
|
|
|
}
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
|
|
|
|
/* How many cells do we have space for? It will be the minimum of
|
|
|
|
* the number needed to exhaust the package window, and the minimum
|
|
|
|
* needed to fill the cell queue. */
|
2012-12-27 22:38:33 +01:00
|
|
|
max_to_package = circ->package_window;
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
if (CIRCUIT_IS_ORIGIN(circ)) {
|
2012-08-25 23:30:01 +02:00
|
|
|
cells_on_queue = circ->n_chan_cells.n;
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
} else {
|
|
|
|
or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
|
2012-08-25 23:30:01 +02:00
|
|
|
cells_on_queue = or_circ->p_chan_cells.n;
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
}
|
|
|
|
if (CELL_QUEUE_HIGHWATER_SIZE - cells_on_queue < max_to_package)
|
|
|
|
max_to_package = CELL_QUEUE_HIGHWATER_SIZE - cells_on_queue;
|
|
|
|
|
2010-11-29 21:59:59 +01:00
|
|
|
/* Once we used to start listening on the streams in the order they
|
|
|
|
* appeared in the linked list. That leads to starvation on the
|
|
|
|
* streams that appeared later on the list, since the first streams
|
|
|
|
* would always get to read first. Instead, we just pick a random
|
|
|
|
* stream on the list, and enable reading for streams starting at that
|
|
|
|
* point (and wrapping around as if the list were circular). It would
|
|
|
|
* probably be better to actually remember which streams we've
|
|
|
|
* serviced in the past, but this is simple and effective. */
|
Improve fairness when activating streams in circuit_resume_edge_reading_helper
The reason the "streams problem" occurs is due to the complicated
interaction between Tor's congestion control and libevent. At some point
during the experiment, the circuit window is exhausted, which blocks all
edge streams. When a circuit level sendme is received at Exit, it
resumes edge reading by looping over linked list of edge streams, and
calling connection_start_reading() to inform libevent to resume reading.
When the streams are activated again, Tor gets the chance to service the
first three streams activated before the circuit window is exhausted
again, which causes all streams to be blocked again. As an experiment,
we reversed the order in which the streams are activated, and indeed the
first three streams, rather than the last three, got service, while the
others starved.
Our solution is to change the order in which streams are activated. We
choose a random edge connection from the linked list, and then we
activate streams starting from that chosen stream. When we reach the end
of the list, then we continue from the head of the list until our chosen
stream (treating the linked list as a circular linked list). It would
probably be better to actually remember which streams have received
service recently, but this way is simple and effective.
2010-11-29 21:34:21 +01:00
|
|
|
|
|
|
|
/* Select a stream uniformly at random from the linked list. We
|
|
|
|
* don't need cryptographic randomness here. */
|
2010-11-29 21:59:59 +01:00
|
|
|
{
|
|
|
|
int num_streams = 0;
|
|
|
|
for (conn = first_conn; conn; conn = conn->next_stream) {
|
|
|
|
num_streams++;
|
2013-02-08 22:28:05 +01:00
|
|
|
if (tor_weak_random_one_in_n(&stream_choice_rng, num_streams)) {
|
2010-11-29 21:59:59 +01:00
|
|
|
chosen_stream = conn;
|
2013-02-08 22:28:05 +01:00
|
|
|
}
|
2013-02-08 22:31:46 +01:00
|
|
|
/* Invariant: chosen_stream has been chosen uniformly at random from
|
|
|
|
* among the first num_streams streams on first_conn.
|
|
|
|
*
|
|
|
|
* (Note that we iterate over every stream on the circuit, so that after
|
|
|
|
* we've considered the first stream, we've chosen it with P=1; and
|
|
|
|
* after we consider the second stream, we've switched to it with P=1/2
|
|
|
|
* and stayed with the first stream with P=1/2; and after we've
|
|
|
|
* considered the third stream, we've switched to it with P=1/3 and
|
|
|
|
* remained with one of the first two streams with P=(2/3), giving each
|
|
|
|
* one P=(1/2)(2/3) )=(1/3).) */
|
2010-11-29 21:59:59 +01:00
|
|
|
}
|
Improve fairness when activating streams in circuit_resume_edge_reading_helper
The reason the "streams problem" occurs is due to the complicated
interaction between Tor's congestion control and libevent. At some point
during the experiment, the circuit window is exhausted, which blocks all
edge streams. When a circuit level sendme is received at Exit, it
resumes edge reading by looping over linked list of edge streams, and
calling connection_start_reading() to inform libevent to resume reading.
When the streams are activated again, Tor gets the chance to service the
first three streams activated before the circuit window is exhausted
again, which causes all streams to be blocked again. As an experiment,
we reversed the order in which the streams are activated, and indeed the
first three streams, rather than the last three, got service, while the
others starved.
Our solution is to change the order in which streams are activated. We
choose a random edge connection from the linked list, and then we
activate streams starting from that chosen stream. When we reach the end
of the list, then we continue from the head of the list until our chosen
stream (treating the linked list as a circular linked list). It would
probably be better to actually remember which streams have received
service recently, but this way is simple and effective.
2010-11-29 21:34:21 +01:00
|
|
|
}
|
2010-11-29 21:59:59 +01:00
|
|
|
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
/* Count how many non-marked streams there are that have anything on
|
|
|
|
* their inbuf, and enable reading on all of the connections. */
|
2010-11-29 21:59:59 +01:00
|
|
|
n_packaging_streams = 0;
|
Improve fairness when activating streams in circuit_resume_edge_reading_helper
The reason the "streams problem" occurs is due to the complicated
interaction between Tor's congestion control and libevent. At some point
during the experiment, the circuit window is exhausted, which blocks all
edge streams. When a circuit level sendme is received at Exit, it
resumes edge reading by looping over linked list of edge streams, and
calling connection_start_reading() to inform libevent to resume reading.
When the streams are activated again, Tor gets the chance to service the
first three streams activated before the circuit window is exhausted
again, which causes all streams to be blocked again. As an experiment,
we reversed the order in which the streams are activated, and indeed the
first three streams, rather than the last three, got service, while the
others starved.
Our solution is to change the order in which streams are activated. We
choose a random edge connection from the linked list, and then we
activate streams starting from that chosen stream. When we reach the end
of the list, then we continue from the head of the list until our chosen
stream (treating the linked list as a circular linked list). It would
probably be better to actually remember which streams have received
service recently, but this way is simple and effective.
2010-11-29 21:34:21 +01:00
|
|
|
/* Activate reading starting from the chosen stream */
|
|
|
|
for (conn=chosen_stream; conn; conn = conn->next_stream) {
|
|
|
|
/* Start reading for the streams starting from here */
|
2012-10-12 18:22:13 +02:00
|
|
|
if (conn->base_.marked_for_close || conn->package_window <= 0)
|
Improve fairness when activating streams in circuit_resume_edge_reading_helper
The reason the "streams problem" occurs is due to the complicated
interaction between Tor's congestion control and libevent. At some point
during the experiment, the circuit window is exhausted, which blocks all
edge streams. When a circuit level sendme is received at Exit, it
resumes edge reading by looping over linked list of edge streams, and
calling connection_start_reading() to inform libevent to resume reading.
When the streams are activated again, Tor gets the chance to service the
first three streams activated before the circuit window is exhausted
again, which causes all streams to be blocked again. As an experiment,
we reversed the order in which the streams are activated, and indeed the
first three streams, rather than the last three, got service, while the
others starved.
Our solution is to change the order in which streams are activated. We
choose a random edge connection from the linked list, and then we
activate streams starting from that chosen stream. When we reach the end
of the list, then we continue from the head of the list until our chosen
stream (treating the linked list as a circular linked list). It would
probably be better to actually remember which streams have received
service recently, but this way is simple and effective.
2010-11-29 21:34:21 +01:00
|
|
|
continue;
|
|
|
|
if (!layer_hint || conn->cpath_layer == layer_hint) {
|
2010-11-29 21:53:12 +01:00
|
|
|
connection_start_reading(TO_CONN(conn));
|
Improve fairness when activating streams in circuit_resume_edge_reading_helper
The reason the "streams problem" occurs is due to the complicated
interaction between Tor's congestion control and libevent. At some point
during the experiment, the circuit window is exhausted, which blocks all
edge streams. When a circuit level sendme is received at Exit, it
resumes edge reading by looping over linked list of edge streams, and
calling connection_start_reading() to inform libevent to resume reading.
When the streams are activated again, Tor gets the chance to service the
first three streams activated before the circuit window is exhausted
again, which causes all streams to be blocked again. As an experiment,
we reversed the order in which the streams are activated, and indeed the
first three streams, rather than the last three, got service, while the
others starved.
Our solution is to change the order in which streams are activated. We
choose a random edge connection from the linked list, and then we
activate streams starting from that chosen stream. When we reach the end
of the list, then we continue from the head of the list until our chosen
stream (treating the linked list as a circular linked list). It would
probably be better to actually remember which streams have received
service recently, but this way is simple and effective.
2010-11-29 21:34:21 +01:00
|
|
|
|
2010-12-01 01:23:40 +01:00
|
|
|
if (connection_get_inbuf_len(TO_CONN(conn)) > 0)
|
2010-11-29 21:59:59 +01:00
|
|
|
++n_packaging_streams;
|
Improve fairness when activating streams in circuit_resume_edge_reading_helper
The reason the "streams problem" occurs is due to the complicated
interaction between Tor's congestion control and libevent. At some point
during the experiment, the circuit window is exhausted, which blocks all
edge streams. When a circuit level sendme is received at Exit, it
resumes edge reading by looping over linked list of edge streams, and
calling connection_start_reading() to inform libevent to resume reading.
When the streams are activated again, Tor gets the chance to service the
first three streams activated before the circuit window is exhausted
again, which causes all streams to be blocked again. As an experiment,
we reversed the order in which the streams are activated, and indeed the
first three streams, rather than the last three, got service, while the
others starved.
Our solution is to change the order in which streams are activated. We
choose a random edge connection from the linked list, and then we
activate streams starting from that chosen stream. When we reach the end
of the list, then we continue from the head of the list until our chosen
stream (treating the linked list as a circular linked list). It would
probably be better to actually remember which streams have received
service recently, but this way is simple and effective.
2010-11-29 21:34:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Go back and do the ones we skipped, circular-style */
|
2010-11-29 21:53:12 +01:00
|
|
|
for (conn = first_conn; conn != chosen_stream; conn = conn->next_stream) {
|
2012-10-12 18:22:13 +02:00
|
|
|
if (conn->base_.marked_for_close || conn->package_window <= 0)
|
2005-03-24 22:57:45 +01:00
|
|
|
continue;
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
if (!layer_hint || conn->cpath_layer == layer_hint) {
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_start_reading(TO_CONN(conn));
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
|
2010-10-14 02:36:34 +02:00
|
|
|
if (connection_get_inbuf_len(TO_CONN(conn)) > 0)
|
2010-11-29 21:59:59 +01:00
|
|
|
++n_packaging_streams;
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-29 21:59:59 +01:00
|
|
|
if (n_packaging_streams == 0) /* avoid divide-by-zero */
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
2010-11-29 21:59:59 +01:00
|
|
|
cells_per_conn = CEIL_DIV(max_to_package, n_packaging_streams);
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
|
|
|
|
packaged_this_round = 0;
|
|
|
|
n_streams_left = 0;
|
|
|
|
|
|
|
|
/* Iterate over all connections. Package up to cells_per_conn cells on
|
|
|
|
* each. Update packaged_this_round with the total number of cells
|
|
|
|
* packaged, and n_streams_left with the number that still have data to
|
|
|
|
* package.
|
|
|
|
*/
|
|
|
|
for (conn=first_conn; conn; conn=conn->next_stream) {
|
2012-10-12 18:22:13 +02:00
|
|
|
if (conn->base_.marked_for_close || conn->package_window <= 0)
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
continue;
|
|
|
|
if (!layer_hint || conn->cpath_layer == layer_hint) {
|
|
|
|
int n = cells_per_conn, r;
|
2004-05-13 09:24:49 +02:00
|
|
|
/* handle whatever might still be on the inbuf */
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
r = connection_edge_package_raw_inbuf(conn, 1, &n);
|
|
|
|
|
|
|
|
/* Note how many we packaged */
|
|
|
|
packaged_this_round += (cells_per_conn-n);
|
|
|
|
|
|
|
|
if (r<0) {
|
|
|
|
/* Problem while packaging. (We already sent an end cell if
|
|
|
|
* possible) */
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_mark_for_close(TO_CONN(conn));
|
2005-03-02 04:13:05 +01:00
|
|
|
continue;
|
|
|
|
}
|
2004-05-13 09:24:49 +02:00
|
|
|
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
/* If there's still data to read, we'll be coming back to this stream. */
|
2010-10-14 02:36:34 +02:00
|
|
|
if (connection_get_inbuf_len(TO_CONN(conn)))
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
++n_streams_left;
|
|
|
|
|
2004-05-13 09:24:49 +02:00
|
|
|
/* If the circuit won't accept any more data, return without looking
|
|
|
|
* at any more of the streams. Any connections that should be stopped
|
|
|
|
* have already been stopped by connection_edge_package_raw_inbuf. */
|
2004-11-28 10:05:49 +01:00
|
|
|
if (circuit_consider_stop_edge_reading(circ, layer_hint))
|
2004-05-13 09:24:49 +02:00
|
|
|
return -1;
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
/* XXXX should we also stop immediately if we fill up the cell queue?
|
|
|
|
* Probably. */
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
}
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
|
|
|
|
/* If we made progress, and we are willing to package more, and there are
|
|
|
|
* any streams left that want to package stuff... try again!
|
|
|
|
*/
|
|
|
|
if (packaged_this_round && packaged_this_round < max_to_package &&
|
|
|
|
n_streams_left) {
|
|
|
|
max_to_package -= packaged_this_round;
|
2010-11-29 21:59:59 +01:00
|
|
|
n_packaging_streams = n_streams_left;
|
Make circuit_resume_edge_reading_helper treat streams more fairly.
Previously[*], the function would start with the first stream on the
circuit, and let it package as many cells as it wanted before
proceeding to the next stream in turn. If a circuit had many live
streams that all wanted to package data, the oldest would get
preference, and the newest would get ignored.
Now, we figure out how many cells we're willing to send per stream,
and try to allocate them fairly.
Roger diagnosed this in the comments for bug 1298.
[*] This bug has existed since before the first-ever public release
of Tor. It was added by r152 of Tor on 26 Jan 2003, which was
the first commit to implement streams (then called "topics").
This is not the oldest bug to be fixed in 0.2.2.x: that honor
goes to the windowing bug in r54, which got fixed in e50b7768 by
Roger with diagnosis by Karsten. This is, however, the most
long-lived bug to be fixed in 0.2.2.x: the r54 bug was fixed
2580 days after it was introduced, whereas I am writing this
commit message 2787 days after r152.
2010-09-13 23:04:13 +02:00
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
2004-05-13 09:24:49 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Check if the package window for <b>circ</b> is empty (at
|
|
|
|
* hop <b>layer_hint</b> if it's defined).
|
|
|
|
*
|
2004-11-21 12:20:28 +01:00
|
|
|
* If yes, tell edge streams to stop reading and return 1.
|
2004-05-13 09:24:49 +02:00
|
|
|
* Else return 0.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
|
|
|
|
{
|
2006-07-26 21:07:26 +02:00
|
|
|
edge_connection_t *conn = NULL;
|
2005-10-25 17:30:02 +02:00
|
|
|
unsigned domain = layer_hint ? LD_APP : LD_EXIT;
|
2004-05-13 09:24:49 +02:00
|
|
|
|
2004-11-21 12:20:28 +01:00
|
|
|
if (!layer_hint) {
|
2006-07-23 09:37:35 +02:00
|
|
|
or_circuit_t *or_circ = TO_OR_CIRCUIT(circ);
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(domain,"considering circ->package_window %d",
|
|
|
|
circ->package_window);
|
2004-11-21 12:20:28 +01:00
|
|
|
if (circ->package_window <= 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(domain,"yes, not-at-origin. stopped.");
|
2006-07-23 09:37:35 +02:00
|
|
|
for (conn = or_circ->n_streams; conn; conn=conn->next_stream)
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_stop_reading(TO_CONN(conn));
|
2004-11-21 12:20:28 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* else, layer hint is defined, use it */
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(domain,"considering layer_hint->package_window %d",
|
|
|
|
layer_hint->package_window);
|
2004-11-21 12:20:28 +01:00
|
|
|
if (layer_hint->package_window <= 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(domain,"yes, at-origin. stopped.");
|
2006-07-23 09:37:35 +02:00
|
|
|
for (conn = TO_ORIGIN_CIRCUIT(circ)->p_streams; conn;
|
2011-07-20 18:55:42 +02:00
|
|
|
conn=conn->next_stream) {
|
2004-11-28 10:05:49 +01:00
|
|
|
if (conn->cpath_layer == layer_hint)
|
2006-07-26 21:07:26 +02:00
|
|
|
connection_stop_reading(TO_CONN(conn));
|
2011-07-20 18:55:42 +02:00
|
|
|
}
|
2004-11-21 12:20:28 +01:00
|
|
|
return 1;
|
2004-05-13 09:24:49 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Check if the deliver_window for circuit <b>circ</b> (at hop
|
|
|
|
* <b>layer_hint</b> if it's defined) is low enough that we should
|
|
|
|
* send a circuit-level sendme back down the circuit. If so, send
|
|
|
|
* enough sendmes that the window would be overfull if we sent any
|
|
|
|
* more.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint)
|
|
|
|
{
|
|
|
|
// log_fn(LOG_INFO,"Considering: layer_hint is %s",
|
|
|
|
// layer_hint ? "defined" : "null");
|
2009-08-10 10:13:18 +02:00
|
|
|
while ((layer_hint ? layer_hint->deliver_window : circ->deliver_window) <=
|
2004-11-28 12:39:53 +01:00
|
|
|
CIRCWINDOW_START - CIRCWINDOW_INCREMENT) {
|
2009-05-27 23:55:51 +02:00
|
|
|
log_debug(LD_CIRC,"Queuing circuit sendme.");
|
2004-11-28 10:05:49 +01:00
|
|
|
if (layer_hint)
|
2004-05-13 09:24:49 +02:00
|
|
|
layer_hint->deliver_window += CIRCWINDOW_INCREMENT;
|
|
|
|
else
|
|
|
|
circ->deliver_window += CIRCWINDOW_INCREMENT;
|
2007-03-24 16:57:51 +01:00
|
|
|
if (relay_send_command_from_edge(0, circ, RELAY_COMMAND_SENDME,
|
2004-11-28 12:39:53 +01:00
|
|
|
NULL, 0, layer_hint) < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_CIRC,
|
2008-02-20 01:57:37 +01:00
|
|
|
"relay_send_command_from_edge failed. Circuit's closed.");
|
2004-05-13 09:24:49 +02:00
|
|
|
return; /* the circuit's closed, don't continue */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-06-09 21:03:31 +02:00
|
|
|
|
2007-03-29 04:41:36 +02:00
|
|
|
#ifdef ACTIVE_CIRCUITS_PARANOIA
|
2012-09-21 23:46:22 +02:00
|
|
|
#define assert_cmux_ok_paranoid(chan) \
|
2012-10-01 23:24:08 +02:00
|
|
|
assert_circuit_mux_okay(chan)
|
2007-03-29 04:41:36 +02:00
|
|
|
#else
|
2012-09-21 23:46:22 +02:00
|
|
|
#define assert_cmux_ok_paranoid(chan)
|
2007-03-29 04:41:36 +02:00
|
|
|
#endif
|
|
|
|
|
2014-05-13 03:23:34 +02:00
|
|
|
/** The total number of cells we have allocated. */
|
2013-06-16 15:55:44 +02:00
|
|
|
static size_t total_cells_allocated = 0;
|
2007-04-19 21:52:30 +02:00
|
|
|
|
2007-04-11 02:30:34 +02:00
|
|
|
/** Release storage held by <b>cell</b>. */
|
2015-12-10 16:19:43 +01:00
|
|
|
static inline void
|
2009-09-28 16:37:01 +02:00
|
|
|
packed_cell_free_unchecked(packed_cell_t *cell)
|
2007-04-11 02:30:29 +02:00
|
|
|
{
|
2007-04-19 21:52:30 +02:00
|
|
|
--total_cells_allocated;
|
2015-02-21 23:18:20 +01:00
|
|
|
tor_free(cell);
|
2007-04-11 02:30:29 +02:00
|
|
|
}
|
|
|
|
|
2007-04-11 02:30:34 +02:00
|
|
|
/** Allocate and return a new packed_cell_t. */
|
2013-07-18 17:02:36 +02:00
|
|
|
STATIC packed_cell_t *
|
2012-09-03 14:49:31 +02:00
|
|
|
packed_cell_new(void)
|
2007-04-11 02:30:29 +02:00
|
|
|
{
|
2007-04-19 21:52:30 +02:00
|
|
|
++total_cells_allocated;
|
2015-02-21 23:18:20 +01:00
|
|
|
return tor_malloc_zero(sizeof(packed_cell_t));
|
2007-04-11 02:30:29 +02:00
|
|
|
}
|
2008-12-22 18:53:04 +01:00
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
/** Return a packed cell used outside by channel_t lower layer */
|
|
|
|
void
|
|
|
|
packed_cell_free(packed_cell_t *cell)
|
|
|
|
{
|
2013-07-18 17:02:36 +02:00
|
|
|
if (!cell)
|
|
|
|
return;
|
2012-08-25 23:30:01 +02:00
|
|
|
packed_cell_free_unchecked(cell);
|
|
|
|
}
|
|
|
|
|
2008-12-22 18:53:04 +01:00
|
|
|
/** Log current statistics for cell pool allocation at log level
|
|
|
|
* <b>severity</b>. */
|
2007-04-19 20:47:04 +02:00
|
|
|
void
|
|
|
|
dump_cell_pool_usage(int severity)
|
|
|
|
{
|
2007-04-19 21:52:30 +02:00
|
|
|
int n_circs = 0;
|
|
|
|
int n_cells = 0;
|
2014-08-15 22:23:22 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, c) {
|
2012-08-25 23:30:01 +02:00
|
|
|
n_cells += c->n_chan_cells.n;
|
2007-04-19 21:52:30 +02:00
|
|
|
if (!CIRCUIT_IS_ORIGIN(c))
|
2012-08-25 23:30:01 +02:00
|
|
|
n_cells += TO_OR_CIRCUIT(c)->p_chan_cells.n;
|
2007-04-19 21:52:30 +02:00
|
|
|
++n_circs;
|
|
|
|
}
|
2014-08-15 22:23:22 +02:00
|
|
|
SMARTLIST_FOREACH_END(c);
|
2013-02-01 22:19:02 +01:00
|
|
|
tor_log(severity, LD_MM,
|
|
|
|
"%d cells allocated on %d circuits. %d cells leaked.",
|
2013-06-18 16:23:03 +02:00
|
|
|
n_cells, n_circs, (int)total_cells_allocated - n_cells);
|
2007-04-19 20:47:04 +02:00
|
|
|
}
|
2007-04-11 02:30:29 +02:00
|
|
|
|
2007-04-10 01:15:46 +02:00
|
|
|
/** Allocate a new copy of packed <b>cell</b>. */
|
2015-12-10 16:19:43 +01:00
|
|
|
static inline packed_cell_t *
|
2012-11-07 01:56:47 +01:00
|
|
|
packed_cell_copy(const cell_t *cell, int wide_circ_ids)
|
2007-03-26 16:07:59 +02:00
|
|
|
{
|
2012-09-03 14:49:31 +02:00
|
|
|
packed_cell_t *c = packed_cell_new();
|
2012-11-07 01:56:47 +01:00
|
|
|
cell_pack(c, cell, wide_circ_ids);
|
2007-03-26 16:07:59 +02:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2007-03-26 16:08:35 +02:00
|
|
|
/** Append <b>cell</b> to the end of <b>queue</b>. */
|
2007-03-26 16:07:59 +02:00
|
|
|
void
|
2007-04-10 01:15:46 +02:00
|
|
|
cell_queue_append(cell_queue_t *queue, packed_cell_t *cell)
|
2007-03-26 16:07:59 +02:00
|
|
|
{
|
2013-03-22 19:25:34 +01:00
|
|
|
TOR_SIMPLEQ_INSERT_TAIL(&queue->head, cell, next);
|
2007-03-26 16:07:59 +02:00
|
|
|
++queue->n;
|
|
|
|
}
|
|
|
|
|
2013-02-06 14:37:38 +01:00
|
|
|
/** Append a newly allocated copy of <b>cell</b> to the end of the
|
2013-10-31 04:00:52 +01:00
|
|
|
* <b>exitward</b> (or app-ward) <b>queue</b> of <b>circ</b>. If
|
|
|
|
* <b>use_stats</b> is true, record statistics about the cell.
|
|
|
|
*/
|
2007-03-26 16:07:59 +02:00
|
|
|
void
|
2013-02-06 14:37:38 +01:00
|
|
|
cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
|
2013-05-24 12:29:42 +02:00
|
|
|
int exitward, const cell_t *cell,
|
2013-03-21 19:51:27 +01:00
|
|
|
int wide_circ_ids, int use_stats)
|
2007-03-26 16:07:59 +02:00
|
|
|
{
|
2012-11-07 01:56:47 +01:00
|
|
|
packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids);
|
2014-02-10 21:04:23 +01:00
|
|
|
(void)circ;
|
|
|
|
(void)exitward;
|
|
|
|
(void)use_stats;
|
2014-03-04 16:54:54 +01:00
|
|
|
|
2016-07-08 21:24:21 +02:00
|
|
|
copy->inserted_time = (uint32_t) monotime_coarse_absolute_msec();
|
2013-11-07 18:15:30 +01:00
|
|
|
|
2009-07-05 19:53:25 +02:00
|
|
|
cell_queue_append(queue, copy);
|
2007-03-26 16:07:59 +02:00
|
|
|
}
|
|
|
|
|
2013-03-22 19:25:34 +01:00
|
|
|
/** Initialize <b>queue</b> as an empty cell queue. */
|
|
|
|
void
|
|
|
|
cell_queue_init(cell_queue_t *queue)
|
|
|
|
{
|
|
|
|
memset(queue, 0, sizeof(cell_queue_t));
|
|
|
|
TOR_SIMPLEQ_INIT(&queue->head);
|
|
|
|
}
|
|
|
|
|
2007-03-26 16:08:35 +02:00
|
|
|
/** Remove and free every cell in <b>queue</b>. */
|
2007-03-26 16:07:59 +02:00
|
|
|
void
|
|
|
|
cell_queue_clear(cell_queue_t *queue)
|
|
|
|
{
|
2013-03-22 19:25:34 +01:00
|
|
|
packed_cell_t *cell;
|
|
|
|
while ((cell = TOR_SIMPLEQ_FIRST(&queue->head))) {
|
|
|
|
TOR_SIMPLEQ_REMOVE_HEAD(&queue->head, next);
|
2009-09-28 16:37:01 +02:00
|
|
|
packed_cell_free_unchecked(cell);
|
2007-03-26 16:07:59 +02:00
|
|
|
}
|
2013-03-22 19:25:34 +01:00
|
|
|
TOR_SIMPLEQ_INIT(&queue->head);
|
2007-03-26 16:07:59 +02:00
|
|
|
queue->n = 0;
|
|
|
|
}
|
|
|
|
|
2007-03-26 16:08:35 +02:00
|
|
|
/** Extract and return the cell at the head of <b>queue</b>; return NULL if
|
|
|
|
* <b>queue</b> is empty. */
|
2013-07-18 17:02:36 +02:00
|
|
|
STATIC packed_cell_t *
|
2007-03-26 16:07:59 +02:00
|
|
|
cell_queue_pop(cell_queue_t *queue)
|
|
|
|
{
|
2013-03-22 19:25:34 +01:00
|
|
|
packed_cell_t *cell = TOR_SIMPLEQ_FIRST(&queue->head);
|
2007-03-26 16:07:59 +02:00
|
|
|
if (!cell)
|
|
|
|
return NULL;
|
2013-03-22 19:25:34 +01:00
|
|
|
TOR_SIMPLEQ_REMOVE_HEAD(&queue->head, next);
|
2007-03-26 16:07:59 +02:00
|
|
|
--queue->n;
|
|
|
|
return cell;
|
|
|
|
}
|
|
|
|
|
2017-12-19 19:53:52 +01:00
|
|
|
/** Initialize <b>queue</b> as an empty cell queue. */
|
|
|
|
void
|
|
|
|
destroy_cell_queue_init(destroy_cell_queue_t *queue)
|
|
|
|
{
|
|
|
|
memset(queue, 0, sizeof(destroy_cell_queue_t));
|
|
|
|
TOR_SIMPLEQ_INIT(&queue->head);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Remove and free every cell in <b>queue</b>. */
|
|
|
|
void
|
|
|
|
destroy_cell_queue_clear(destroy_cell_queue_t *queue)
|
|
|
|
{
|
|
|
|
destroy_cell_t *cell;
|
|
|
|
while ((cell = TOR_SIMPLEQ_FIRST(&queue->head))) {
|
|
|
|
TOR_SIMPLEQ_REMOVE_HEAD(&queue->head, next);
|
|
|
|
tor_free(cell);
|
|
|
|
}
|
|
|
|
TOR_SIMPLEQ_INIT(&queue->head);
|
|
|
|
queue->n = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Extract and return the cell at the head of <b>queue</b>; return NULL if
|
|
|
|
* <b>queue</b> is empty. */
|
|
|
|
STATIC destroy_cell_t *
|
|
|
|
destroy_cell_queue_pop(destroy_cell_queue_t *queue)
|
|
|
|
{
|
|
|
|
destroy_cell_t *cell = TOR_SIMPLEQ_FIRST(&queue->head);
|
|
|
|
if (!cell)
|
|
|
|
return NULL;
|
|
|
|
TOR_SIMPLEQ_REMOVE_HEAD(&queue->head, next);
|
|
|
|
--queue->n;
|
|
|
|
return cell;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Append a destroy cell for <b>circid</b> to <b>queue</b>. */
|
|
|
|
void
|
|
|
|
destroy_cell_queue_append(destroy_cell_queue_t *queue,
|
|
|
|
circid_t circid,
|
|
|
|
uint8_t reason)
|
|
|
|
{
|
|
|
|
destroy_cell_t *cell = tor_malloc_zero(sizeof(destroy_cell_t));
|
|
|
|
cell->circid = circid;
|
|
|
|
cell->reason = reason;
|
|
|
|
/* Not yet used, but will be required for OOM handling. */
|
2017-12-21 16:48:37 +01:00
|
|
|
cell->inserted_time = (uint32_t) monotime_coarse_absolute_msec();
|
2017-12-19 19:53:52 +01:00
|
|
|
|
|
|
|
TOR_SIMPLEQ_INSERT_TAIL(&queue->head, cell, next);
|
|
|
|
++queue->n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Convert a destroy_cell_t to a newly allocated cell_t. Frees its input. */
|
|
|
|
static packed_cell_t *
|
|
|
|
destroy_cell_to_packed_cell(destroy_cell_t *inp, int wide_circ_ids)
|
|
|
|
{
|
|
|
|
packed_cell_t *packed = packed_cell_new();
|
|
|
|
cell_t cell;
|
|
|
|
memset(&cell, 0, sizeof(cell));
|
|
|
|
cell.circ_id = inp->circid;
|
|
|
|
cell.command = CELL_DESTROY;
|
|
|
|
cell.payload[0] = inp->reason;
|
|
|
|
cell_pack(packed, &cell, wide_circ_ids);
|
|
|
|
|
|
|
|
tor_free(inp);
|
|
|
|
return packed;
|
|
|
|
}
|
|
|
|
|
2013-06-16 15:55:44 +02:00
|
|
|
/** Return the total number of bytes used for each packed_cell in a queue.
|
|
|
|
* Approximate. */
|
|
|
|
size_t
|
|
|
|
packed_cell_mem_cost(void)
|
|
|
|
{
|
2015-02-21 23:18:20 +01:00
|
|
|
return sizeof(packed_cell_t);
|
2013-06-16 15:55:44 +02:00
|
|
|
}
|
|
|
|
|
2016-03-26 15:11:45 +01:00
|
|
|
/* DOCDOC */
|
2014-01-13 15:52:07 +01:00
|
|
|
STATIC size_t
|
|
|
|
cell_queues_get_total_allocation(void)
|
|
|
|
{
|
|
|
|
return total_cells_allocated * packed_cell_mem_cost();
|
|
|
|
}
|
|
|
|
|
2014-11-17 17:43:50 +01:00
|
|
|
/** How long after we've been low on memory should we try to conserve it? */
|
|
|
|
#define MEMORY_PRESSURE_INTERVAL (30*60)
|
|
|
|
|
|
|
|
/** The time at which we were last low on memory. */
|
|
|
|
static time_t last_time_under_memory_pressure = 0;
|
|
|
|
|
2013-06-16 15:55:44 +02:00
|
|
|
/** Check whether we've got too much space used for cells. If so,
|
|
|
|
* call the OOM handler and return 1. Otherwise, return 0. */
|
2014-01-13 15:52:07 +01:00
|
|
|
STATIC int
|
2013-06-16 15:55:44 +02:00
|
|
|
cell_queues_check_size(void)
|
|
|
|
{
|
2018-02-02 16:15:28 +01:00
|
|
|
time_t now = time(NULL);
|
2014-01-13 15:52:07 +01:00
|
|
|
size_t alloc = cell_queues_get_total_allocation();
|
2013-11-16 00:38:52 +01:00
|
|
|
alloc += buf_get_total_allocation();
|
2014-08-19 16:59:15 +02:00
|
|
|
alloc += tor_zlib_get_total_allocation();
|
2015-01-05 02:05:29 +01:00
|
|
|
const size_t rend_cache_total = rend_cache_get_total_allocation();
|
|
|
|
alloc += rend_cache_total;
|
2018-02-02 16:15:28 +01:00
|
|
|
const size_t geoip_client_cache_total =
|
|
|
|
geoip_client_cache_total_allocation();
|
|
|
|
alloc += geoip_client_cache_total;
|
2014-11-17 17:43:50 +01:00
|
|
|
if (alloc >= get_options()->MaxMemInQueues_low_threshold) {
|
|
|
|
last_time_under_memory_pressure = approx_time();
|
2015-01-12 19:59:50 +01:00
|
|
|
if (alloc >= get_options()->MaxMemInQueues) {
|
|
|
|
/* If we're spending over 20% of the memory limit on hidden service
|
2018-02-02 16:15:28 +01:00
|
|
|
* descriptors, free them until we're down to 10%. Do the same for geoip
|
|
|
|
* client cache. */
|
2015-01-12 19:59:50 +01:00
|
|
|
if (rend_cache_total > get_options()->MaxMemInQueues / 5) {
|
|
|
|
const size_t bytes_to_remove =
|
2015-01-13 04:33:10 +01:00
|
|
|
rend_cache_total - (size_t)(get_options()->MaxMemInQueues / 10);
|
2018-02-02 16:15:28 +01:00
|
|
|
rend_cache_clean_v2_descs_as_dir(now, bytes_to_remove);
|
2015-01-12 19:59:50 +01:00
|
|
|
alloc -= rend_cache_total;
|
|
|
|
alloc += rend_cache_get_total_allocation();
|
|
|
|
}
|
2018-02-02 16:15:28 +01:00
|
|
|
if (geoip_client_cache_total > get_options()->MaxMemInQueues / 5) {
|
|
|
|
const size_t bytes_to_remove =
|
|
|
|
geoip_client_cache_total -
|
|
|
|
(size_t)(get_options()->MaxMemInQueues / 10);
|
|
|
|
alloc -= geoip_client_cache_handle_oom(now, bytes_to_remove);
|
|
|
|
}
|
2015-01-12 19:59:50 +01:00
|
|
|
circuits_handle_oom(alloc);
|
|
|
|
return 1;
|
2014-11-17 17:43:50 +01:00
|
|
|
}
|
2013-06-16 15:55:44 +02:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-11-17 17:43:50 +01:00
|
|
|
/** Return true if we've been under memory pressure in the last
|
|
|
|
* MEMORY_PRESSURE_INTERVAL seconds. */
|
|
|
|
int
|
|
|
|
have_been_under_memory_pressure(void)
|
|
|
|
{
|
|
|
|
return last_time_under_memory_pressure + MEMORY_PRESSURE_INTERVAL
|
|
|
|
< approx_time();
|
|
|
|
}
|
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/**
|
|
|
|
* Update the number of cells available on the circuit's n_chan or p_chan's
|
|
|
|
* circuit mux.
|
|
|
|
*/
|
2007-03-26 16:07:59 +02:00
|
|
|
void
|
2012-11-12 04:20:59 +01:00
|
|
|
update_circuit_on_cmux_(circuit_t *circ, cell_direction_t direction,
|
|
|
|
const char *file, int lineno)
|
2007-03-26 16:07:59 +02:00
|
|
|
{
|
2012-09-21 23:46:22 +02:00
|
|
|
channel_t *chan = NULL;
|
|
|
|
or_circuit_t *or_circ = NULL;
|
|
|
|
circuitmux_t *cmux = NULL;
|
2012-08-25 23:30:01 +02:00
|
|
|
|
|
|
|
tor_assert(circ);
|
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/* Okay, get the channel */
|
|
|
|
if (direction == CELL_DIRECTION_OUT) {
|
|
|
|
chan = circ->n_chan;
|
2009-12-12 06:49:48 +01:00
|
|
|
} else {
|
2012-09-21 23:46:22 +02:00
|
|
|
or_circ = TO_OR_CIRCUIT(circ);
|
|
|
|
chan = or_circ->p_chan;
|
2009-12-12 06:49:48 +01:00
|
|
|
}
|
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
tor_assert(chan);
|
2012-09-21 23:46:22 +02:00
|
|
|
tor_assert(chan->cmux);
|
2012-08-25 23:30:01 +02:00
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/* Now get the cmux */
|
|
|
|
cmux = chan->cmux;
|
2007-04-10 18:24:50 +02:00
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/* Cmux sanity check */
|
2012-11-12 04:20:59 +01:00
|
|
|
if (! circuitmux_is_circuit_attached(cmux, circ)) {
|
2016-09-08 21:15:57 +02:00
|
|
|
log_warn(LD_BUG, "called on non-attached circuit from %s:%d",
|
2012-11-12 04:20:59 +01:00
|
|
|
file, lineno);
|
|
|
|
return;
|
|
|
|
}
|
2012-09-21 23:46:22 +02:00
|
|
|
tor_assert(circuitmux_attached_circuit_direction(cmux, circ) == direction);
|
2007-03-26 16:08:35 +02:00
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
assert_cmux_ok_paranoid(chan);
|
2009-12-12 06:49:48 +01:00
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/* Update the number of cells we have for the circuit mux */
|
|
|
|
if (direction == CELL_DIRECTION_OUT) {
|
|
|
|
circuitmux_set_num_cells(cmux, circ, circ->n_chan_cells.n);
|
2009-12-12 06:49:48 +01:00
|
|
|
} else {
|
2012-09-21 23:46:22 +02:00
|
|
|
circuitmux_set_num_cells(cmux, circ, or_circ->p_chan_cells.n);
|
2009-12-12 06:49:48 +01:00
|
|
|
}
|
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
assert_cmux_ok_paranoid(chan);
|
2007-03-26 16:07:59 +02:00
|
|
|
}
|
|
|
|
|
2013-09-09 19:48:44 +02:00
|
|
|
/** Remove all circuits from the cmux on <b>chan</b>.
|
|
|
|
*
|
|
|
|
* If <b>circuits_out</b> is non-NULL, add all detached circuits to
|
|
|
|
* <b>circuits_out</b>.
|
|
|
|
**/
|
2007-03-26 16:07:59 +02:00
|
|
|
void
|
2013-09-09 19:48:44 +02:00
|
|
|
channel_unlink_all_circuits(channel_t *chan, smartlist_t *circuits_out)
|
2007-03-26 16:07:59 +02:00
|
|
|
{
|
2012-08-25 23:30:01 +02:00
|
|
|
tor_assert(chan);
|
2012-09-21 23:46:22 +02:00
|
|
|
tor_assert(chan->cmux);
|
2012-08-25 23:30:01 +02:00
|
|
|
|
2013-09-09 19:48:44 +02:00
|
|
|
circuitmux_detach_all_circuits(chan->cmux, circuits_out);
|
2012-09-21 23:46:22 +02:00
|
|
|
chan->num_n_circuits = 0;
|
|
|
|
chan->num_p_circuits = 0;
|
2007-03-26 16:07:59 +02:00
|
|
|
}
|
|
|
|
|
2007-03-26 16:08:35 +02:00
|
|
|
/** Block (if <b>block</b> is true) or unblock (if <b>block</b> is false)
|
2012-08-25 23:30:01 +02:00
|
|
|
* every edge connection that is using <b>circ</b> to write to <b>chan</b>,
|
2010-08-18 20:20:49 +02:00
|
|
|
* and start or stop reading as appropriate.
|
|
|
|
*
|
2010-09-02 21:26:17 +02:00
|
|
|
* If <b>stream_id</b> is nonzero, block only the edge connection whose
|
|
|
|
* stream_id matches it.
|
|
|
|
*
|
2010-08-18 20:20:49 +02:00
|
|
|
* Returns the number of streams whose status we changed.
|
|
|
|
*/
|
|
|
|
static int
|
2012-08-25 23:30:01 +02:00
|
|
|
set_streams_blocked_on_circ(circuit_t *circ, channel_t *chan,
|
2010-09-08 16:53:19 +02:00
|
|
|
int block, streamid_t stream_id)
|
2007-03-26 16:08:18 +02:00
|
|
|
{
|
|
|
|
edge_connection_t *edge = NULL;
|
2010-08-18 20:20:49 +02:00
|
|
|
int n = 0;
|
2012-08-25 23:30:01 +02:00
|
|
|
if (circ->n_chan == chan) {
|
|
|
|
circ->streams_blocked_on_n_chan = block;
|
2007-03-26 16:08:18 +02:00
|
|
|
if (CIRCUIT_IS_ORIGIN(circ))
|
|
|
|
edge = TO_ORIGIN_CIRCUIT(circ)->p_streams;
|
|
|
|
} else {
|
2012-08-25 23:30:01 +02:00
|
|
|
circ->streams_blocked_on_p_chan = block;
|
2007-03-26 16:08:18 +02:00
|
|
|
tor_assert(!CIRCUIT_IS_ORIGIN(circ));
|
|
|
|
edge = TO_OR_CIRCUIT(circ)->n_streams;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; edge; edge = edge->next_stream) {
|
|
|
|
connection_t *conn = TO_CONN(edge);
|
2010-09-02 21:26:17 +02:00
|
|
|
if (stream_id && edge->stream_id != stream_id)
|
|
|
|
continue;
|
|
|
|
|
2010-08-18 20:20:49 +02:00
|
|
|
if (edge->edge_blocked_on_circ != block) {
|
|
|
|
++n;
|
|
|
|
edge->edge_blocked_on_circ = block;
|
|
|
|
}
|
2007-03-26 16:08:18 +02:00
|
|
|
|
2016-08-02 19:15:10 +02:00
|
|
|
if (!conn->read_event) {
|
2008-03-18 20:00:12 +01:00
|
|
|
/* This connection is a placeholder for something; probably a DNS
|
|
|
|
* request. It can't actually stop or start reading.*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2007-03-26 16:08:18 +02:00
|
|
|
if (block) {
|
|
|
|
if (connection_is_reading(conn))
|
|
|
|
connection_stop_reading(conn);
|
|
|
|
} else {
|
|
|
|
/* Is this right? */
|
|
|
|
if (!connection_is_reading(conn))
|
|
|
|
connection_start_reading(conn);
|
|
|
|
}
|
|
|
|
}
|
2010-08-18 20:20:49 +02:00
|
|
|
|
|
|
|
return n;
|
2007-03-26 16:08:18 +02:00
|
|
|
}
|
|
|
|
|
2014-02-10 21:04:23 +01:00
|
|
|
/** Extract the command from a packed cell. */
|
|
|
|
static uint8_t
|
|
|
|
packed_cell_get_command(const packed_cell_t *cell, int wide_circ_ids)
|
|
|
|
{
|
|
|
|
if (wide_circ_ids) {
|
|
|
|
return get_uint8(cell->body+4);
|
|
|
|
} else {
|
|
|
|
return get_uint8(cell->body+2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-10 19:35:45 +02:00
|
|
|
/** Extract the circuit ID from a packed cell. */
|
|
|
|
circid_t
|
|
|
|
packed_cell_get_circid(const packed_cell_t *cell, int wide_circ_ids)
|
|
|
|
{
|
|
|
|
if (wide_circ_ids) {
|
|
|
|
return ntohl(get_uint32(cell->body));
|
|
|
|
} else {
|
|
|
|
return ntohs(get_uint16(cell->body));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-26 16:08:35 +02:00
|
|
|
/** Pull as many cells as possible (but no more than <b>max</b>) from the
|
2012-08-25 23:30:01 +02:00
|
|
|
* queue of the first active circuit on <b>chan</b>, and write them to
|
|
|
|
* <b>chan</b>->outbuf. Return the number of cells written. Advance
|
2007-03-26 16:08:35 +02:00
|
|
|
* the active circuit pointer to the next active circuit in the ring. */
|
2014-01-22 10:04:39 +01:00
|
|
|
MOCK_IMPL(int,
|
|
|
|
channel_flush_from_first_active_circuit, (channel_t *chan, int max))
|
2007-03-26 16:07:59 +02:00
|
|
|
{
|
2012-09-21 23:46:22 +02:00
|
|
|
circuitmux_t *cmux = NULL;
|
|
|
|
int n_flushed = 0;
|
2017-12-19 19:53:52 +01:00
|
|
|
cell_queue_t *queue;
|
|
|
|
destroy_cell_queue_t *destroy_queue=NULL;
|
2007-03-26 16:07:59 +02:00
|
|
|
circuit_t *circ;
|
2012-09-21 23:46:22 +02:00
|
|
|
or_circuit_t *or_circ;
|
2007-03-26 16:08:18 +02:00
|
|
|
int streams_blocked;
|
2012-09-21 23:46:22 +02:00
|
|
|
packed_cell_t *cell;
|
2009-12-10 17:12:42 +01:00
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/* Get the cmux */
|
2012-08-25 23:30:01 +02:00
|
|
|
tor_assert(chan);
|
2012-09-21 23:46:22 +02:00
|
|
|
tor_assert(chan->cmux);
|
|
|
|
cmux = chan->cmux;
|
|
|
|
|
|
|
|
/* Main loop: pick a circuit, send a cell, update the cmux */
|
|
|
|
while (n_flushed < max) {
|
2013-03-21 19:51:27 +01:00
|
|
|
circ = circuitmux_get_first_active_circuit(cmux, &destroy_queue);
|
|
|
|
if (destroy_queue) {
|
2017-12-19 19:53:52 +01:00
|
|
|
destroy_cell_t *dcell;
|
2013-03-21 19:51:27 +01:00
|
|
|
/* this code is duplicated from some of the logic below. Ugly! XXXX */
|
2017-12-19 19:53:52 +01:00
|
|
|
/* If we are given a destroy_queue here, then it is required to be
|
|
|
|
* nonempty... */
|
2013-03-21 19:51:27 +01:00
|
|
|
tor_assert(destroy_queue->n > 0);
|
2017-12-19 19:53:52 +01:00
|
|
|
dcell = destroy_cell_queue_pop(destroy_queue);
|
|
|
|
/* ...and pop() will always yield a cell from a nonempty queue. */
|
|
|
|
tor_assert(dcell);
|
|
|
|
/* frees dcell */
|
|
|
|
cell = destroy_cell_to_packed_cell(dcell, chan->wide_circ_ids);
|
|
|
|
/* frees cell */
|
2013-03-21 19:51:27 +01:00
|
|
|
channel_write_packed_cell(chan, cell);
|
2013-06-13 07:22:21 +02:00
|
|
|
/* Update the cmux destroy counter */
|
|
|
|
circuitmux_notify_xmit_destroy(cmux);
|
2013-03-21 19:51:27 +01:00
|
|
|
cell = NULL;
|
|
|
|
++n_flushed;
|
|
|
|
continue;
|
|
|
|
}
|
2012-09-21 23:46:22 +02:00
|
|
|
/* If it returns NULL, no cells left to send */
|
|
|
|
if (!circ) break;
|
|
|
|
assert_cmux_ok_paranoid(chan);
|
|
|
|
|
|
|
|
if (circ->n_chan == chan) {
|
|
|
|
queue = &circ->n_chan_cells;
|
|
|
|
streams_blocked = circ->streams_blocked_on_n_chan;
|
|
|
|
} else {
|
|
|
|
or_circ = TO_OR_CIRCUIT(circ);
|
|
|
|
tor_assert(or_circ->p_chan == chan);
|
|
|
|
queue = &TO_OR_CIRCUIT(circ)->p_chan_cells;
|
|
|
|
streams_blocked = circ->streams_blocked_on_p_chan;
|
|
|
|
}
|
2007-03-26 16:07:59 +02:00
|
|
|
|
2012-10-01 23:24:08 +02:00
|
|
|
/* Circuitmux told us this was active, so it should have cells */
|
2016-09-22 01:01:12 +02:00
|
|
|
if (/*BUG(*/ queue->n == 0 /*)*/) {
|
|
|
|
log_warn(LD_BUG, "Found a supposedly active circuit with no cells "
|
|
|
|
"to send. Trying to recover.");
|
|
|
|
circuitmux_set_num_cells(cmux, circ, 0);
|
|
|
|
if (! circ->marked_for_close)
|
|
|
|
circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-10-01 23:24:08 +02:00
|
|
|
tor_assert(queue->n > 0);
|
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/*
|
|
|
|
* Get just one cell here; once we've sent it, that can change the circuit
|
|
|
|
* selection, so we have to loop around for another even if this circuit
|
|
|
|
* has more than one.
|
|
|
|
*/
|
|
|
|
cell = cell_queue_pop(queue);
|
2007-03-26 16:07:59 +02:00
|
|
|
|
2009-07-05 19:53:25 +02:00
|
|
|
/* Calculate the exact time that this cell has spent in the queue. */
|
2013-02-06 14:37:38 +01:00
|
|
|
if (get_options()->CellStatistics ||
|
2013-05-24 12:29:42 +02:00
|
|
|
get_options()->TestingEnableCellStatsEvent) {
|
2014-02-10 19:55:27 +01:00
|
|
|
uint32_t msec_waiting;
|
2016-07-21 12:36:13 +02:00
|
|
|
uint32_t msec_now = (uint32_t)monotime_coarse_absolute_msec();
|
|
|
|
msec_waiting = msec_now - cell->inserted_time;
|
2014-02-10 19:55:27 +01:00
|
|
|
|
2014-02-10 21:04:23 +01:00
|
|
|
if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) {
|
|
|
|
or_circ = TO_OR_CIRCUIT(circ);
|
|
|
|
or_circ->total_cell_waiting_time += msec_waiting;
|
|
|
|
or_circ->processed_cells++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (get_options()->TestingEnableCellStatsEvent) {
|
|
|
|
uint8_t command = packed_cell_get_command(cell, chan->wide_circ_ids);
|
|
|
|
|
|
|
|
testing_cell_stats_entry_t *ent =
|
|
|
|
tor_malloc_zero(sizeof(testing_cell_stats_entry_t));
|
|
|
|
ent->command = command;
|
|
|
|
ent->waiting_time = msec_waiting / 10;
|
|
|
|
ent->removed = 1;
|
|
|
|
if (circ->n_chan == chan)
|
|
|
|
ent->exitward = 1;
|
|
|
|
if (!circ->testing_cell_stats)
|
|
|
|
circ->testing_cell_stats = smartlist_new();
|
|
|
|
smartlist_add(circ->testing_cell_stats, ent);
|
2009-08-14 14:52:39 +02:00
|
|
|
}
|
2009-07-05 19:53:25 +02:00
|
|
|
}
|
2009-08-14 14:45:53 +02:00
|
|
|
|
2009-07-12 16:33:31 +02:00
|
|
|
/* If we just flushed our queue and this circuit is used for a
|
|
|
|
* tunneled directory request, possibly advance its state. */
|
2012-10-09 09:51:33 +02:00
|
|
|
if (queue->n == 0 && chan->dirreq_id)
|
|
|
|
geoip_change_dirreq_state(chan->dirreq_id,
|
2009-07-14 22:24:50 +02:00
|
|
|
DIRREQ_TUNNELED,
|
|
|
|
DIRREQ_CIRC_QUEUE_FLUSHED);
|
2009-07-12 16:33:31 +02:00
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/* Now send the cell */
|
2012-08-25 23:30:01 +02:00
|
|
|
channel_write_packed_cell(chan, cell);
|
2012-09-21 23:46:22 +02:00
|
|
|
cell = NULL;
|
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
/*
|
|
|
|
* Don't packed_cell_free_unchecked(cell) here because the channel will
|
|
|
|
* do so when it gets out of the channel queue (probably already did, in
|
|
|
|
* which case that was an immediate double-free bug).
|
|
|
|
*/
|
2007-04-10 01:15:46 +02:00
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/* Update the counter */
|
2007-03-26 16:07:59 +02:00
|
|
|
++n_flushed;
|
2012-09-21 23:46:22 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now update the cmux; tell it we've just sent a cell, and how many
|
|
|
|
* we have left.
|
|
|
|
*/
|
|
|
|
circuitmux_notify_xmit_cells(cmux, circ, 1);
|
|
|
|
circuitmux_set_num_cells(cmux, circ, queue->n);
|
|
|
|
if (queue->n == 0)
|
|
|
|
log_debug(LD_GENERAL, "Made a circuit inactive.");
|
|
|
|
|
|
|
|
/* Is the cell queue low enough to unblock all the streams that are waiting
|
|
|
|
* to write to this circuit? */
|
|
|
|
if (streams_blocked && queue->n <= CELL_QUEUE_LOWWATER_SIZE)
|
|
|
|
set_streams_blocked_on_circ(circ, chan, 0, 0); /* unblock streams */
|
|
|
|
|
|
|
|
/* If n_flushed < max still, loop around and pick another circuit */
|
2007-03-26 16:07:59 +02:00
|
|
|
}
|
2012-09-21 23:46:22 +02:00
|
|
|
|
|
|
|
/* Okay, we're done sending now */
|
|
|
|
assert_cmux_ok_paranoid(chan);
|
2012-08-25 23:30:01 +02:00
|
|
|
|
2007-03-26 16:07:59 +02:00
|
|
|
return n_flushed;
|
|
|
|
}
|
|
|
|
|
2013-06-15 11:27:23 +02:00
|
|
|
#if 0
|
2013-06-14 05:32:31 +02:00
|
|
|
/** Indicate the current preferred cap for middle circuits; zero disables
|
|
|
|
* the cap. Right now it's just a constant, ORCIRC_MAX_MIDDLE_CELLS, but
|
|
|
|
* the logic in append_cell_to_circuit_queue() is written to be correct
|
|
|
|
* if we want to base it on a consensus param or something that might change
|
|
|
|
* in the future.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
get_max_middle_cells(void)
|
|
|
|
{
|
|
|
|
return ORCIRC_MAX_MIDDLE_CELLS;
|
|
|
|
}
|
2013-06-15 11:27:23 +02:00
|
|
|
#endif
|
2013-06-14 05:32:31 +02:00
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
/** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b>
|
2007-03-26 16:08:35 +02:00
|
|
|
* transmitting in <b>direction</b>. */
|
2007-03-26 16:07:59 +02:00
|
|
|
void
|
2012-08-25 23:30:01 +02:00
|
|
|
append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
|
2010-09-02 21:26:17 +02:00
|
|
|
cell_t *cell, cell_direction_t direction,
|
2010-09-08 16:53:19 +02:00
|
|
|
streamid_t fromstream)
|
2007-03-26 16:07:59 +02:00
|
|
|
{
|
2013-06-14 05:32:31 +02:00
|
|
|
or_circuit_t *orcirc = NULL;
|
2007-03-26 16:07:59 +02:00
|
|
|
cell_queue_t *queue;
|
2007-03-26 16:08:18 +02:00
|
|
|
int streams_blocked;
|
2013-06-15 11:27:23 +02:00
|
|
|
#if 0
|
2013-06-14 05:32:31 +02:00
|
|
|
uint32_t tgt_max_middle_cells, p_len, n_len, tmp, hard_max_middle_cells;
|
2013-06-15 11:27:23 +02:00
|
|
|
#endif
|
2013-06-14 05:32:31 +02:00
|
|
|
|
2013-05-24 12:29:42 +02:00
|
|
|
int exitward;
|
2010-09-15 19:06:54 +02:00
|
|
|
if (circ->marked_for_close)
|
|
|
|
return;
|
|
|
|
|
2013-05-24 12:29:42 +02:00
|
|
|
exitward = (direction == CELL_DIRECTION_OUT);
|
|
|
|
if (exitward) {
|
2012-08-25 23:30:01 +02:00
|
|
|
queue = &circ->n_chan_cells;
|
|
|
|
streams_blocked = circ->streams_blocked_on_n_chan;
|
2007-03-26 16:07:59 +02:00
|
|
|
} else {
|
2013-06-14 05:32:31 +02:00
|
|
|
orcirc = TO_OR_CIRCUIT(circ);
|
2012-08-25 23:30:01 +02:00
|
|
|
queue = &orcirc->p_chan_cells;
|
|
|
|
streams_blocked = circ->streams_blocked_on_p_chan;
|
2007-03-26 16:07:59 +02:00
|
|
|
}
|
|
|
|
|
2013-06-15 11:16:00 +02:00
|
|
|
/*
|
|
|
|
* Disabling this for now because of a possible guard discovery attack
|
|
|
|
*/
|
|
|
|
#if 0
|
2013-06-14 05:32:31 +02:00
|
|
|
/* Are we a middle circuit about to exceed ORCIRC_MAX_MIDDLE_CELLS? */
|
|
|
|
if ((circ->n_chan != NULL) && CIRCUIT_IS_ORCIRC(circ)) {
|
|
|
|
orcirc = TO_OR_CIRCUIT(circ);
|
|
|
|
if (orcirc->p_chan) {
|
|
|
|
/* We are a middle circuit if we have both n_chan and p_chan */
|
|
|
|
/* We'll need to know the current preferred maximum */
|
|
|
|
tgt_max_middle_cells = get_max_middle_cells();
|
|
|
|
if (tgt_max_middle_cells > 0) {
|
|
|
|
/* Do we need to initialize middle_max_cells? */
|
|
|
|
if (orcirc->max_middle_cells == 0) {
|
|
|
|
orcirc->max_middle_cells = tgt_max_middle_cells;
|
|
|
|
} else {
|
|
|
|
if (tgt_max_middle_cells > orcirc->max_middle_cells) {
|
|
|
|
/* If we want to increase the cap, we can do so right away */
|
|
|
|
orcirc->max_middle_cells = tgt_max_middle_cells;
|
|
|
|
} else if (tgt_max_middle_cells < orcirc->max_middle_cells) {
|
|
|
|
/*
|
|
|
|
* If we're shrinking the cap, we can't shrink past either queue;
|
|
|
|
* compare tgt_max_middle_cells rather than tgt_max_middle_cells *
|
|
|
|
* ORCIRC_MAX_MIDDLE_KILL_THRESH so the queues don't shrink enough
|
|
|
|
* to generate spurious warnings, either.
|
|
|
|
*/
|
|
|
|
n_len = circ->n_chan_cells.n;
|
|
|
|
p_len = orcirc->p_chan_cells.n;
|
|
|
|
tmp = tgt_max_middle_cells;
|
|
|
|
if (tmp < n_len) tmp = n_len;
|
|
|
|
if (tmp < p_len) tmp = p_len;
|
|
|
|
orcirc->max_middle_cells = tmp;
|
|
|
|
}
|
|
|
|
/* else no change */
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* tgt_max_middle_cells == 0 indicates we should disable the cap */
|
|
|
|
orcirc->max_middle_cells = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now we know orcirc->max_middle_cells is set correctly */
|
|
|
|
if (orcirc->max_middle_cells > 0) {
|
|
|
|
hard_max_middle_cells =
|
|
|
|
(uint32_t)(((double)orcirc->max_middle_cells) *
|
|
|
|
ORCIRC_MAX_MIDDLE_KILL_THRESH);
|
|
|
|
|
2013-06-14 07:41:53 +02:00
|
|
|
if ((unsigned)queue->n + 1 >= hard_max_middle_cells) {
|
2013-06-14 05:32:31 +02:00
|
|
|
/* Queueing this cell would put queue over the kill theshold */
|
|
|
|
log_warn(LD_CIRC,
|
|
|
|
"Got a cell exceeding the hard cap of %u in the "
|
|
|
|
"%s direction on middle circ ID %u on chan ID "
|
|
|
|
U64_FORMAT "; killing the circuit.",
|
|
|
|
hard_max_middle_cells,
|
|
|
|
(direction == CELL_DIRECTION_OUT) ? "n" : "p",
|
|
|
|
(direction == CELL_DIRECTION_OUT) ?
|
|
|
|
circ->n_circ_id : orcirc->p_circ_id,
|
|
|
|
U64_PRINTF_ARG(
|
|
|
|
(direction == CELL_DIRECTION_OUT) ?
|
|
|
|
circ->n_chan->global_identifier :
|
|
|
|
orcirc->p_chan->global_identifier));
|
|
|
|
circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
|
|
|
|
return;
|
2013-06-14 07:41:53 +02:00
|
|
|
} else if ((unsigned)queue->n + 1 == orcirc->max_middle_cells) {
|
2013-06-14 05:32:31 +02:00
|
|
|
/* Only use ==, not >= for this test so we don't spam the log */
|
|
|
|
log_warn(LD_CIRC,
|
|
|
|
"While trying to queue a cell, reached the soft cap of %u "
|
|
|
|
"in the %s direction on middle circ ID %u "
|
|
|
|
"on chan ID " U64_FORMAT ".",
|
|
|
|
orcirc->max_middle_cells,
|
|
|
|
(direction == CELL_DIRECTION_OUT) ? "n" : "p",
|
|
|
|
(direction == CELL_DIRECTION_OUT) ?
|
|
|
|
circ->n_circ_id : orcirc->p_circ_id,
|
|
|
|
U64_PRINTF_ARG(
|
|
|
|
(direction == CELL_DIRECTION_OUT) ?
|
|
|
|
circ->n_chan->global_identifier :
|
|
|
|
orcirc->p_chan->global_identifier));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-06-15 11:16:00 +02:00
|
|
|
#endif
|
2013-06-14 05:32:31 +02:00
|
|
|
|
2013-05-24 12:29:42 +02:00
|
|
|
cell_queue_append_packed_copy(circ, queue, exitward, cell,
|
2013-10-28 11:30:49 +01:00
|
|
|
chan->wide_circ_ids, 1);
|
2007-03-26 16:07:59 +02:00
|
|
|
|
2013-06-16 15:55:44 +02:00
|
|
|
if (PREDICT_UNLIKELY(cell_queues_check_size())) {
|
|
|
|
/* We ran the OOM handler */
|
|
|
|
if (circ->marked_for_close)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-03-26 16:08:35 +02:00
|
|
|
/* If we have too many cells on the circuit, we should stop reading from
|
|
|
|
* the edge streams for a while. */
|
2007-03-26 16:08:18 +02:00
|
|
|
if (!streams_blocked && queue->n >= CELL_QUEUE_HIGHWATER_SIZE)
|
2012-08-25 23:30:01 +02:00
|
|
|
set_streams_blocked_on_circ(circ, chan, 1, 0); /* block streams */
|
2010-09-02 21:26:17 +02:00
|
|
|
|
|
|
|
if (streams_blocked && fromstream) {
|
|
|
|
/* This edge connection is apparently not blocked; block it. */
|
2012-08-25 23:30:01 +02:00
|
|
|
set_streams_blocked_on_circ(circ, chan, 1, fromstream);
|
2010-08-18 20:20:49 +02:00
|
|
|
}
|
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
update_circuit_on_cmux(circ, direction);
|
2007-03-26 16:07:59 +02:00
|
|
|
if (queue->n == 1) {
|
2012-09-21 23:46:22 +02:00
|
|
|
/* This was the first cell added to the queue. We just made this
|
2007-03-26 16:07:59 +02:00
|
|
|
* circuit active. */
|
2007-04-25 08:10:16 +02:00
|
|
|
log_debug(LD_GENERAL, "Made a circuit active.");
|
2007-03-26 16:07:59 +02:00
|
|
|
}
|
|
|
|
|
2013-08-25 17:45:07 +02:00
|
|
|
/* New way: mark this as having waiting cells for the scheduler */
|
|
|
|
scheduler_channel_has_waiting_cells(chan);
|
2007-03-26 16:07:59 +02:00
|
|
|
}
|
|
|
|
|
2008-12-22 15:56:16 +01:00
|
|
|
/** Append an encoded value of <b>addr</b> to <b>payload_out</b>, which must
|
2008-12-17 23:58:20 +01:00
|
|
|
* have at least 18 bytes of free space. The encoding is, as specified in
|
|
|
|
* tor-spec.txt:
|
|
|
|
* RESOLVED_TYPE_IPV4 or RESOLVED_TYPE_IPV6 [1 byte]
|
|
|
|
* LENGTH [1 byte]
|
|
|
|
* ADDRESS [length bytes]
|
|
|
|
* Return the number of bytes added, or -1 on error */
|
2008-08-05 22:08:19 +02:00
|
|
|
int
|
2010-12-14 01:34:01 +01:00
|
|
|
append_address_to_payload(uint8_t *payload_out, const tor_addr_t *addr)
|
2008-08-05 22:08:19 +02:00
|
|
|
{
|
|
|
|
uint32_t a;
|
|
|
|
switch (tor_addr_family(addr)) {
|
|
|
|
case AF_INET:
|
|
|
|
payload_out[0] = RESOLVED_TYPE_IPV4;
|
|
|
|
payload_out[1] = 4;
|
|
|
|
a = tor_addr_to_ipv4n(addr);
|
|
|
|
memcpy(payload_out+2, &a, 4);
|
|
|
|
return 6;
|
|
|
|
case AF_INET6:
|
|
|
|
payload_out[0] = RESOLVED_TYPE_IPV6;
|
|
|
|
payload_out[1] = 16;
|
|
|
|
memcpy(payload_out+2, tor_addr_to_in6_addr8(addr), 16);
|
|
|
|
return 18;
|
|
|
|
case AF_UNSPEC:
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-17 23:58:20 +01:00
|
|
|
/** Given <b>payload_len</b> bytes at <b>payload</b>, starting with an address
|
|
|
|
* encoded as by append_address_to_payload(), try to decode the address into
|
|
|
|
* *<b>addr_out</b>. Return the next byte in the payload after the address on
|
|
|
|
* success, or NULL on failure. */
|
2010-12-14 01:34:01 +01:00
|
|
|
const uint8_t *
|
|
|
|
decode_address_from_payload(tor_addr_t *addr_out, const uint8_t *payload,
|
2008-08-05 22:08:19 +02:00
|
|
|
int payload_len)
|
|
|
|
{
|
|
|
|
if (payload_len < 2)
|
|
|
|
return NULL;
|
2010-12-14 01:34:01 +01:00
|
|
|
if (payload_len < 2+payload[1])
|
2008-08-05 22:08:19 +02:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
switch (payload[0]) {
|
|
|
|
case RESOLVED_TYPE_IPV4:
|
|
|
|
if (payload[1] != 4)
|
|
|
|
return NULL;
|
|
|
|
tor_addr_from_ipv4n(addr_out, get_uint32(payload+2));
|
|
|
|
break;
|
|
|
|
case RESOLVED_TYPE_IPV6:
|
|
|
|
if (payload[1] != 16)
|
|
|
|
return NULL;
|
2010-12-14 01:34:01 +01:00
|
|
|
tor_addr_from_ipv6_bytes(addr_out, (char*)(payload+2));
|
2008-08-05 22:08:19 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tor_addr_make_unspec(addr_out);
|
|
|
|
break;
|
|
|
|
}
|
2010-12-14 01:34:01 +01:00
|
|
|
return payload + 2 + payload[1];
|
2008-08-05 22:08:19 +02:00
|
|
|
}
|
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
/** Remove all the cells queued on <b>circ</b> for <b>chan</b>. */
|
2010-07-31 00:55:24 +02:00
|
|
|
void
|
2012-08-25 23:30:01 +02:00
|
|
|
circuit_clear_cell_queue(circuit_t *circ, channel_t *chan)
|
2010-07-31 00:55:24 +02:00
|
|
|
{
|
|
|
|
cell_queue_t *queue;
|
2012-09-21 23:46:22 +02:00
|
|
|
cell_direction_t direction;
|
|
|
|
|
2012-08-25 23:30:01 +02:00
|
|
|
if (circ->n_chan == chan) {
|
|
|
|
queue = &circ->n_chan_cells;
|
2012-09-21 23:46:22 +02:00
|
|
|
direction = CELL_DIRECTION_OUT;
|
2010-07-31 00:55:24 +02:00
|
|
|
} else {
|
|
|
|
or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
|
2012-08-25 23:30:01 +02:00
|
|
|
tor_assert(orcirc->p_chan == chan);
|
|
|
|
queue = &orcirc->p_chan_cells;
|
2012-09-21 23:46:22 +02:00
|
|
|
direction = CELL_DIRECTION_IN;
|
2010-07-31 00:55:24 +02:00
|
|
|
}
|
|
|
|
|
2012-09-21 23:46:22 +02:00
|
|
|
/* Clear the queue */
|
2010-07-31 00:55:24 +02:00
|
|
|
cell_queue_clear(queue);
|
2012-09-21 23:46:22 +02:00
|
|
|
|
|
|
|
/* Update the cell counter in the cmux */
|
2012-11-12 04:20:59 +01:00
|
|
|
if (chan->cmux && circuitmux_is_circuit_attached(chan->cmux, circ))
|
|
|
|
update_circuit_on_cmux(circ, direction);
|
2012-09-21 23:46:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Fail with an assert if the circuit mux on chan is corrupt
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
assert_circuit_mux_okay(channel_t *chan)
|
|
|
|
{
|
|
|
|
tor_assert(chan);
|
|
|
|
tor_assert(chan->cmux);
|
|
|
|
|
|
|
|
circuitmux_assert_okay(chan->cmux);
|
2010-07-31 00:55:24 +02:00
|
|
|
}
|
|
|
|
|
2010-08-18 20:14:28 +02:00
|
|
|
/** Return 1 if we shouldn't restart reading on this circuit, even if
|
|
|
|
* we get a SENDME. Else return 0.
|
2010-08-18 19:55:01 +02:00
|
|
|
*/
|
|
|
|
static int
|
2010-08-18 20:14:28 +02:00
|
|
|
circuit_queue_streams_are_blocked(circuit_t *circ)
|
2010-08-18 19:55:01 +02:00
|
|
|
{
|
|
|
|
if (CIRCUIT_IS_ORIGIN(circ)) {
|
2012-08-25 23:30:01 +02:00
|
|
|
return circ->streams_blocked_on_n_chan;
|
2010-08-18 19:55:01 +02:00
|
|
|
} else {
|
2012-08-25 23:30:01 +02:00
|
|
|
return circ->streams_blocked_on_p_chan;
|
2010-08-18 19:55:01 +02:00
|
|
|
}
|
|
|
|
}
|
2010-09-16 21:43:00 +02:00
|
|
|
|