diff options
author | Roger Dingledine <arma@torproject.org> | 2004-05-13 07:24:49 +0000 |
---|---|---|
committer | Roger Dingledine <arma@torproject.org> | 2004-05-13 07:24:49 +0000 |
commit | ef561c0e42dcd33a2ca8ee29a582dd68da32f3be (patch) | |
tree | deb933ba547882bf8a0300674745c8f4c4922757 /src/or/relay.c | |
parent | 6c68187e9f7e86a6a5d0e49a2bc99b0994df7ae0 (diff) | |
download | tor-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.c | 617 |
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 */ + } + } +} + |