diff --git a/doc/spec/control-spec.txt b/doc/spec/control-spec.txt index 4057ffa741..7d3312a5a8 100644 --- a/doc/spec/control-spec.txt +++ b/doc/spec/control-spec.txt @@ -606,15 +606,20 @@ 3.10. EXTENDCIRCUIT Sent from the client to the server. The format is: - "EXTENDCIRCUIT" SP CircuitID SP - ServerSpec *("," ServerSpec) - [SP "purpose=" Purpose] CRLF + "EXTENDCIRCUIT" SP CircuitID + [SP ServerSpec *("," ServerSpec) + SP "purpose=" Purpose] CRLF 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 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. + which case it is a request for the server to build a new circuit, + 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 CircuitID is 0, the controller has the option of providing + a path for Tor to use to build the circuit. If it does not provide + a path, Tor will select one automatically from high capacity nodes + according to path-spec.txt. If CircuitID is 0 and "purpose=" is specified, then the circuit's purpose is set. Two choices are recognized: "general" and diff --git a/src/or/control.c b/src/or/control.c index c34848d454..a7e60d55f0 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -2055,27 +2055,58 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len, router_nicknames = smartlist_create(); - args = getargs_helper("EXTENDCIRCUIT", conn, body, 2, -1); + args = getargs_helper("EXTENDCIRCUIT", conn, body, 1, -1); if (!args) goto done; zero_circ = !strcmp("0", (char*)smartlist_get(args,0)); + + if (zero_circ) { + char *purp = NULL; + if (smartlist_len(args) == 2) { + // "EXTENDCIRCUIT 0 PURPOSE=foo" + purp = smartlist_get(args,1); + } else if (smartlist_len(args) == 3) { + // "EXTENDCIRCUIT 0 router1,router2 PURPOSE=foo" + purp = smartlist_get(args,2); + } + + if (purp && strcmpstart(purp, "purpose=") != 0) + purp = NULL; + + if (purp) { + intended_purpose = circuit_purpose_from_string(purp); + if (intended_purpose == CIRCUIT_PURPOSE_UNKNOWN) { + connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp); + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); + smartlist_free(args); + } + } + + if ((smartlist_len(args) == 1) || (purp && (smartlist_len(args) == 2))) { + // "EXTENDCIRCUIT 0" || EXTENDCIRCUIT 0 PURPOSE=foo" + circ = circuit_launch_by_router(intended_purpose, NULL, + CIRCLAUNCH_NEED_CAPACITY); + if (!circ) { + connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn); + } else { + connection_printf_to_buf(conn, "250 EXTENDED %lu\r\n", + (unsigned long)circ->global_identifier); + } + goto done; + } + // "EXTENDCIRCUIT 0 router1,router2" || + // "EXTENDCIRCUIT 0 router1,router2 PURPOSE=foo" + } + if (!zero_circ && !(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; } + smartlist_split_string(router_nicknames, smartlist_get(args,1), ",", 0, 0); - if (zero_circ && smartlist_len(args)>2) { - char *purp = smartlist_get(args,2); - intended_purpose = circuit_purpose_from_string(purp); - if (intended_purpose == CIRCUIT_PURPOSE_UNKNOWN) { - connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp); - SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); - smartlist_free(args); - goto done; - } - } SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); smartlist_free(args); if (!zero_circ && !circ) {