tor_assert(conn);
conn->cpath_layer = NULL; /* make sure we don't keep a stale pointer */
+ conn->exit_allows_optimistic_data = 0;
conn->on_circuit = NULL;
if (CIRCUIT_IS_ORIGIN(circ)) {
link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ,
crypt_path_t *cpath)
{
+ const node_t *exitnode;
+
/* add it into the linked list of streams on this circuit */
log_debug(LD_APP|LD_CIRC, "attaching new conn to circ. n_circ_id %d.",
circ->_base.n_circ_id);
tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
apconn->cpath_layer = circ->cpath->prev;
}
+
+ /* See if we can use optimistic data on this circuit */
+ if (apconn->cpath_layer->extend_info &&
+ (exitnode = node_get_by_id(
+ apconn->cpath_layer->extend_info->identity_digest)) &&
+ exitnode->rs) {
+ /* Okay; we know what exit node this is. */
+ if (circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL &&
+ exitnode->rs->version_supports_optimistic_data)
+ apconn->exit_allows_optimistic_data = 1;
+ else
+ apconn->exit_allows_optimistic_data = 0;
+ log_info(LD_APP, "Looks like completed circuit to %s %s allow "
+ "optimistic data for connection to %s",
+ safe_str_client(node_describe(exitnode)),
+ apconn->exit_allows_optimistic_data ? "does" : "doesn't",
+ safe_str_client(apconn->socks_request->address));
+ }
}
/** Return true iff <b>address</b> is matched by one of the entries in
static int address_is_in_virtual_range(const char *addr);
static int consider_plaintext_ports(edge_connection_t *conn, uint16_t port);
static void clear_trackexithost_mappings(const char *exitname);
+static int connection_ap_supports_optimistic_data(const edge_connection_t *);
/** An AP stream has failed/finished. If it hasn't already sent back
* a socks reply, send one now (based on endreason). Also set
return -1;
}
return 0;
+ case AP_CONN_STATE_CONNECT_WAIT:
+ if (connection_ap_supports_optimistic_data(conn)) {
+ log_info(LD_EDGE,
+ "data from edge while in '%s' state. Sending it anyway. "
+ "package_partial=%d, buflen=%ld",
+ conn_state_to_string(conn->_base.type, conn->_base.state),
+ package_partial, connection_get_inbuf_len(TO_CONN(conn)));
+ if (connection_edge_package_raw_inbuf(conn, package_partial, NULL)<0) {
+ /* (We already sent an end cell if possible) */
+ connection_mark_for_close(TO_CONN(conn));
+ return -1;
+ }
+ return 0;
+ }
+ /* Fall through if the connection is on a circuit without optimistic
+ * data support. */
case EXIT_CONN_STATE_CONNECTING:
case AP_CONN_STATE_RENDDESC_WAIT:
case AP_CONN_STATE_CIRCUIT_WAIT:
"data from edge while in '%s' state. Leaving it on buffer.",
conn_state_to_string(conn->_base.type, conn->_base.state));
return 0;
- case AP_CONN_STATE_CONNECT_WAIT:
- log_info(LD_EDGE,
- "data from edge while in '%s' state. Sending it anyway. "
- "package_partial=%d, buflen=%ld",
- conn_state_to_string(conn->_base.type, conn->_base.state),
- package_partial, connection_get_inbuf_len(TO_CONN(conn)));
- if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) {
- /* (We already sent an end cell if possible) */
- connection_mark_for_close(TO_CONN(conn));
- return -1;
- }
- return 0;
}
log_warn(LD_BUG,"Got unexpected state %d. Closing.",conn->_base.state);
tor_fragile_assert();
return test_stream_id;
}
+/** Return true iff <b>conn</b> is linked to a circuit and configured to use
+ * an exit that supports optimistic data. */
+static int
+connection_ap_supports_optimistic_data(const edge_connection_t *conn)
+{
+ tor_assert(conn->_base.type == CONN_TYPE_AP);
+ /* We can only send optimistic data if we're connected to an open
+ general circuit. */
+ if (conn->on_circuit == NULL ||
+ conn->on_circuit->state != CIRCUIT_STATE_OPEN ||
+ conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+ return 0;
+
+ return conn->exit_allows_optimistic_data;
+}
+
/** Write a relay begin cell, using destaddr and destport from ap_conn's
* socks_request field, and send it down circ.
*
control_event_stream_status(ap_conn, STREAM_EVENT_SENT_CONNECT, 0);
/* If there's queued-up data, send it now */
- log_info(LD_APP, "Possibly sending queued-up data: %ld",
- connection_get_inbuf_len(TO_CONN(ap_conn)));
- if (connection_edge_package_raw_inbuf(ap_conn, 1, NULL) < 0) {
- connection_mark_for_close(TO_CONN(ap_conn));
+ if (connection_get_inbuf_len(TO_CONN(ap_conn)) &&
+ connection_ap_supports_optimistic_data(ap_conn)) {
+ log_info(LD_APP, "Sending up to %ld bytes of queued-up data",
+ connection_get_inbuf_len(TO_CONN(ap_conn)));
+ if (connection_edge_package_raw_inbuf(ap_conn, 1, NULL) < 0) {
+ connection_mark_for_close(TO_CONN(ap_conn));
+ }
}
return 0;
* NATd connection */
unsigned int is_transparent_ap:1;
+ /** Set if this connection's target exit node allows optimistic data.
+ * (That is, data sent on this stream before the exit has sent a
+ * CONNECTED cell.)*/
+ unsigned int exit_allows_optimistic_data : 1;
+
/** If this is a DNSPort connection, this field holds the pending DNS
* request that we're going to try to answer. */
struct evdns_server_request *dns_server_request;
/** True iff this router is a version that, if it caches directory info,
* we can get microdescriptors from. */
unsigned int version_supports_microdesc_cache:1;
+ /** True iff this router is a version that allows DATA cells to arrive on
+ * a stream before it has sent a CONNECTED cell. */
+ unsigned int version_supports_optimistic_data:1;
unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */