aboutsummaryrefslogtreecommitdiff
path: root/src/or/relay.c
diff options
context:
space:
mode:
authorRoger Dingledine <arma@torproject.org>2004-05-13 07:24:49 +0000
committerRoger Dingledine <arma@torproject.org>2004-05-13 07:24:49 +0000
commitef561c0e42dcd33a2ca8ee29a582dd68da32f3be (patch)
treedeb933ba547882bf8a0300674745c8f4c4922757 /src/or/relay.c
parent6c68187e9f7e86a6a5d0e49a2bc99b0994df7ae0 (diff)
downloadtor-ef561c0e42dcd33a2ca8ee29a582dd68da32f3be.tar.gz
tor-ef561c0e42dcd33a2ca8ee29a582dd68da32f3be.zip
Break files apart into more modules
* \file circuitbuild.c * \brief The actual details of building circuits. * \file circuitlist.c * \brief Manage the global circuit list. * \file circuituse.c * \brief Launch the right sort of circuits, attach streams to them. * \file connection_edge.c * \brief Handle edge streams. * \file onion.c * \brief Functions to queue create cells, and handle onionskin * parsing and creation. * \file relay.c * \brief Handle relay cell encryption/decryption, plus packaging and * receiving from circuits. svn:r1863
Diffstat (limited to 'src/or/relay.c')
-rw-r--r--src/or/relay.c617
1 files changed, 614 insertions, 3 deletions
diff --git a/src/or/relay.c b/src/or/relay.c
index 9f54d2ed11..444b940101 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -4,15 +4,33 @@
/**
* \file relay.c
- * \brief Handle relay cell encryption/decryption.
+ * \brief Handle relay cell encryption/decryption, plus packaging and
+ * receiving from circuits.
**/
#include "or.h"
static int relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction,
crypt_path_t **layer_hint, char *recognized);
-static connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction)
-;
+static connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction);
+
+static int
+connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
+ 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(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);
+void connection_edge_consider_sending_sendme(connection_t *conn);
+
+
/** Stats: how many relay cells have originated at this hop, or have
* been relayed onward (not recognized at this hop)?
@@ -348,3 +366,596 @@ relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction)
return NULL; /* probably a begin relay cell */
}
+/** 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.
+ */
+void relay_header_pack(char *dest, const relay_header_t *src) {
+ *(uint8_t*)(dest) = src->command;
+
+ 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>.
+ */
+void relay_header_unpack(relay_header_t *dest, const char *src) {
+ dest->command = *(uint8_t*)(src);
+
+ 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));
+}
+
+/** 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 connection_edge_send_command(connection_t *fromconn, circuit_t *circ,
+ int relay_command, const char *payload,
+ int payload_len, crypt_path_t *cpath_layer) {
+ cell_t cell;
+ relay_header_t rh;
+ int cell_direction;
+
+ if(!circ) {
+ log_fn(LOG_WARN,"no circ. Closing conn.");
+ tor_assert(fromconn);
+ fromconn->has_sent_end = 1; /* no circ to send to */
+ connection_mark_for_close(fromconn);
+ return -1;
+ }
+
+ memset(&cell, 0, sizeof(cell_t));
+ cell.command = CELL_RELAY;
+ if(cpath_layer) {
+ cell.circ_id = circ->n_circ_id;
+ cell_direction = CELL_DIRECTION_OUT;
+ } else {
+ cell.circ_id = circ->p_circ_id;
+ cell_direction = CELL_DIRECTION_IN;
+ }
+
+ memset(&rh, 0, sizeof(rh));
+ rh.command = relay_command;
+ if(fromconn)
+ rh.stream_id = fromconn->stream_id; /* else it's 0 */
+ rh.length = payload_len;
+ relay_header_pack(cell.payload, &rh);
+ if(payload_len)
+ memcpy(cell.payload+RELAY_HEADER_SIZE, payload, payload_len);
+
+ log_fn(LOG_DEBUG,"delivering %d cell %s.", relay_command,
+ cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward");
+
+ if(circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer) < 0) {
+ log_fn(LOG_WARN,"circuit_package_relay_cell failed. Closing.");
+ circuit_mark_for_close(circ);
+ return -1;
+ }
+ return 0;
+}
+
+/** Translate the <b>payload</b> of length <b>length</b>, which
+ * came from a relay 'end' cell, into a static const string describing
+ * why the stream is closing.
+ */
+static const char *
+connection_edge_end_reason(char *payload, uint16_t length) {
+ if(length < 1) {
+ log_fn(LOG_WARN,"End cell arrived with length 0. Should be at least 1.");
+ return "MALFORMED";
+ }
+ if(*payload < _MIN_END_STREAM_REASON || *payload > _MAX_END_STREAM_REASON) {
+ log_fn(LOG_WARN,"Reason for ending (%d) not recognized.",*payload);
+ return "MALFORMED";
+ }
+ switch(*payload) {
+ case END_STREAM_REASON_MISC: return "misc error";
+ case END_STREAM_REASON_RESOLVEFAILED: return "resolve failed";
+ case END_STREAM_REASON_CONNECTFAILED: return "connect failed";
+ case END_STREAM_REASON_EXITPOLICY: return "exit policy failed";
+ case END_STREAM_REASON_DESTROY: return "destroyed";
+ case END_STREAM_REASON_DONE: return "closed normally";
+ case END_STREAM_REASON_TIMEOUT: return "gave up (timeout)";
+ }
+ tor_assert(0);
+ return "";
+}
+
+/** How many times will I retry a stream that fails due to DNS
+ * resolve failure?
+ */
+#define MAX_RESOLVE_FAILURES 3
+
+/** 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,
+ connection_t *conn, crypt_path_t *layer_hint) {
+ uint32_t addr;
+ int reason;
+
+ if(rh->command == RELAY_COMMAND_END) {
+ reason = *(cell->payload+RELAY_HEADER_SIZE);
+ /* We have to check this here, since we aren't connected yet. */
+ if (rh->length >= 5 && reason == END_STREAM_REASON_EXITPOLICY) {
+ log_fn(LOG_INFO,"Address %s refused due to exit policy. Retrying.",
+ conn->socks_request->address);
+ addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
+ client_dns_set_entry(conn->socks_request->address, addr);
+ conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ circuit_detach_stream(circ,conn);
+ if(connection_ap_handshake_attach_circuit(conn) >= 0)
+ return 0;
+ log_fn(LOG_INFO,"Giving up on retrying (from exitpolicy); conn can't be handled.");
+ /* else, conn will get closed below */
+ } else if (rh->length && reason == END_STREAM_REASON_RESOLVEFAILED) {
+ if (client_dns_incr_failures(conn->socks_request->address)
+ < MAX_RESOLVE_FAILURES) {
+ /* We haven't retried too many times; reattach the connection. */
+ conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ circuit_detach_stream(circ,conn);
+ if(connection_ap_handshake_attach_circuit(conn) >= 0)
+ return 0;
+ /* else, conn will get closed below */
+ log_fn(LOG_INFO,"Giving up on retrying (from resolvefailed); conn can't be handled.");
+ } else {
+ log_fn(LOG_WARN,"Have tried resolving address %s at %d different places. Giving up.",
+ conn->socks_request->address, MAX_RESOLVE_FAILURES);
+ }
+ }
+ log_fn(LOG_INFO,"Edge got end (%s) before we're connected. Marking for close.",
+ connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh->length));
+ if(CIRCUIT_IS_ORIGIN(circ))
+ circuit_log_path(LOG_INFO,circ);
+ conn->has_sent_end = 1; /* we just got an 'end', don't need to send one */
+ connection_mark_for_close(conn);
+ return 0;
+ }
+
+ if(conn->type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_CONNECTED) {
+ if(conn->state != AP_CONN_STATE_CONNECT_WAIT) {
+ log_fn(LOG_WARN,"Got 'connected' while not in state connect_wait. Dropping.");
+ return 0;
+ }
+// log_fn(LOG_INFO,"Connected! Notifying application.");
+ conn->state = AP_CONN_STATE_OPEN;
+ if (rh->length >= 4) {
+ addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE));
+ client_dns_set_entry(conn->socks_request->address, addr);
+ }
+ log_fn(LOG_INFO,"'connected' received after %d seconds.",
+ (int)(time(NULL) - conn->timestamp_lastread));
+ circuit_log_path(LOG_INFO,circ);
+ connection_ap_handshake_socks_reply(conn, NULL, 0, 1);
+ conn->socks_request->has_finished = 1;
+ /* handle anything that might have queued */
+ if (connection_edge_package_raw_inbuf(conn) < 0) {
+ connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
+ connection_mark_for_close(conn);
+ return 0;
+ }
+ return 0;
+ }
+
+ log_fn(LOG_WARN,"Got an unexpected relay command %d, in state %d (%s). Closing.",
+ rh->command, conn->state, conn_state_to_string[conn->type][conn->state]);
+ connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
+ connection_mark_for_close(conn);
+ return -1;
+}
+
+/** 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>.
+ *
+ * Return -1 if you want to tear down the circuit, else 0.
+ */
+static int
+connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
+ connection_t *conn,
+ crypt_path_t *layer_hint)
+{
+ static int num_seen=0;
+ relay_header_t rh;
+
+ tor_assert(cell && circ);
+
+ relay_header_unpack(&rh, cell->payload);
+// log_fn(LOG_DEBUG,"command %d stream %d", rh.command, rh.stream_id);
+ num_seen++;
+ log_fn(LOG_DEBUG,"Now seen %d relay cells here.", num_seen);
+
+ /* either conn is NULL, in which case we've got a control cell, or else
+ * conn points to the recognized stream. */
+
+ if(conn &&
+ conn->state != AP_CONN_STATE_OPEN &&
+ conn->state != EXIT_CONN_STATE_OPEN) {
+ return connection_edge_process_relay_cell_not_open(
+ &rh, cell, circ, conn, layer_hint);
+ }
+
+ switch(rh.command) {
+ case RELAY_COMMAND_DROP:
+ log_fn(LOG_INFO,"Got a relay-level padding cell. Dropping.");
+ return 0;
+ case RELAY_COMMAND_BEGIN:
+ if (layer_hint &&
+ circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) {
+ log_fn(LOG_WARN,"relay begin request unsupported at AP. Dropping.");
+ return 0;
+ }
+ if(conn) {
+ log_fn(LOG_WARN,"begin cell for known stream. Dropping.");
+ return 0;
+ }
+ connection_exit_begin_conn(cell, circ);
+ return 0;
+ case RELAY_COMMAND_DATA:
+ ++stats_n_data_cells_received;
+ if((layer_hint && --layer_hint->deliver_window < 0) ||
+ (!layer_hint && --circ->deliver_window < 0)) {
+ log_fn(LOG_WARN,"(relay data) circ deliver_window below 0. Killing.");
+ connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer);
+ connection_mark_for_close(conn);
+ return -1;
+ }
+ log_fn(LOG_DEBUG,"circ deliver_window now %d.", layer_hint ?
+ layer_hint->deliver_window : circ->deliver_window);
+
+ circuit_consider_sending_sendme(circ, layer_hint);
+
+ if(!conn) {
+ log_fn(LOG_INFO,"data cell dropped, unknown stream.");
+ return 0;
+ }
+
+ if(--conn->deliver_window < 0) { /* is it below 0 after decrement? */
+ log_fn(LOG_WARN,"(relay data) conn deliver_window below 0. Killing.");
+ return -1; /* somebody's breaking protocol. kill the whole circuit. */
+ }
+
+ stats_n_data_bytes_received += rh.length;
+ connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE,
+ rh.length, conn);
+ connection_edge_consider_sending_sendme(conn);
+ return 0;
+ case RELAY_COMMAND_END:
+ if(!conn) {
+ log_fn(LOG_INFO,"end cell (%s) dropped, unknown stream.",
+ connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length));
+ return 0;
+ }
+/* XXX add to this log_fn the exit node's nickname? */
+ log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.",
+ connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length),
+ conn->stream_id);
+
+#ifdef HALF_OPEN
+ conn->done_sending = 1;
+ shutdown(conn->s, 1); /* XXX check return; refactor NM */
+ if (conn->done_receiving) {
+ /* We just *got* an end; no reason to send one. */
+ conn->has_sent_end = 1;
+ connection_mark_for_close(conn);
+ conn->hold_open_until_flushed = 1;
+ }
+#else
+ /* We just *got* an end; no reason to send one. */
+ conn->has_sent_end = 1;
+ connection_mark_for_close(conn);
+ conn->hold_open_until_flushed = 1;
+#endif
+ return 0;
+ case RELAY_COMMAND_EXTEND:
+ if(conn) {
+ log_fn(LOG_WARN,"'extend' for non-zero stream. Dropping.");
+ return 0;
+ }
+ return circuit_extend(cell, circ);
+ case RELAY_COMMAND_EXTENDED:
+ if(!layer_hint) {
+ log_fn(LOG_WARN,"'extended' unsupported at non-origin. Dropping.");
+ return 0;
+ }
+ log_fn(LOG_DEBUG,"Got an extended cell! Yay.");
+ if(circuit_finish_handshake(circ, cell->payload+RELAY_HEADER_SIZE) < 0) {
+ log_fn(LOG_WARN,"circuit_finish_handshake failed.");
+ return -1;
+ }
+ if (circuit_send_next_onion_skin(circ)<0) {
+ log_fn(LOG_INFO,"circuit_send_next_onion_skin() failed.");
+ return -1;
+ }
+ return 0;
+ case RELAY_COMMAND_TRUNCATE:
+ if(layer_hint) {
+ log_fn(LOG_WARN,"'truncate' unsupported at origin. Dropping.");
+ return 0;
+ }
+ if(circ->n_conn) {
+ connection_send_destroy(circ->n_circ_id, circ->n_conn);
+ circ->n_conn = NULL;
+ }
+ log_fn(LOG_DEBUG, "Processed 'truncate', replying.");
+ connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED,
+ NULL, 0, NULL);
+ return 0;
+ case RELAY_COMMAND_TRUNCATED:
+ if(!layer_hint) {
+ log_fn(LOG_WARN,"'truncated' unsupported at non-origin. Dropping.");
+ return 0;
+ }
+ circuit_truncated(circ, layer_hint);
+ return 0;
+ case RELAY_COMMAND_CONNECTED:
+ if(conn) {
+ log_fn(LOG_WARN,"'connected' unsupported while open. Closing circ.");
+ return -1;
+ }
+ log_fn(LOG_INFO,"'connected' received, no conn attached anymore. Ignoring.");
+ return 0;
+ case RELAY_COMMAND_SENDME:
+ if(!conn) {
+ if(layer_hint) {
+ layer_hint->package_window += CIRCWINDOW_INCREMENT;
+ log_fn(LOG_DEBUG,"circ-level sendme at origin, packagewindow %d.",
+ layer_hint->package_window);
+ circuit_resume_edge_reading(circ, layer_hint);
+ } else {
+ circ->package_window += CIRCWINDOW_INCREMENT;
+ log_fn(LOG_DEBUG,"circ-level sendme at non-origin, packagewindow %d.",
+ circ->package_window);
+ circuit_resume_edge_reading(circ, layer_hint);
+ }
+ return 0;
+ }
+ conn->package_window += STREAMWINDOW_INCREMENT;
+ log_fn(LOG_DEBUG,"stream-level sendme, packagewindow now %d.", conn->package_window);
+ connection_start_reading(conn);
+ connection_edge_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */
+ return 0;
+ 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:
+ rend_process_relay_cell(circ, rh.command, rh.length,
+ cell->payload+RELAY_HEADER_SIZE);
+ return 0;
+ }
+ log_fn(LOG_WARN,"unknown relay command %d.",rh.command);
+ return -1;
+}
+
+uint64_t stats_n_data_cells_packaged = 0;
+uint64_t stats_n_data_bytes_packaged = 0;
+uint64_t stats_n_data_cells_received = 0;
+uint64_t stats_n_data_bytes_received = 0;
+
+/** While conn->inbuf has an entire relay payload of bytes on it,
+ * and the appropriate package windows aren't empty, grab a cell
+ * and send it down the circuit.
+ *
+ * Return -1 if conn should be marked for close, else return 0.
+ */
+int connection_edge_package_raw_inbuf(connection_t *conn) {
+ int amount_to_process, length;
+ char payload[CELL_PAYLOAD_SIZE];
+ circuit_t *circ;
+
+ tor_assert(conn);
+ tor_assert(!connection_speaks_cells(conn));
+
+repeat_connection_edge_package_raw_inbuf:
+
+ circ = circuit_get_by_conn(conn);
+ if(!circ) {
+ log_fn(LOG_INFO,"conn has no circuits! Closing.");
+ return -1;
+ }
+
+ if(circuit_consider_stop_edge_reading(circ, conn->cpath_layer))
+ return 0;
+
+ if(conn->package_window <= 0) {
+ log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window);
+ connection_stop_reading(conn);
+ return 0;
+ }
+
+ amount_to_process = buf_datalen(conn->inbuf);
+
+ if(!amount_to_process)
+ return 0;
+
+ if(amount_to_process > RELAY_PAYLOAD_SIZE) {
+ length = RELAY_PAYLOAD_SIZE;
+ } else {
+ length = amount_to_process;
+ }
+ stats_n_data_bytes_packaged += length;
+ stats_n_data_cells_packaged += 1;
+
+ connection_fetch_from_buf(payload, length, conn);
+
+ log_fn(LOG_DEBUG,"(%d) Packaging %d bytes (%d waiting).", conn->s, length,
+ (int)buf_datalen(conn->inbuf));
+
+ if(connection_edge_send_command(conn, circ, RELAY_COMMAND_DATA,
+ payload, length, conn->cpath_layer) < 0)
+ return 0; /* circuit is closed, don't continue */
+
+ if(!conn->cpath_layer) { /* non-rendezvous exit */
+ tor_assert(circ->package_window > 0);
+ circ->package_window--;
+ } else { /* we're an AP, or an exit on a rendezvous circ */
+ tor_assert(conn->cpath_layer->package_window > 0);
+ conn->cpath_layer->package_window--;
+ }
+
+ if(--conn->package_window <= 0) { /* is it 0 after decrement? */
+ connection_stop_reading(conn);
+ log_fn(LOG_DEBUG,"conn->package_window reached 0.");
+ circuit_consider_stop_edge_reading(circ, conn->cpath_layer);
+ return 0; /* don't process the inbuf any more */
+ }
+ log_fn(LOG_DEBUG,"conn->package_window is now %d",conn->package_window);
+
+ /* handle more if there's more, or return 0 if there isn't */
+ goto repeat_connection_edge_package_raw_inbuf;
+}
+
+/** Called when we've just received a relay data cell, or when
+ * we've just finished flushing all bytes to stream <b>conn</b>.
+ *
+ * If conn->outbuf is not too full, and our deliver window is
+ * low, send back a suitable number of stream-level sendme cells.
+ */
+void connection_edge_consider_sending_sendme(connection_t *conn) {
+ circuit_t *circ;
+
+ if(connection_outbuf_too_full(conn))
+ return;
+
+ circ = circuit_get_by_conn(conn);
+ if(!circ) {
+ /* this can legitimately happen if the destroy has already
+ * arrived and torn down the circuit */
+ log_fn(LOG_INFO,"No circuit associated with conn. Skipping.");
+ return;
+ }
+
+ while(conn->deliver_window < STREAMWINDOW_START - STREAMWINDOW_INCREMENT) {
+ log_fn(LOG_DEBUG,"Outbuf %d, Queueing stream sendme.", conn->outbuf_flushlen);
+ conn->deliver_window += STREAMWINDOW_INCREMENT;
+ if(connection_edge_send_command(conn, circ, RELAY_COMMAND_SENDME,
+ NULL, 0, conn->cpath_layer) < 0) {
+ log_fn(LOG_WARN,"connection_edge_send_command failed. Returning.");
+ 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)
+{
+
+ log_fn(LOG_DEBUG,"resuming");
+
+ /* have to check both n_streams and p_streams, to handle rendezvous */
+ if(circuit_resume_edge_reading_helper(circ->n_streams, circ, layer_hint) >= 0)
+ circuit_resume_edge_reading_helper(circ->p_streams, circ, layer_hint);
+}
+
+/** 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
+circuit_resume_edge_reading_helper(connection_t *conn,
+ circuit_t *circ,
+ crypt_path_t *layer_hint) {
+
+ for( ; conn; conn=conn->next_stream) {
+ if((!layer_hint && conn->package_window > 0) ||
+ (layer_hint && conn->package_window > 0 && conn->cpath_layer == layer_hint)) {
+ connection_start_reading(conn);
+ /* handle whatever might still be on the inbuf */
+ connection_edge_package_raw_inbuf(conn);
+
+ /* 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. */
+ if(circuit_consider_stop_edge_reading(circ, layer_hint))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/** Check if the package window for <b>circ</b> is empty (at
+ * hop <b>layer_hint</b> if it's defined).
+ *
+ * If yes, tell edge streams to stop reading and return -1.
+ * Else return 0.
+ */
+static int
+circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint)
+{
+ connection_t *conn = NULL;
+
+ log_fn(LOG_DEBUG,"considering");
+ if(!layer_hint && circ->package_window <= 0) {
+ log_fn(LOG_DEBUG,"yes, not-at-origin. stopped.");
+ for(conn = circ->n_streams; conn; conn=conn->next_stream)
+ connection_stop_reading(conn);
+ return -1;
+ } else if(layer_hint && layer_hint->package_window <= 0) {
+ log_fn(LOG_DEBUG,"yes, at-origin. stopped.");
+ for(conn = circ->n_streams; conn; conn=conn->next_stream)
+ if(conn->cpath_layer == layer_hint)
+ connection_stop_reading(conn);
+ for(conn = circ->p_streams; conn; conn=conn->next_stream)
+ if(conn->cpath_layer == layer_hint)
+ connection_stop_reading(conn);
+ return -1;
+ }
+ 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");
+ while((layer_hint ? layer_hint->deliver_window : circ->deliver_window) <
+ CIRCWINDOW_START - CIRCWINDOW_INCREMENT) {
+ log_fn(LOG_DEBUG,"Queueing circuit sendme.");
+ if(layer_hint)
+ layer_hint->deliver_window += CIRCWINDOW_INCREMENT;
+ else
+ circ->deliver_window += CIRCWINDOW_INCREMENT;
+ if(connection_edge_send_command(NULL, circ, RELAY_COMMAND_SENDME,
+ NULL, 0, layer_hint) < 0) {
+ log_fn(LOG_WARN,"connection_edge_send_command failed. Circuit's closed.");
+ return; /* the circuit's closed, don't continue */
+ }
+ }
+}
+