summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/TODO17
-rw-r--r--doc/control-spec.txt23
-rw-r--r--src/or/control.c83
-rw-r--r--src/or/or.h2
-rw-r--r--src/or/relay.c28
5 files changed, 131 insertions, 22 deletions
diff --git a/doc/TODO b/doc/TODO
index 0f19b860c6..77fd8adc3c 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -62,7 +62,7 @@ N . Switch to libevent
R o Reset uptime when IP changes.
Functionality
-N . Implement pending controller features.
+ o Implement pending controller features.
o Stubs for new functions.
o GETINFO
o Version
@@ -84,9 +84,9 @@ N . Implement pending controller features.
o Event for "new descriptors"
o Better stream IDs
o Stream status changed: "new" state.
- - EXTENDCIRCUIT
-R - revised circ selection stuff.
- - Implement controller interface.
+ o EXTENDCIRCUIT
+ o revised circ selection stuff.
+ o Implement controller interface.
o ATTACHSTREAM
o Make streams have an 'unattached and not-automatically-attachable'
state. ("Controller managed.")
@@ -96,8 +96,13 @@ R - revised circ selection stuff.
o Time out never-attached streams.
o If we never get a CONNECTED back, we should put the stream back in
CONTROLLER_WAIT, not in CIRCUIT_WAIT.
- - Add a way for the controller to say, "Hey, nuke this stream."
- - Tests for new controller features
+ o Add a way for the controller to say, "Hey, nuke this stream."
+ o Specify
+ o Implement
+ o Add a way for the controller to say, "Hey, nuke this circuit."
+ o Specify
+ o Implement
+ - Tests for new controller features
R o HTTPS proxy for OR CONNECT stuff. (For outgoing SSL connections to
other ORs.)
o Changes for forward compatibility
diff --git a/doc/control-spec.txt b/doc/control-spec.txt
index b1092eeb34..ed7ec4bfd5 100644
--- a/doc/control-spec.txt
+++ b/doc/control-spec.txt
@@ -417,6 +417,29 @@ the message.
after a new stream event is received, and before attaching this stream to
a circuit.
+3.20 CLOSESTREAM (Type 0x0013)
+
+ Sent from the client to the server. The message body contains three
+ fields:
+ Stream ID [4 octets]
+ Reason [1 octet]
+ Flags [1 octet]
+
+ Tells the server to close the specified stream. The reason should be one
+ of the Tor RELAY_END reasons given in tor-spec.txt. If the LSB of the
+ flags field is nonzero, and the stream wants to write data, Tor tries to
+ hold the stream open for a while until it can be flushed.
+
+3.21 CLOSECIRCUIT (Type 0x0014)
+
+ Sent from the client to the server. The message body contains two
+ fields:
+ Circuit ID [4 octets]
+ Flags [1 octet]
+
+ Tells the server to close the specified circuit. If the LSB of the flags
+ field is nonzero, do not close the circuit unless it is unused.
+
4. Implementation notes
4.1. There are four ways we could authenticate, for now:
diff --git a/src/or/control.c b/src/or/control.c
index a655521ec9..3114dbf2d3 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -48,8 +48,10 @@ const char control_c_id[] = "$Id$";
#define CONTROL_CMD_POSTDESCRIPTOR 0x000F
#define CONTROL_CMD_FRAGMENTHEADER 0x0010
#define CONTROL_CMD_FRAGMENT 0x0011
-#define CONTROL_CMD_REDIRECTSTREAM 0x0012
-#define _CONTROL_CMD_MAX_RECOGNIZED 0x0012
+#define CONTROL_CMD_REDIRECTSTREAM 0x0012
+#define CONTROL_CMD_CLOSESTREAM 0x0013
+#define CONTROL_CMD_CLOSECIRCUIT 0x0014
+#define _CONTROL_CMD_MAX_RECOGNIZED 0x0014
/* Recognized error codes. */
#define ERR_UNSPECIFIED 0x0000
@@ -152,6 +154,10 @@ static int handle_control_postdescriptor(connection_t *conn, uint32_t len,
const char *body);
static int handle_control_redirectstream(connection_t *conn, uint32_t len,
const char *body);
+static int handle_control_closestream(connection_t *conn, uint32_t len,
+ const char *body);
+static int handle_control_closecircuit(connection_t *conn, uint32_t len,
+ const char *body);
/** Given a possibly invalid message type code <b>cmd</b>, return a
* human-readable string equivalent. */
@@ -772,7 +778,72 @@ handle_control_redirectstream(connection_t *conn, uint32_t len,
send_control_done(conn);
return 0;
}
+static int
+handle_control_closestream(connection_t *conn, uint32_t len,
+ const char *body)
+{
+ uint32_t conn_id;
+ connection_t *ap_conn;
+ uint8_t reason;
+ int hold_open;
+
+ if (len < 6) {
+ send_control_error(conn, ERR_SYNTAX, "closestream message too short");
+ return 0;
+ }
+
+ conn_id = ntohl(get_uint32(body));
+ reason = *(uint8_t*)(body+4);
+ hold_open = (*(uint8_t*)(body+5)) & 1;
+
+ if (!(ap_conn = connection_get_by_global_id(conn_id))
+ || ap_conn->state != CONN_TYPE_AP
+ || !ap_conn->socks_request) {
+ send_control_error(conn, ERR_NO_STREAM,
+ "No AP connection found with given ID");
+ return 0;
+ }
+
+ if (!ap_conn->socks_request->has_finished) {
+ socks5_reply_status_t status =
+ connection_edge_end_reason_socks5_response(reason);
+ connection_ap_handshake_socks_reply(ap_conn, NULL, 0, status);
+ }
+ if (hold_open)
+ ap_conn->hold_open_until_flushed = 1;
+ connection_mark_for_close(ap_conn);
+
+ send_control_done(conn);
+ return 0;
+}
+static int
+handle_control_closecircuit(connection_t *conn, uint32_t len,
+ const char *body)
+{
+ uint32_t circ_id;
+ circuit_t *circ;
+ int safe;
+ if (len < 5) {
+ send_control_error(conn, ERR_SYNTAX, "closecircuit message too short");
+ return 0;
+ }
+ circ_id = ntohl(get_uint32(body));
+ safe = (*(uint8_t*)(body+4)) & 1;
+
+ if (!(circ = circuit_get_by_global_id(circ_id))) {
+ send_control_error(conn, ERR_NO_CIRC,
+ "No circuit found with given ID");
+ return 0;
+ }
+
+ if (!safe || !circ->p_streams) {
+ circuit_mark_for_close(circ);
+ }
+
+ send_control_done(conn);
+ return 0;
+}
/** Called when <b>conn</b> has no more bytes left on its outbuf. */
int
@@ -882,6 +953,14 @@ connection_control_process_inbuf(connection_t *conn) {
if (handle_control_redirectstream(conn, body_len, body))
return -1;
break;
+ case CONTROL_CMD_CLOSESTREAM:
+ if (handle_control_closestream(conn, body_len, body))
+ return -1;
+ break;
+ case CONTROL_CMD_CLOSECIRCUIT:
+ if (handle_control_closecircuit(conn, body_len, body))
+ return -1;
+ break;
case CONTROL_CMD_ERROR:
case CONTROL_CMD_DONE:
case CONTROL_CMD_CONFVALUE:
diff --git a/src/or/or.h b/src/or/or.h
index 19efee2760..c4e7f4f28d 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1560,7 +1560,7 @@ int connection_edge_send_command(connection_t *fromconn, circuit_t *circ,
size_t payload_len, crypt_path_t *cpath_layer);
int connection_edge_package_raw_inbuf(connection_t *conn, int package_partial);
void connection_edge_consider_sending_sendme(connection_t *conn);
-socks5_reply_status_t connection_edge_end_reason_sock5_response(char *payload, uint16_t length);
+socks5_reply_status_t connection_edge_end_reason_socks5_response(int reason);
int errno_to_end_reason(int e);
extern uint64_t stats_n_data_cells_packaged;
diff --git a/src/or/relay.c b/src/or/relay.c
index 5fd5be9097..5651fd35e1 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -476,16 +476,13 @@ connection_edge_end_reason_str(char *payload, uint16_t length) {
}
}
-/** Translate the <b>payload</b> of length <b>length</b>, which
- * came from a relay 'end' cell, into an appropriate SOCKS5 reply code.
+/** Translate <b>reason</b> (as from a relay 'end' cell) into an
+ * appropriate SOCKS5 reply code.
*/
-static socks5_reply_status_t
-connection_edge_end_reason_socks5_response(char *payload, uint16_t length) {
- if (length < 1) {
- log_fn(LOG_WARN,"End cell arrived with length 0. Should be at least 1.");
- return SOCKS5_GENERAL_ERROR;
- }
- switch (*payload) {
+socks5_reply_status_t
+connection_edge_end_reason_socks5_response(int reason)
+{
+ switch (reason) {
case END_STREAM_REASON_MISC:
return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_RESOLVEFAILED:
@@ -511,7 +508,7 @@ connection_edge_end_reason_socks5_response(char *payload, uint16_t length) {
case END_STREAM_REASON_TORPROTOCOL:
return SOCKS5_GENERAL_ERROR;
default:
- log_fn(LOG_WARN,"Reason for ending (%d) not recognized.",*payload);
+ log_fn(LOG_WARN,"Reason for ending (%d) not recognized.",reason);
return SOCKS5_GENERAL_ERROR;
}
}
@@ -817,9 +814,14 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
rh.length),
conn->stream_id, (int)conn->stream_size);
if (conn->socks_request && !conn->socks_request->has_finished) {
- socks5_reply_status_t status =
- connection_edge_end_reason_socks5_response(
- cell->payload+RELAY_HEADER_SIZE, rh.length);
+ socks5_reply_status_t status;
+ if (rh.length < 1) {
+ log_fn(LOG_WARN,"End cell arrived with length 0. Should be at least 1.");
+ status = SOCKS5_GENERAL_ERROR;
+ } else {
+ status = connection_edge_end_reason_socks5_response(
+ *(uint8_t*)cell->payload+RELAY_HEADER_SIZE);
+ }
connection_ap_handshake_socks_reply(conn, NULL, 0, status);
}
#ifdef HALF_OPEN