diff --git a/doc/control-spec.txt b/doc/control-spec.txt index 4e5b298386..96ac7e56a7 100644 --- a/doc/control-spec.txt +++ b/doc/control-spec.txt @@ -405,19 +405,32 @@ $Id$ 3.10. EXTENDCIRCUIT Sent from the client to the server. The format is: - "EXTENDCIRCUIT" SP CircuitID SP ServerID *("," ServerID) CRLF + "EXTENDCIRCUIT" SP CircuitID SP + ServerID *("," ServerID) SP + ("purpose=" Purpose) CRLF - This request takes one of two forms: either the Circuit ID is zero, in + This request takes one of two forms: either the CircuitID is zero, in which case it is a request for the server to build a new circuit according - to the specified path, or the Circuit ID is nonzero, in which case it is a + to the specified path, or the CircuitID is nonzero, in which case it is a request for the server to extend an existing circuit with that ID according to the specified path. - If the request is successful, the server sends a reply containing a message - body consisting of the Circuit ID of the (maybe newly created) circuit. - The syntax is "250" SP "EXTENDED" SP CircuitID CRLF. + If CircuitID is 0 and "purpose=" is specified, then the circuit's + purpose is set. Two choices are recognized: "general" and + "controller". If not specified, circuits are created as "general". -3.11. ATTACHSTREAM + If the request is successful, the server sends a reply containing a + message body consisting of the CircuitID of the (maybe newly created) + circuit. The syntax is "250" SP "EXTENDED" SP CircuitID CRLF. + +3.11. SETCIRCUITPURPOSE + + Sent from the client to the server. The format is: + "SETCIRCUITPURPOSE" SP CircuitID SP Purpose CRLF + + This changes the circuit's purpose. See EXTENDCIRCUIT above for details. + +3.12. ATTACHSTREAM Sent from the client to the server. The syntax is: "ATTACHSTREAM" SP StreamID SP CircuitID CRLF @@ -446,7 +459,7 @@ $Id$ via TC when "__LeaveStreamsUnattached" is false may cause a race between Tor and the controller, as both attempt to attach streams to circuits.} -3.12. POSTDESCRIPTOR +3.13. POSTDESCRIPTOR Sent from the client to the server. The syntax is: "+POSTDESCRIPTOR" CRLF Descriptor CRLF "." CRLF @@ -462,7 +475,7 @@ $Id$ why the server was not added. If the descriptor is added, Tor replies with "250 OK". -3.13. REDIRECTSTREAM +3.14. REDIRECTSTREAM Sent from the client to the server. The syntax is: "REDIRECTSTREAM" SP StreamID SP Address (SP Port) CRLF @@ -477,7 +490,7 @@ $Id$ Tor replies with "250 OK" on success. -3.14. CLOSESTREAM +3.15. CLOSESTREAM Sent from the client to the server. The syntax is: @@ -491,7 +504,7 @@ $Id$ Tor replies with "250 OK" on success, or a 512 if there aren't enough arguments, or a 552 if it doesn't recognize the StreamID or reason. -3.15. CLOSECIRCUIT +3.16. CLOSECIRCUIT The syntax is: CLOSECIRCUIT SP CircuitID *(SP Flag) CRLF @@ -506,7 +519,7 @@ $Id$ Tor replies with "250 OK" on success, or a 512 if there aren't enough arguments, or a 552 if it doesn't recognize the CircuitID. -3.16. QUIT +3.17. QUIT Tells the server to hang up on this controller connection. This command can be used before authenticating. diff --git a/src/or/control.c b/src/or/control.c index a843b98152..bd845bb04e 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -169,6 +169,8 @@ static int handle_control_getinfo(connection_t *conn, uint32_t len, const char *body); static int handle_control_extendcircuit(connection_t *conn, uint32_t len, const char *body); +static int handle_control_setcircuitpurpose(connection_t *conn, uint32_t len, + const char *body); static int handle_control_attachstream(connection_t *conn, uint32_t len, const char *body); static int handle_control_postdescriptor(connection_t *conn, uint32_t len, @@ -1541,6 +1543,25 @@ handle_control_getinfo(connection_t *conn, uint32_t len, const char *body) return 0; } +/** If string contains a recognized circuit purpose, + * possibly prefaced with the string "purpose=", then assign it + * and return 0. Otherwise return -1. */ +static int +get_purpose(char *string, uint8_t *purpose) +{ + if (!strcmpstart(string, "purpose=")) + string += strlen("purpose="); + + if (!strcmp(string, "general")) + *purpose = CIRCUIT_PURPOSE_C_GENERAL; + else if (!strcmp(string, "controller")) + *purpose = CIRCUIT_PURPOSE_CONTROLLER; + else { /* not a recognized purpose */ + return -1; + } + return 0; +} + /** Called when we get an EXTENDCIRCUIT message. Try to extend the listed * circuit, and report success or failure. */ static int @@ -1552,6 +1573,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len, circuit_t *circ = NULL; int zero_circ, v0; char reply[4]; + uint8_t intended_purpose = CIRCUIT_PURPOSE_C_GENERAL; v0 = STATE_IS_V0(conn->state); router_nicknames = smartlist_create(); @@ -1600,6 +1622,13 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len, if (!zero_circ && !circ) { goto done; } + if (zero_circ && smartlist_len(args)>2) { + if (get_purpose(smartlist_get(args,2), &intended_purpose) < 0) { + connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", + (char *)smartlist_get(args,2)); + goto done; + } + } } routers = smartlist_create(); @@ -1625,7 +1654,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len, if (zero_circ) { /* start a new circuit */ - circ = circuit_init(CIRCUIT_PURPOSE_C_GENERAL, 0, 0, 0); + circ = circuit_init(intended_purpose, 0, 0, 0); } /* now circ refers to something that is ready to be extended */ @@ -1677,6 +1706,44 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len, return 0; } +/** Called when we get a SETCIRCUITPURPOSE message. If we can find + * the circuit and it's a valid purpose, change it. */ +static int +handle_control_setcircuitpurpose(connection_t *conn, uint32_t len, + const char *body) +{ + circuit_t *circ; + uint8_t new_purpose; + smartlist_t *args = smartlist_create(); + smartlist_split_string(args, body, " ", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(args)<2) { + connection_printf_to_buf(conn, + "512 Missing argument to SETCIRCUITPURPOSE\r\n"); + goto done; + } + + if (!(circ = get_circ(smartlist_get(args,0)))) { + connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n", + (char*)smartlist_get(args, 0)); + goto done; + } + + if (get_purpose(smartlist_get(args,1), &new_purpose) < 0) { + connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", + (char *)smartlist_get(args,1)); + goto done; + } + + circ->purpose = new_purpose; + connection_write_str_to_buf("250 OK\r\n", conn); + +done: + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); + smartlist_free(args); + return 0; +} + /** Called when we get an ATTACHSTREAM message. Try to attach the requested * stream, and report success or failure. */ static int @@ -2187,6 +2254,9 @@ connection_control_process_inbuf_v1(connection_t *conn) } else if (!strcasecmp(conn->incoming_cmd, "EXTENDCIRCUIT")) { if (handle_control_extendcircuit(conn, data_len, args)) return -1; + } else if (!strcasecmp(conn->incoming_cmd, "SETCIRCUITPURPOSE")) { + if (handle_control_setcircuitpurpose(conn, data_len, args)) + return -1; } else if (!strcasecmp(conn->incoming_cmd, "ATTACHSTREAM")) { if (handle_control_attachstream(conn, data_len, args)) return -1; diff --git a/src/or/or.h b/src/or/or.h index eee2c80db5..efaa00235e 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -423,7 +423,9 @@ typedef enum { #define CIRCUIT_PURPOSE_S_REND_JOINED 16 /** A testing circuit; not meant to be used for actual traffic. */ #define CIRCUIT_PURPOSE_TESTING 17 -#define _CIRCUIT_PURPOSE_MAX 17 +/** A controller made this circuit and Tor should not use it. */ +#define CIRCUIT_PURPOSE_CONTROLLER 18 +#define _CIRCUIT_PURPOSE_MAX 18 /** True iff the circuit purpose p is for a circuit that * originated at this node. */