diff options
author | Nick Mathewson <nickm@torproject.org> | 2005-06-17 18:49:55 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2005-06-17 18:49:55 +0000 |
commit | f9f09f1c5e69fcbd9a1c840c1b86854dfee3af6a (patch) | |
tree | be08d99134fa5d6e5c61db0eea34a479ba7db470 | |
parent | 946892ab68bfeab4aa503d673adaa8374f778c55 (diff) | |
download | tor-f9f09f1c5e69fcbd9a1c840c1b86854dfee3af6a.tar.gz tor-f9f09f1c5e69fcbd9a1c840c1b86854dfee3af6a.zip |
Start implementing framework for revised control spec.
svn:r4447
-rw-r--r-- | src/or/buffers.c | 78 | ||||
-rw-r--r-- | src/or/connection.c | 14 | ||||
-rw-r--r-- | src/or/control.c | 440 | ||||
-rw-r--r-- | src/or/or.h | 17 |
4 files changed, 395 insertions, 154 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c index b0c3d912ab..4c813eb2ef 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -108,10 +108,22 @@ _buf_end(buf_t *buf) /** If the pointer <b>cp</b> has passed beyond the end of the buffer, wrap it * around. */ static INLINE char * -_wrap_ptr(buf_t *buf, char *cp) { +_wrap_ptr(buf_t *buf, char *cp) +{ return (cp >= buf->mem + buf->len) ? (cp - buf->len) : cp; } +/** Return the offset of <b>cp</b> within the buffer. */ +static INLINE int +_buf_offset(buf_t *buf, char *cp) +{ + if (cp >= buf->cur) + return cp - buf->cur; + else + /* return (cp - buf->mem) + buf->mem+buf->len - buf->cur */ + return cp + buf->len - buf->cur; +} + /** If the range of *<b>len</b> bytes starting at <b>at</b> wraps around the * end of the buffer, then set *<b>len</b> to the number of bytes starting * at <b>at</b>, and set *<b>more_len</b> to the number of bytes starting @@ -1099,7 +1111,7 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req) #define CONTROL_CMD_FRAGMENTHEADER 0x0010 #define CONTROL_CMD_FRAGMENT 0x0011 -/** If there is a complete control message waiting on buf, then store +/** If there is a complete version 0control message waiting on buf, then store * its contents into *<b>type_out</b>, store its body's length into * *<b>len_out</b>, allocate and store a string for its body into * *<b>body_out</b>, and return 1. (body_out will always be NUL-terminated, @@ -1107,11 +1119,11 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req) * * If there is not a complete control message waiting, return 0. * - * Return -1 on error. + * Return -1 on error; return -2 on "seems to be control protocol v1." */ int -fetch_from_buf_control(buf_t *buf, uint32_t *len_out, uint16_t *type_out, - char **body_out) +fetch_from_buf_control0(buf_t *buf, uint32_t *len_out, uint16_t *type_out, + char **body_out, int check_for_v1) { uint32_t msglen; uint16_t type; @@ -1128,10 +1140,13 @@ fetch_from_buf_control(buf_t *buf, uint32_t *len_out, uint16_t *type_out, peek_from_buf(tmp, 4, buf); msglen = ntohs(get_uint16(tmp)); + type = ntohs(get_uint16(tmp+2)); + if (type > 255 && check_for_v1) + return -2; + if (buf->datalen < 4 + (unsigned)msglen) return 0; - type = ntohs(get_uint16(tmp+2)); *len_out = msglen; *type_out = type; buf_remove_from_front(buf, 4); @@ -1145,6 +1160,57 @@ fetch_from_buf_control(buf_t *buf, uint32_t *len_out, uint16_t *type_out, return 1; } +/** DOCDOC */ +static char * +find_char_on_buf(buf_t *buf, char *start, size_t len, char c) +{ + size_t len_rest; + char *cp; + _split_range(buf, start, &len, &len_rest); + cp = memchr(buf->cur, c, len); + if (cp || !len_rest) + return cp; + return memchr(buf->mem, c, len_rest); +} + +static char * +find_crlf_on_buf(buf_t *buf, char *cp) +{ + char *next; + while (1) { + size_t remaining = buf->datalen - _buf_offset(buf,cp); + cp = find_char_on_buf(buf, cp, remaining, '\r'); + if (!cp) + return NULL; + next = _wrap_ptr(buf, cp+1); + if (next == _buf_end(buf)) + return NULL; + if (*next == '\n') + return cp; + cp = next; + } +} + +/* DOCDOC : 0 means 'need to read more'. means done, -1 means "grow buffer." */ +int +fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len) +{ + char *eol; + size_t sz; + /* Look for a CRLF. */ + if (!(eol = find_crlf_on_buf(buf, buf->cur))) + return 0; + sz = _buf_offset(buf, eol); + if (sz+2 > *data_len) { + *data_len = sz+2; + return -1; + } + fetch_from_buf(data_out, sz+2, buf); + *data_len = sz+2; + return 1; +} + + /** Log an error and exit if <b>buf</b> is corrupted. */ void assert_buf_ok(buf_t *buf) diff --git a/src/or/connection.c b/src/or/connection.c index 71b9dfe106..06464c9e84 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -120,8 +120,12 @@ conn_state_to_string(int type, int state) break; case CONN_TYPE_CONTROL: switch (state) { - case CONTROL_CONN_STATE_OPEN: return "open"; - case CONTROL_CONN_STATE_NEEDAUTH: return "waiting for authentication"; + case CONTROL_CONN_STATE_OPEN_V0: return "open (protocol v0)"; + case CONTROL_CONN_STATE_OPEN_V1: return "open (protocol v1)"; + case CONTROL_CONN_STATE_NEEDAUTH_V0: + return "waiting for authentication (protocol unknown)"; + case CONTROL_CONN_STATE_NEEDAUTH_V1: + return "waiting for authentication (protocol v1)"; } break; } @@ -667,7 +671,7 @@ connection_init_accepted_conn(connection_t *conn) conn->state = DIR_CONN_STATE_SERVER_COMMAND_WAIT; break; case CONN_TYPE_CONTROL: - conn->state = CONTROL_CONN_STATE_NEEDAUTH; + conn->state = CONTROL_CONN_STATE_NEEDAUTH_V0; break; } return 0; @@ -1578,7 +1582,9 @@ connection_state_is_open(connection_t *conn) if ((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) || (conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) || (conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN) || - (conn->type == CONN_TYPE_CONTROL && conn->state ==CONTROL_CONN_STATE_OPEN)) + (conn->type == CONN_TYPE_CONTROL && + (conn->state == CONTROL_CONN_STATE_OPEN_V0 || + conn->state == CONTROL_CONN_STATE_OPEN_V1))) return 1; return 0; diff --git a/src/or/control.c b/src/or/control.c index d802de2075..1136bab2cd 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -10,47 +10,38 @@ const char control_c_id[] = "$Id$"; #include "or.h" -/* Protocol outline: a bidirectional stream, over which each side - * sends a series of messages. Each message has a two-byte length field, - * a two-byte typecode, and a variable-length body whose length is - * given in the length field. - * - * By default, the server only sends messages in response to client messages. - * Every client message gets a message in response. The client may, however, - * _request_ that other messages be delivered asynchronously. - * - * - * Every message type is either client-only or server-only, and every - * server message type is either synchronous-only (only occurs in - * response to a client request) or asynchronous-only (never is an - * answer to a client request. - * - * See control-spec.txt for full details. +#define STATE_IS_OPEN(s) ((s) == CONTROL_CONN_STATE_OPEN_V0 || \ + (s) == CONTROL_CONN_STATE_OPEN_V1) +#define STATE_IS_V0(s) ((s) == CONTROL_CONN_STATE_NEEDAUTH_V0 || \ + (s) == CONTROL_CONN_STATE_OPEN_V0) + +/* + * See control-spec.txt and control-spec-v0.txt for full details on protocol(s). */ /* Recognized message type codes. */ -#define CONTROL_CMD_ERROR 0x0000 -#define CONTROL_CMD_DONE 0x0001 -#define CONTROL_CMD_SETCONF 0x0002 -#define CONTROL_CMD_GETCONF 0x0003 -#define CONTROL_CMD_CONFVALUE 0x0004 -#define CONTROL_CMD_SETEVENTS 0x0005 -#define CONTROL_CMD_EVENT 0x0006 -#define CONTROL_CMD_AUTHENTICATE 0x0007 -#define CONTROL_CMD_SAVECONF 0x0008 -#define CONTROL_CMD_SIGNAL 0x0009 -#define CONTROL_CMD_MAPADDRESS 0x000A -#define CONTROL_CMD_GETINFO 0x000B -#define CONTROL_CMD_INFOVALUE 0x000C -#define CONTROL_CMD_EXTENDCIRCUIT 0x000D -#define CONTROL_CMD_ATTACHSTREAM 0x000E -#define CONTROL_CMD_POSTDESCRIPTOR 0x000F -#define CONTROL_CMD_FRAGMENTHEADER 0x0010 -#define CONTROL_CMD_FRAGMENT 0x0011 -#define CONTROL_CMD_REDIRECTSTREAM 0x0012 -#define CONTROL_CMD_CLOSESTREAM 0x0013 -#define CONTROL_CMD_CLOSECIRCUIT 0x0014 -#define _CONTROL_CMD_MAX_RECOGNIZED 0x0014 +#define CONTROL0_CMD_ERROR 0x0000 +#define CONTROL0_CMD_DONE 0x0001 +#define CONTROL0_CMD_SETCONF 0x0002 +#define CONTROL0_CMD_GETCONF 0x0003 +#define CONTROL0_CMD_CONFVALUE 0x0004 +#define CONTROL0_CMD_SETEVENTS 0x0005 +#define CONTROL0_CMD_EVENT 0x0006 +#define CONTROL0_CMD_AUTHENTICATE 0x0007 +#define CONTROL0_CMD_SAVECONF 0x0008 +#define CONTROL0_CMD_SIGNAL 0x0009 +#define CONTROL0_CMD_MAPADDRESS 0x000A +#define CONTROL0_CMD_GETINFO 0x000B +#define CONTROL0_CMD_INFOVALUE 0x000C +#define CONTROL0_CMD_EXTENDCIRCUIT 0x000D +#define CONTROL0_CMD_ATTACHSTREAM 0x000E +#define CONTROL0_CMD_POSTDESCRIPTOR 0x000F +#define CONTROL0_CMD_FRAGMENTHEADER 0x0010 +#define CONTROL0_CMD_FRAGMENT 0x0011 +#define CONTROL0_CMD_REDIRECTSTREAM 0x0012 +#define CONTROL0_CMD_CLOSESTREAM 0x0013 +#define CONTROL0_CMD_CLOSECIRCUIT 0x0014 +#define _CONTROL0_CMD_MAX_RECOGNIZED 0x0014 /* Recognized error codes. */ #define ERR_UNSPECIFIED 0x0000 @@ -84,7 +75,7 @@ const char control_c_id[] = "$Id$"; /** Array mapping from message type codes to human-readable message * type names. */ -static const char * CONTROL_COMMANDS[_CONTROL_CMD_MAX_RECOGNIZED+1] = { +static const char * CONTROL0_COMMANDS[_CONTROL0_CMD_MAX_RECOGNIZED+1] = { "error", "done", "setconf", @@ -127,13 +118,13 @@ static int authentication_cookie_is_set = 0; static char authentication_cookie[AUTHENTICATION_COOKIE_LEN]; static void update_global_event_mask(void); -static void send_control_message(connection_t *conn, uint16_t type, +static void send_control0_message(connection_t *conn, uint16_t type, uint32_t len, const char *body); static void send_control_done(connection_t *conn); static void send_control_done2(connection_t *conn, const char *msg, size_t len); -static void send_control_error(connection_t *conn, uint16_t error, +static void send_control0_error(connection_t *conn, uint16_t error, const char *message); -static void send_control_event(uint16_t event, uint32_t len, const char *body); +static void send_control0_event(uint16_t event, uint32_t len, const char *body); static int handle_control_setconf(connection_t *conn, uint32_t len, char *body); static int handle_control_getconf(connection_t *conn, uint32_t len, @@ -168,7 +159,7 @@ static int handle_control_closecircuit(connection_t *conn, uint32_t len, static INLINE const char * control_cmd_to_string(uint16_t cmd) { - return (cmd<=_CONTROL_CMD_MAX_RECOGNIZED) ? CONTROL_COMMANDS[cmd] : "Unknown"; + return (cmd<=_CONTROL0_CMD_MAX_RECOGNIZED) ? CONTROL0_COMMANDS[cmd] : "Unknown"; } static INLINE int @@ -209,7 +200,7 @@ update_global_event_mask(void) get_connection_array(&conns, &n_conns); for (i = 0; i < n_conns; ++i) { if (conns[i]->type == CONN_TYPE_CONTROL && - conns[i]->state == CONTROL_CONN_STATE_OPEN) { + STATE_IS_OPEN(conns[i]->state)) { global_event_mask |= conns[i]->event_mask; } } @@ -247,16 +238,45 @@ adjust_event_log_severity(void) control_event_logmsg); } +static INLINE void +connection_write_str_to_buf(const char *s, connection_t *conn) +{ + size_t len = strlen(s); + connection_write_to_buf(s, len, conn); +} + + +static void +connection_printf_to_buf(connection_t *conn, const char *format, ...) +{ + va_list ap; + char buf[1024]; + int r; + size_t len; + va_start(ap,format); + r = tor_snprintf(buf, sizeof(buf), format, ap); + va_end(ap); + len = strlen(buf); + if (memcmp("\r\n\0", buf+len-2, 3)) { + buf[1023] = '\0'; + buf[1022] = '\n'; + buf[1021] = '\r'; + } + connection_write_to_buf(buf, len, conn); +} + + /** Send a message of type <b>type</b> containing <b>len</b> bytes * from <b>body</b> along the control connection <b>conn</b> */ static void -send_control_message(connection_t *conn, uint16_t type, uint32_t len, +send_control0_message(connection_t *conn, uint16_t type, uint32_t len, const char *body) { char buf[10]; tor_assert(conn); + tor_assert(STATE_IS_V0(conn->state)); tor_assert(len || !body); - tor_assert(type <= _CONTROL_CMD_MAX_RECOGNIZED); + tor_assert(type <= _CONTROL0_CMD_MAX_RECOGNIZED); if (len < 65536) { set_uint16(buf, htons(len)); set_uint16(buf+2, htons(type)); @@ -265,7 +285,7 @@ send_control_message(connection_t *conn, uint16_t type, uint32_t len, connection_write_to_buf(body, len, conn); } else { set_uint16(buf, htons(65535)); - set_uint16(buf+2, htons(CONTROL_CMD_FRAGMENTHEADER)); + set_uint16(buf+2, htons(CONTROL0_CMD_FRAGMENTHEADER)); set_uint16(buf+4, htons(type)); set_uint32(buf+6, htonl(len)); connection_write_to_buf(buf, 10, conn); @@ -275,7 +295,7 @@ send_control_message(connection_t *conn, uint16_t type, uint32_t len, while (len) { size_t chunklen = (len<65535)?len:65535; set_uint16(buf, htons((uint16_t)chunklen)); - set_uint16(buf+2, htons(CONTROL_CMD_FRAGMENT)); + set_uint16(buf+2, htons(CONTROL0_CMD_FRAGMENT)); connection_write_to_buf(buf, 4, conn); connection_write_to_buf(body, chunklen, conn); len -= chunklen; @@ -288,7 +308,11 @@ send_control_message(connection_t *conn, uint16_t type, uint32_t len, static void send_control_done(connection_t *conn) { - send_control_message(conn, CONTROL_CMD_DONE, 0, NULL); + if (STATE_IS_V0(conn->state)) { + send_control0_message(conn, CONTROL0_CMD_DONE, 0, NULL); + } else { + connection_write_str_to_buf("250 OK\r\n", conn); + } } /** DOCDOC */ @@ -297,13 +321,13 @@ send_control_done2(connection_t *conn, const char *msg, size_t len) { if (len==0) len = strlen(msg); - send_control_message(conn, CONTROL_CMD_DONE, len, msg); + send_control0_message(conn, CONTROL0_CMD_DONE, len, msg); } /** Send an error message with error code <b>error</b> and body * <b>message</b> down the connection <b>conn</b> */ static void -send_control_error(connection_t *conn, uint16_t error, const char *message) +send_control0_error(connection_t *conn, uint16_t error, const char *message) { char buf[256]; size_t len; @@ -311,14 +335,14 @@ send_control_error(connection_t *conn, uint16_t error, const char *message) len = strlen(message); tor_assert(len < (256-2)); memcpy(buf+2, message, len); - send_control_message(conn, CONTROL_CMD_ERROR, (uint16_t)(len+2), buf); + send_control0_message(conn, CONTROL0_CMD_ERROR, (uint16_t)(len+2), buf); } /** Send an 'event' message of event type <b>event</b>, containing * <b>len</b> bytes in <b>body</b> to every control connection that * is interested in it. */ static void -send_control_event(uint16_t event, uint32_t len, const char *body) +send_control0_event(uint16_t event, uint32_t len, const char *body) { connection_t **conns; int n_conns, i; @@ -335,9 +359,9 @@ send_control_event(uint16_t event, uint32_t len, const char *body) get_connection_array(&conns, &n_conns); for (i = 0; i < n_conns; ++i) { if (conns[i]->type == CONN_TYPE_CONTROL && - conns[i]->state == CONTROL_CONN_STATE_OPEN && + conns[i]->state == CONTROL_CONN_STATE_OPEN_V0 && conns[i]->event_mask & (1<<event)) { - send_control_message(conns[i], CONTROL_CMD_EVENT, buflen, buf); + send_control0_message(conns[i], CONTROL0_CMD_EVENT, buflen, buf); if (event == EVENT_ERR_MSG) { _connection_controller_force_write(conns[i]); } @@ -357,17 +381,17 @@ handle_control_setconf(connection_t *conn, uint32_t len, char *body) if (config_get_lines(body, &lines) < 0) { log_fn(LOG_WARN,"Controller gave us config lines we can't parse."); - send_control_error(conn, ERR_SYNTAX, "Couldn't parse configuration"); + send_control0_error(conn, ERR_SYNTAX, "Couldn't parse configuration"); return 0; } if ((r=config_trial_assign(lines, 1)) < 0) { log_fn(LOG_WARN,"Controller gave us config lines that didn't validate."); if (r==-1) { - send_control_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, + send_control0_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, "Unrecognized option"); } else { - send_control_error(conn, ERR_INVALID_CONFIG_VALUE,"Invalid option value"); + send_control0_error(conn, ERR_INVALID_CONFIG_VALUE,"Invalid option value"); } config_free_lines(lines); return 0; @@ -401,7 +425,7 @@ handle_control_getconf(connection_t *conn, uint32_t body_len, const char *body) { int recognized = config_option_is_recognized(q); if (!recognized) { - send_control_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, q); + send_control0_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, q); goto done; } else { struct config_line_t *answer = config_get_assigned_option(options,q); @@ -423,7 +447,7 @@ handle_control_getconf(connection_t *conn, uint32_t body_len, const char *body) }); msg = smartlist_join_strings(answers, "", 0, &msg_len); - send_control_message(conn, CONTROL_CMD_CONFVALUE, + send_control0_message(conn, CONTROL0_CMD_CONFVALUE, (uint16_t)msg_len, msg_len?msg:NULL); done: @@ -444,7 +468,7 @@ handle_control_setevents(connection_t *conn, uint32_t len, const char *body) uint16_t event_code; uint32_t event_mask = 0; if (len % 2) { - send_control_error(conn, ERR_SYNTAX, + send_control0_error(conn, ERR_SYNTAX, "Odd number of bytes in setevents message"); return 0; } @@ -452,7 +476,7 @@ handle_control_setevents(connection_t *conn, uint32_t len, const char *body) for (; len; len -= 2, body += 2) { event_code = ntohs(get_uint16(body)); if (event_code < _EVENT_MIN || event_code > _EVENT_MAX) { - send_control_error(conn, ERR_UNRECOGNIZED_EVENT_CODE, + send_control0_error(conn, ERR_UNRECOGNIZED_EVENT_CODE, "Unrecognized event code"); return 0; } @@ -525,12 +549,15 @@ handle_control_authenticate(connection_t *conn, uint32_t len, const char *body) } err: - send_control_error(conn, ERR_REJECTED_AUTHENTICATION,"Authentication failed"); + send_control0_error(conn, ERR_REJECTED_AUTHENTICATION,"Authentication failed"); return 0; ok: log_fn(LOG_INFO, "Authenticated control connection (%d)", conn->s); send_control_done(conn); - conn->state = CONTROL_CONN_STATE_OPEN; + if (STATE_IS_V0(conn->state)) + conn->state = CONTROL_CONN_STATE_OPEN_V0; + else + conn->state = CONTROL_CONN_STATE_OPEN_V1; return 0; } @@ -540,7 +567,7 @@ handle_control_saveconf(connection_t *conn, uint32_t len, const char *body) { if (save_current_config()<0) { - send_control_error(conn, ERR_INTERNAL, + send_control0_error(conn, ERR_INTERNAL, "Unable to write configuration to disk."); } else { send_control_done(conn); @@ -554,10 +581,10 @@ handle_control_signal(connection_t *conn, uint32_t len, const char *body) { if (len != 1) { - send_control_error(conn, ERR_SYNTAX, + send_control0_error(conn, ERR_SYNTAX, "Body of SIGNAL command too long or too short."); } else if (control_signal_act((uint8_t)body[0]) < 0) { - send_control_error(conn, ERR_SYNTAX, "Unrecognized signal number."); + send_control0_error(conn, ERR_SYNTAX, "Unrecognized signal number."); } else { send_control_done(conn); } @@ -689,10 +716,10 @@ handle_control_getinfo(connection_t *conn, uint32_t len, const char *body) SMARTLIST_FOREACH(questions, const char *, q, { if (handle_getinfo_helper(q, &ans) < 0) { - send_control_error(conn, ERR_INTERNAL, body); + send_control0_error(conn, ERR_INTERNAL, body); goto done; } if (!ans) { - send_control_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, body); + send_control0_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, body); goto done; } smartlist_add(answers, tor_strdup(q)); @@ -701,7 +728,7 @@ handle_control_getinfo(connection_t *conn, uint32_t len, const char *body) msg = smartlist_join_strings2(answers, "\0", 1, 1, &msg_len); tor_assert(msg_len > 0); /* it will at least be terminated */ - send_control_message(conn, CONTROL_CMD_INFOVALUE, + send_control0_message(conn, CONTROL0_CMD_INFOVALUE, msg_len, msg); done: @@ -724,7 +751,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len, circuit_t *circ; char reply[4]; if (len<5) { - send_control_error(conn, ERR_SYNTAX, "extendcircuit message too short"); + send_control0_error(conn, ERR_SYNTAX, "extendcircuit message too short"); return 0; } @@ -735,13 +762,13 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len, { routerinfo_t *r = router_get_by_nickname(n); if (!r) { - send_control_error(conn, ERR_NO_ROUTER, n); + send_control0_error(conn, ERR_NO_ROUTER, n); goto done; } smartlist_add(routers, r); }); if (!smartlist_len(routers)) { - send_control_error(conn, ERR_SYNTAX, "No router names provided"); + send_control0_error(conn, ERR_SYNTAX, "No router names provided"); goto done; } @@ -752,7 +779,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len, } else { circ = circuit_get_by_global_id(circ_id); if (!circ) { - send_control_error(conn, ERR_NO_CIRC, + send_control0_error(conn, ERR_NO_CIRC, "No circuit found with given ID"); goto done; } @@ -769,7 +796,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len, if (!circ_id) { if (circuit_handle_first_hop(circ) < 0) { circuit_mark_for_close(circ); - send_control_error(conn, ERR_INTERNAL, "couldn't start circuit"); + send_control0_error(conn, ERR_INTERNAL, "couldn't start circuit"); goto done; } } else { @@ -778,7 +805,7 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len, if (circuit_send_next_onion_skin(circ) < 0) { log_fn(LOG_INFO,"send_next_onion_skin failed; circuit marked for closing."); circuit_mark_for_close(circ); - send_control_error(conn, ERR_INTERNAL, "couldn't send onion skin"); + send_control0_error(conn, ERR_INTERNAL, "couldn't send onion skin"); goto done; } } @@ -804,7 +831,7 @@ handle_control_attachstream(connection_t *conn, uint32_t len, circuit_t *circ; if (len < 8) { - send_control_error(conn, ERR_SYNTAX, "attachstream message too short"); + send_control0_error(conn, ERR_SYNTAX, "attachstream message too short"); return 0; } @@ -812,12 +839,12 @@ handle_control_attachstream(connection_t *conn, uint32_t len, circ_id = ntohl(get_uint32(body+4)); if (!(ap_conn = connection_get_by_global_id(conn_id))) { - send_control_error(conn, ERR_NO_STREAM, + send_control0_error(conn, ERR_NO_STREAM, "No connection found with given ID"); return 0; } if (ap_conn->state != AP_CONN_STATE_CONTROLLER_WAIT) { - send_control_error(conn, ERR_NO_STREAM, + send_control0_error(conn, ERR_NO_STREAM, "Connection was not managed by controller."); return 0; } @@ -831,15 +858,15 @@ handle_control_attachstream(connection_t *conn, uint32_t len, } if (!(circ = circuit_get_by_global_id(circ_id))) { - send_control_error(conn, ERR_NO_CIRC, "No circuit found with given ID"); + send_control0_error(conn, ERR_NO_CIRC, "No circuit found with given ID"); return 0; } if (circ->state != CIRCUIT_STATE_OPEN) { - send_control_error(conn, ERR_INTERNAL, "Refuse to attach stream to non-open circ."); + send_control0_error(conn, ERR_INTERNAL, "Refuse to attach stream to non-open circ."); return 0; } if (connection_ap_handshake_attach_chosen_circuit(ap_conn, circ) != 1) { - send_control_error(conn, ERR_INTERNAL, "Unable to attach stream."); + send_control0_error(conn, ERR_INTERNAL, "Unable to attach stream."); return 0; } send_control_done(conn); @@ -854,7 +881,7 @@ handle_control_postdescriptor(connection_t *conn, uint32_t len, const char *msg=NULL; switch (router_load_single_router(body, &msg)) { case -1: - send_control_error(conn,ERR_SYNTAX,msg?msg: "Could not parse descriptor"); + send_control0_error(conn,ERR_SYNTAX,msg?msg: "Could not parse descriptor"); break; case 0: send_control_done2(conn,msg?msg: "Descriptor not added",0); @@ -875,7 +902,7 @@ handle_control_redirectstream(connection_t *conn, uint32_t len, connection_t *ap_conn; uint32_t conn_id; if (len < 6) { - send_control_error(conn, ERR_SYNTAX, "redirectstream message too short"); + send_control0_error(conn, ERR_SYNTAX, "redirectstream message too short"); return 0; } conn_id = ntohl(get_uint32(body)); @@ -883,7 +910,7 @@ handle_control_redirectstream(connection_t *conn, uint32_t len, 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, + send_control0_error(conn, ERR_NO_STREAM, "No AP connection found with given ID"); return 0; } @@ -904,7 +931,7 @@ handle_control_closestream(connection_t *conn, uint32_t len, uint8_t reason; if (len < 6) { - send_control_error(conn, ERR_SYNTAX, "closestream message too short"); + send_control0_error(conn, ERR_SYNTAX, "closestream message too short"); return 0; } @@ -914,7 +941,7 @@ handle_control_closestream(connection_t *conn, uint32_t len, 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, + send_control0_error(conn, ERR_NO_STREAM, "No AP connection found with given ID"); return 0; } @@ -933,14 +960,14 @@ handle_control_closecircuit(connection_t *conn, uint32_t len, int safe; if (len < 5) { - send_control_error(conn, ERR_SYNTAX, "closecircuit message too short"); + send_control0_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, + send_control0_error(conn, ERR_NO_CIRC, "No circuit found with given ID"); return 0; } @@ -958,13 +985,13 @@ static int handle_control_fragments(connection_t *conn, uint16_t command_type, uint32_t body_len, char *body) { - if (command_type == CONTROL_CMD_FRAGMENTHEADER) { + if (command_type == CONTROL0_CMD_FRAGMENTHEADER) { if (conn->incoming_cmd) { log_fn(LOG_WARN, "Dropping incomplete fragmented command"); tor_free(conn->incoming_cmd); } if (body_len < 6) { - send_control_error(conn, ERR_SYNTAX, "FRAGMENTHEADER too short."); + send_control0_error(conn, ERR_SYNTAX, "FRAGMENTHEADER too short."); return 0; } conn->incoming_cmd_type = ntohs(get_uint16(body)); @@ -973,9 +1000,9 @@ handle_control_fragments(connection_t *conn, uint16_t command_type, conn->incoming_cmd = tor_malloc(conn->incoming_cmd_len); body += 6; body_len -= 6; - } else if (command_type == CONTROL_CMD_FRAGMENT) { + } else if (command_type == CONTROL0_CMD_FRAGMENT) { if (!conn->incoming_cmd) { - send_control_error(conn, ERR_SYNTAX, "Out-of-place FRAGMENT"); + send_control0_error(conn, ERR_SYNTAX, "Out-of-place FRAGMENT"); return 0; } } else { @@ -984,7 +1011,7 @@ handle_control_fragments(connection_t *conn, uint16_t command_type, if (conn->incoming_cmd_cur_len + body_len > conn->incoming_cmd_len) { tor_free(conn->incoming_cmd); - send_control_error(conn, ERR_SYNTAX, + send_control0_error(conn, ERR_SYNTAX, "Fragmented data exceeds declared length"); return 0; } @@ -1009,27 +1036,150 @@ connection_control_finished_flushing(connection_t *conn) int connection_control_reached_eof(connection_t *conn) { + tor_assert(conn); + tor_assert(conn->type == CONN_TYPE_CONTROL); + log_fn(LOG_INFO,"Control connection reached EOF. Closing."); connection_mark_for_close(conn); return 0; } -/** Called when <b>conn</b> has received more bytes on its inbuf. - */ -int -connection_control_process_inbuf(connection_t *conn) +static int +connection_control_process_inbuf_v1(connection_t *conn) { - uint32_t body_len; - uint16_t command_type; - char *body; + size_t data_len, cmd_len; tor_assert(conn); tor_assert(conn->type == CONN_TYPE_CONTROL); + tor_assert(conn->state == CONTROL_CONN_STATE_OPEN_V1 || + conn->state == CONTROL_CONN_STATE_NEEDAUTH_V1); + + if (!conn->incoming_cmd) { + conn->incoming_cmd = tor_malloc(1024); + conn->incoming_cmd_len = 1024; + conn->incoming_cmd_cur_len = 0; + } + + again: + while (1) { + size_t last_idx; + int r; + while (1) { + data_len = conn->incoming_cmd_len - conn->incoming_cmd_cur_len; + r = fetch_from_buf_line(conn->inbuf, + conn->incoming_cmd+conn->incoming_cmd_cur_len, + &data_len); + if (r == 0) + /* Line not all here yet. Wait. */ + return 0; + else if (r == -1) { + while (conn->incoming_cmd_len < data_len) + conn->incoming_cmd_len *= 2; + conn->incoming_cmd = tor_realloc(conn->incoming_cmd, conn->incoming_cmd_len); + } + } while (r != 1); + + tor_assert(data_len); + + last_idx = conn->incoming_cmd_cur_len; + conn->incoming_cmd_cur_len += data_len; + + /* We have appended a line to incoming_cmd. Is the command done? */ + if (last_idx == 0 && *conn->incoming_cmd != '+') + /* One line command, didn't start with '+'. */ + break; + if (last_idx+3 == conn->incoming_cmd_cur_len && + !memcmp(conn->incoming_cmd + last_idx, ".\r\n", 3)) + /* Just appended ".\r\n"; we're done. */ + break; + /* Otherwise, read another line. */ + } + data_len = conn->incoming_cmd_cur_len; + /* Okay, we now have a command sitting on conn->incoming_cmd. See if we + * recognize it. + */ + while (cmd_len < data_len && !isspace(conn->incoming_cmd[cmd_len])) + ++cmd_len; + + if (conn->state == CONTROL_CONN_STATE_NEEDAUTH_V1 && + strncasecmp(conn->incoming_cmd, "AUTHENTICATE", cmd_len)) { + connection_write_str_to_buf("514 Authentication required.\r\n", conn); + conn->incoming_cmd_cur_len = 0; + goto again; + } + + if (!strncasecmp(conn->incoming_cmd, "SETCONF", cmd_len)) { + if (handle_control_setconf(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "GETCONF", cmd_len)) { + if (handle_control_getconf(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "SETEVENTS", cmd_len)) { + if (handle_control_setevents(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "AUTENTICATE", cmd_len)) { + if (handle_control_authenticate(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "SAVECONF", cmd_len)) { + if (handle_control_saveconf(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "SIGNAL", cmd_len)) { + if (handle_control_signal(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "MAPADDRESS", cmd_len)) { + if (handle_control_mapaddress(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "GETINFO", cmd_len)) { + if (handle_control_getinfo(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "EXTENDCIRCUIT", cmd_len)) { + if (handle_control_extendcircuit(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "ATTACHSTREAM", cmd_len)) { + if (handle_control_attachstream(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "+POSTDESCRIPTOR", cmd_len)) { + if (handle_control_postdescriptor(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "REDIRECTSTREAM", cmd_len)) { + if (handle_control_redirectstream(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "CLOSESTREAM", cmd_len)) { + if (handle_control_closestream(conn, 0, NULL)) + return -1; + } else if (!strncasecmp(conn->incoming_cmd, "CLOSECIRCUIT", cmd_len)) { + if (handle_control_closecircuit(conn, 0, NULL)) + return -1; + } else { + conn->incoming_cmd[cmd_len] = '\0'; + connection_printf_to_buf(conn, "510 Unrecognizd command \"%s\"\r\n", + conn->incoming_cmd); + } + + conn->incoming_cmd_cur_len = 0; + goto again; + +} + + +static int +connection_control_process_inbuf_v0(connection_t *conn) +{ + uint32_t body_len; + uint16_t command_type; + char *body; again: /* Try to suck a control message from the buffer. */ - switch (fetch_from_buf_control(conn->inbuf, &body_len, &command_type, &body)) + switch (fetch_from_buf_control0(conn->inbuf, &body_len, &command_type, &body, + conn->state == CONTROL_CONN_STATE_NEEDAUTH_V0)) { + case -2: + tor_free(body); + log_fn(LOG_INFO, "Detected v1 contol protocol on connection (fd %d)", + conn->s); + conn->state = CONTROL_CONN_STATE_NEEDAUTH_V1; + return connection_control_process_inbuf_v1(conn); case -1: tor_free(body); log_fn(LOG_WARN, "Error in control command. Failing."); @@ -1046,17 +1196,17 @@ connection_control_process_inbuf(connection_t *conn) /* We got a command. If we need authentication, only authentication * commands will be considered. */ - if (conn->state == CONTROL_CONN_STATE_NEEDAUTH && - command_type != CONTROL_CMD_AUTHENTICATE) { + if (conn->state == CONTROL_CONN_STATE_NEEDAUTH_V0 && + command_type != CONTROL0_CMD_AUTHENTICATE) { log_fn(LOG_WARN, "Rejecting '%s' command; authentication needed.", control_cmd_to_string(command_type)); - send_control_error(conn, ERR_UNAUTHORIZED, "Authentication required"); + send_control0_error(conn, ERR_UNAUTHORIZED, "Authentication required"); tor_free(body); goto again; } - if (command_type == CONTROL_CMD_FRAGMENTHEADER || - command_type == CONTROL_CMD_FRAGMENT) { + if (command_type == CONTROL0_CMD_FRAGMENTHEADER || + command_type == CONTROL0_CMD_FRAGMENT) { if (handle_control_fragments(conn, command_type, body_len, body)) return -1; tor_free(body); @@ -1075,80 +1225,80 @@ connection_control_process_inbuf(connection_t *conn) /* Okay, we're willing to process the command. */ switch (command_type) { - case CONTROL_CMD_SETCONF: + case CONTROL0_CMD_SETCONF: if (handle_control_setconf(conn, body_len, body)) return -1; break; - case CONTROL_CMD_GETCONF: + case CONTROL0_CMD_GETCONF: if (handle_control_getconf(conn, body_len, body)) return -1; break; - case CONTROL_CMD_SETEVENTS: + case CONTROL0_CMD_SETEVENTS: if (handle_control_setevents(conn, body_len, body)) return -1; break; - case CONTROL_CMD_AUTHENTICATE: + case CONTROL0_CMD_AUTHENTICATE: if (handle_control_authenticate(conn, body_len, body)) return -1; break; - case CONTROL_CMD_SAVECONF: + case CONTROL0_CMD_SAVECONF: if (handle_control_saveconf(conn, body_len, body)) return -1; break; - case CONTROL_CMD_SIGNAL: + case CONTROL0_CMD_SIGNAL: if (handle_control_signal(conn, body_len, body)) return -1; break; - case CONTROL_CMD_MAPADDRESS: + case CONTROL0_CMD_MAPADDRESS: if (handle_control_mapaddress(conn, body_len, body)) return -1; break; - case CONTROL_CMD_GETINFO: + case CONTROL0_CMD_GETINFO: if (handle_control_getinfo(conn, body_len, body)) return -1; break; - case CONTROL_CMD_EXTENDCIRCUIT: + case CONTROL0_CMD_EXTENDCIRCUIT: if (handle_control_extendcircuit(conn, body_len, body)) return -1; break; - case CONTROL_CMD_ATTACHSTREAM: + case CONTROL0_CMD_ATTACHSTREAM: if (handle_control_attachstream(conn, body_len, body)) return -1; break; - case CONTROL_CMD_POSTDESCRIPTOR: + case CONTROL0_CMD_POSTDESCRIPTOR: if (handle_control_postdescriptor(conn, body_len, body)) return -1; break; - case CONTROL_CMD_REDIRECTSTREAM: + case CONTROL0_CMD_REDIRECTSTREAM: if (handle_control_redirectstream(conn, body_len, body)) return -1; break; - case CONTROL_CMD_CLOSESTREAM: + case CONTROL0_CMD_CLOSESTREAM: if (handle_control_closestream(conn, body_len, body)) return -1; break; - case CONTROL_CMD_CLOSECIRCUIT: + case CONTROL0_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: - case CONTROL_CMD_EVENT: - case CONTROL_CMD_INFOVALUE: + case CONTROL0_CMD_ERROR: + case CONTROL0_CMD_DONE: + case CONTROL0_CMD_CONFVALUE: + case CONTROL0_CMD_EVENT: + case CONTROL0_CMD_INFOVALUE: log_fn(LOG_WARN, "Received client-only '%s' command; ignoring.", control_cmd_to_string(command_type)); - send_control_error(conn, ERR_UNRECOGNIZED_TYPE, + send_control0_error(conn, ERR_UNRECOGNIZED_TYPE, "Command type only valid from server to tor client"); break; - case CONTROL_CMD_FRAGMENTHEADER: - case CONTROL_CMD_FRAGMENT: + case CONTROL0_CMD_FRAGMENTHEADER: + case CONTROL0_CMD_FRAGMENT: log_fn(LOG_WARN, "Recieved command fragment out of order; ignoring."); - send_control_error(conn, ERR_SYNTAX, "Bad fragmentation on command."); + send_control0_error(conn, ERR_SYNTAX, "Bad fragmentation on command."); default: log_fn(LOG_WARN, "Received unrecognized command type %d; ignoring.", (int)command_type); - send_control_error(conn, ERR_UNRECOGNIZED_TYPE, + send_control0_error(conn, ERR_UNRECOGNIZED_TYPE, "Unrecognized command type"); break; } @@ -1156,6 +1306,20 @@ connection_control_process_inbuf(connection_t *conn) goto again; /* There might be more data. */ } +/** Called when <b>conn</b> has received more bytes on its inbuf. + */ +int +connection_control_process_inbuf(connection_t *conn) +{ + tor_assert(conn); + tor_assert(conn->type == CONN_TYPE_CONTROL); + + if (STATE_IS_V0(conn->state)) + return connection_control_process_inbuf_v0(conn); + else + return connection_control_process_inbuf_v1(conn); +} + /** Something has happened to circuit <b>circ</b>: tell any interested * control connections. */ int @@ -1175,7 +1339,7 @@ control_event_circuit_status(circuit_t *circ, circuit_status_event_t tp) set_uint32(msg+1, htonl(circ->global_identifier)); strlcpy(msg+5,path,path_len+1); - send_control_event(EVENT_CIRCUIT_STATUS, (uint32_t)(path_len+6), msg); + send_control0_event(EVENT_CIRCUIT_STATUS, (uint32_t)(path_len+6), msg); tor_free(path); tor_free(msg); return 0; @@ -1207,7 +1371,7 @@ control_event_stream_status(connection_t *conn, stream_status_event_t tp) set_uint32(msg+1, htonl(conn->global_identifier)); strlcpy(msg+5, buf, len+1); - send_control_event(EVENT_STREAM_STATUS, (uint32_t)(5+len+1), msg); + send_control0_event(EVENT_STREAM_STATUS, (uint32_t)(5+len+1), msg); tor_free(msg); return 0; } @@ -1228,7 +1392,7 @@ control_event_or_conn_status(connection_t *conn,or_conn_status_event_t tp) buf[0] = (uint8_t)tp; strlcpy(buf+1,conn->nickname,sizeof(buf)-1); len = strlen(buf+1); - send_control_event(EVENT_OR_CONN_STATUS, (uint32_t)(len+1), buf); + send_control0_event(EVENT_OR_CONN_STATUS, (uint32_t)(len+1), buf); return 0; } @@ -1244,7 +1408,7 @@ control_event_bandwidth_used(uint32_t n_read, uint32_t n_written) set_uint32(buf, htonl(n_read)); set_uint32(buf+4, htonl(n_written)); - send_control_event(EVENT_BANDWIDTH_USED, 8, buf); + send_control0_event(EVENT_BANDWIDTH_USED, 8, buf); return 0; } @@ -1270,9 +1434,9 @@ control_event_logmsg(int severity, const char *msg) size_t len = strlen(msg); sending_logmsg = 1; if (event) - send_control_event(event, (uint32_t)(len+1), msg); + send_control0_event(event, (uint32_t)(len+1), msg); if (oldlog) - send_control_event(EVENT_LOG_OBSOLETE, (uint32_t)(len+1), msg); + send_control0_event(EVENT_LOG_OBSOLETE, (uint32_t)(len+1), msg); sending_logmsg = 0; } } @@ -1298,7 +1462,7 @@ control_event_descriptors_changed(smartlist_t *routers) smartlist_add(identities, tor_strdup(buf)); }); msg = smartlist_join_strings(identities, ",", 1, &len); - send_control_event(EVENT_NEW_DESC, len+1, msg); + send_control0_event(EVENT_NEW_DESC, len+1, msg); SMARTLIST_FOREACH(identities, char *, cp, tor_free(cp)); smartlist_free(identities); diff --git a/src/or/or.h b/src/or/or.h index 650484b21b..29ba3bbe83 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -286,9 +286,11 @@ typedef enum { #define _DIR_CONN_STATE_MAX 5 #define _CONTROL_CONN_STATE_MIN 1 -#define CONTROL_CONN_STATE_OPEN 1 -#define CONTROL_CONN_STATE_NEEDAUTH 2 -#define _CONTROL_CONN_STATE_MAX 2 +#define CONTROL_CONN_STATE_OPEN_V0 1 +#define CONTROL_CONN_STATE_OPEN_V1 2 +#define CONTROL_CONN_STATE_NEEDAUTH_V0 3 +#define CONTROL_CONN_STATE_NEEDAUTH_V1 4 +#define _CONTROL_CONN_STATE_MAX 4 #define _DIR_PURPOSE_MIN 1 /** Purpose for connection to directory server: download a directory. */ @@ -655,10 +657,12 @@ struct connection_t { /* Used only by control connections */ uint32_t event_mask; - uint16_t incoming_cmd_type; uint32_t incoming_cmd_len; uint32_t incoming_cmd_cur_len; char *incoming_cmd; + + /* Used only by control v0 connections */ + uint16_t incoming_cmd_type; }; typedef struct connection_t connection_t; @@ -1181,8 +1185,9 @@ int fetch_from_buf_http(buf_t *buf, char **headers_out, size_t max_headerlen, char **body_out, size_t *body_used, size_t max_bodylen); int fetch_from_buf_socks(buf_t *buf, socks_request_t *req); -int fetch_from_buf_control(buf_t *buf, uint32_t *len_out, uint16_t *type_out, - char **body_out); +int fetch_from_buf_control0(buf_t *buf, uint32_t *len_out, uint16_t *type_out, + char **body_out, int check_for_v1); +int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len); void assert_buf_ok(buf_t *buf); |