diff options
author | Nick Mathewson <nickm@torproject.org> | 2005-06-18 02:39:25 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2005-06-18 02:39:25 +0000 |
commit | c2f6fe9b852551854a5d3ac73edd14210f20f017 (patch) | |
tree | b697c4d6d4f7cf0996bb1164aac8facbcb684050 | |
parent | 8fdab2070447705a72a9dbd901a937a41fb7d855 (diff) | |
download | tor-c2f6fe9b852551854a5d3ac73edd14210f20f017.tar.gz tor-c2f6fe9b852551854a5d3ac73edd14210f20f017.zip |
Implement even more control functionality
svn:r4454
-rw-r--r-- | src/or/control.c | 425 |
1 files changed, 360 insertions, 65 deletions
diff --git a/src/or/control.c b/src/or/control.c index 91abc1a072..af202e8713 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -257,6 +257,116 @@ connection_write_str_to_buf(const char *s, connection_t *conn) connection_write_to_buf(s, len, conn); } +static size_t +write_escaped_data(const char *data, size_t len, int translate_newlines, + char **out) +{ + size_t sz_out = len+8; + char *outp; + const char *end; + int i; + int start_of_line; + for (i=0; i<len; ++i) { + if (data[i]== '\n') + ++sz_out; + } + *out = outp = tor_malloc(sz_out+1); + end = data+len; + start_of_line = 1; + while (data < end) { + if (*data == '\n') { + if (translate_newlines) + *outp++ = '\r'; + start_of_line = 1; + } else if (*data == '.') { + if (start_of_line) { + start_of_line = 0; + *outp++ = '.'; + } + } else { + start_of_line = 0; + } + *outp++ = *data++; + } + *outp++ = '\r'; + *outp++ = '\n'; + *outp++ = '.'; + *outp++ = '\r'; + *outp++ = '\n'; + return outp - *out; +} + +#if 0 +static void +connection_write_escaped_data_to_buf(const char *data, size_t len, + int translate_newlines, + connection_t *conn) +{ + const char *next; + + while (len) { + if (*data == '.') + connection_write_to_buf(".", 1, conn); + + if (translate_newlines) + next = tor_memmem(data, len, "\r\n", 2); + else + next = tor_memmem(data, len, "\r\n.", 3); + + if (next) { + if (translate_newlines) { + connection_write_to_buf(data, next-data, conn); + connection_write_to_buf("\n", 1, conn); + len -= (next-data+2); + } else { + connection_write_to_buf(data, next-data+2, conn); + len -= (next-data+2); + } + data = next + 2; + } else { + connection_write_to_buf(data, len, conn); + break; + } + } + connection_write_to_buf(".\r\n", 3, conn); +} +#endif + +static size_t +read_escaped_data(const char *data, size_t len, int translate_newlines, + char **out) +{ + char *outp; + const char *next; + + *out = outp = tor_malloc(len); + + while (len) { + if (*data == '.') + ++data; + if (translate_newlines) + next = tor_memmem(data, len, "\r\n", 2); + else + next = tor_memmem(data, len, "\r\n.", 3); + if (next) { + memcpy(outp, data, next-data); + outp += (next-data); + data = next+2; + } else { + memcpy(outp, data, len); + outp += len; + return outp - *out; + } + if (translate_newlines) { + *outp++ = '\n'; + } else { + *outp++ = '\r'; + *outp++ = '\n'; + } + } + + return outp - *out; +} static void @@ -441,6 +551,32 @@ send_control1_event(uint16_t event, const char *format, ...) } } +static circuit_t * +get_circ(const char *id) +{ + unsigned long n_id; + int ok; + n_id = tor_parse_ulong(id, 10, 0, ULONG_MAX, &ok, NULL); + if (!ok) + return NULL; + return circuit_get_by_global_id(n_id); +} + +static connection_t * +get_stream(const char *id) +{ + unsigned long n_id; + int ok; + connection_t *conn; + n_id = tor_parse_ulong(id, 10, 0, ULONG_MAX, &ok, NULL); + if (!ok) + return NULL; + conn = connection_get_by_global_id(n_id); + if (conn->type != CONN_TYPE_AP) + return NULL; + return conn; +} + /** Called when we receive a SETCONF message: parse the body and try * to update our configuration. Reply with a DONE or ERROR message. */ static int @@ -892,39 +1028,86 @@ handle_getinfo_helper(const char *question, char **answer) static int handle_control_getinfo(connection_t *conn, uint32_t len, const char *body) { - /* XXXX V1 */ smartlist_t *questions = NULL; smartlist_t *answers = NULL; + smartlist_t *unrecognized = NULL; char *msg = NULL, *ans; size_t msg_len; + int v0 = STATE_IS_V0(conn->state); questions = smartlist_create(); - smartlist_split_string(questions, body, "\n", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + if (v0) + smartlist_split_string(questions, body, "\n", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + else + smartlist_split_string(questions, body, " ", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); answers = smartlist_create(); + unrecognized = smartlist_create(); SMARTLIST_FOREACH(questions, const char *, q, { if (handle_getinfo_helper(q, &ans) < 0) { - send_control0_error(conn, ERR_INTERNAL, body); - goto done; - } if (!ans) { - send_control0_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, body); + if (v0) + send_control0_error(conn, ERR_INTERNAL, body); + else + connection_write_str_to_buf("551 Internal error\r\n", conn); goto done; } - smartlist_add(answers, tor_strdup(q)); - smartlist_add(answers, ans); + if (!ans) { + if (v0) { + send_control0_error(conn, ERR_UNRECOGNIZED_CONFIG_KEY, body); + goto done; + } else + smartlist_add(unrecognized, (char*)q); + } else { + smartlist_add(answers, tor_strdup(q)); + smartlist_add(answers, ans); + } }); + if (smartlist_len(unrecognized)) { + int i; + tor_assert(!v0); + for (i=0; i < len-1; ++i) + connection_printf_to_buf(conn, + "552-Unrecognized configuration key \"%s\"\r\n", + (char*)smartlist_get(unrecognized, i)); + connection_printf_to_buf(conn, + "552 Unrecognized configuration key \"%s\"\r\n", + (char*)smartlist_get(unrecognized, len-1)); + goto done; + } - msg = smartlist_join_strings2(answers, "\0", 1, 1, &msg_len); - tor_assert(msg_len > 0); /* it will at least be terminated */ - send_control0_message(conn, CONTROL0_CMD_INFOVALUE, - msg_len, msg); + if (v0) { + msg = smartlist_join_strings2(answers, "\0", 1, 1, &msg_len); + tor_assert(msg_len > 0); /* it will at least be terminated */ + send_control0_message(conn, CONTROL0_CMD_INFOVALUE, + msg_len, msg); + } else if (smartlist_len(answers)) { + int i; + for (i = 0; i < smartlist_len(answers); i += 2) { + char *k = smartlist_get(answers, i); + char *v = smartlist_get(answers, i+1); + /*XXXX Not an adequate test! XXXX011 */ + if (!strchr(v, '\n') && !strchr(v, '\r')) { + connection_printf_to_buf(conn, "250-%s=%s\r\n", k, v); + } else { + char *esc = NULL; + size_t len; + len = write_escaped_data(v, strlen(v), 1, &esc); + connection_printf_to_buf(conn, "250+%s=", k); + connection_write_to_buf(esc, len, conn); + tor_free(esc); + } + } + connection_write_str_to_buf("250 OK\r\n", conn); + } done: if (answers) SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp)); if (questions) SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp)); smartlist_free(answers); smartlist_free(questions); + smartlist_free(unrecognized); tor_free(msg); return 0; @@ -1069,14 +1252,31 @@ static int handle_control_postdescriptor(connection_t *conn, uint32_t len, const char *body) { - /* XXXX V1 */ + char *desc; + int v0 = STATE_IS_V0(conn->state); + if (v0) + desc = (char*)body; + else { + const char *cp = memchr(body, '\n', len); + tor_assert(cp); + read_escaped_data(cp, len-(cp-body), 1, &desc); + } + const char *msg=NULL; - switch (router_load_single_router(body, &msg)) { + switch (router_load_single_router(desc, &msg)) { case -1: - send_control0_error(conn,ERR_SYNTAX,msg?msg: "Could not parse descriptor"); + if (!msg) msg = "Could not parse descriptor"; + if (v0) + send_control0_error(conn,ERR_SYNTAX,msg); + else + connection_printf_to_buf(conn, "554 %s\r\n", msg); break; case 0: - send_control_done2(conn,msg?msg: "Descriptor not added",0); + if (!msg) msg = "Descriptor not added"; + if (v0) + send_control_done2(conn,msg,0); + else + connection_printf_to_buf(conn, "251 %s\r\n",msg); break; case 1: send_control_done(conn); @@ -1091,25 +1291,48 @@ static int handle_control_redirectstream(connection_t *conn, uint32_t len, const char *body) { - /* XXXX V1 */ - connection_t *ap_conn; + connection_t *ap_conn = NULL; uint32_t conn_id; - if (len < 6) { - send_control0_error(conn, ERR_SYNTAX, "redirectstream message too short"); - return 0; - } - conn_id = ntohl(get_uint32(body)); + char *new_addr = NULL; + if (STATE_IS_V0(conn->state)) { + if (len < 6) { + send_control0_error(conn, ERR_SYNTAX, "redirectstream message too short"); + return 0; + } + conn_id = ntohl(get_uint32(body)); - if (!(ap_conn = connection_get_by_global_id(conn_id)) - || ap_conn->state != CONN_TYPE_AP - || !ap_conn->socks_request) { - send_control0_error(conn, ERR_NO_STREAM, - "No AP connection found with given ID"); - return 0; + if (!(ap_conn = connection_get_by_global_id(conn_id)) + || ap_conn->state != CONN_TYPE_AP + || !ap_conn->socks_request) { + send_control0_error(conn, ERR_NO_STREAM, + "No AP connection found with given ID"); + return 0; + } + new_addr = tor_strdup(body+4); + } else { + smartlist_t *args; + 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 REDIRECTSTREAM\r\n"); + else if (!(ap_conn = get_stream(smartlist_get(args, 0))) + || !ap_conn->socks_request) { + connection_printf_to_buf(conn, "552 Unknown stream \"%s\"\r\n", + (char*)smartlist_get(args, 0)); + } else { + new_addr = tor_strdup(smartlist_get(args, 1)); + } + + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); + smartlist_free(args); + if (!new_addr) + return 0; } - strlcpy(ap_conn->socks_request->address, body+4, - sizeof(ap_conn->socks_request->address)); + strlcpy(ap_conn->socks_request->address, new_addr, + sizeof(ap_conn->socks_request->address)); + tor_free(new_addr); send_control_done(conn); return 0; } @@ -1119,26 +1342,52 @@ static int handle_control_closestream(connection_t *conn, uint32_t len, const char *body) { - /* XXXX V1 */ - uint32_t conn_id; - connection_t *ap_conn; - uint8_t reason; + connection_t *ap_conn=NULL; + uint8_t reason=0; - if (len < 6) { - send_control0_error(conn, ERR_SYNTAX, "closestream message too short"); - return 0; - } + if (STATE_IS_V0(conn->state)) { + uint32_t conn_id; + if (len < 6) { + send_control0_error(conn, ERR_SYNTAX, "closestream message too short"); + return 0; + } - conn_id = ntohl(get_uint32(body)); - reason = *(uint8_t*)(body+4); + conn_id = ntohl(get_uint32(body)); + reason = *(uint8_t*)(body+4); - if (!(ap_conn = connection_get_by_global_id(conn_id)) - || ap_conn->state != CONN_TYPE_AP - || !ap_conn->socks_request) { - send_control0_error(conn, ERR_NO_STREAM, - "No AP connection found with given ID"); - return 0; + if (!(ap_conn = connection_get_by_global_id(conn_id)) + || ap_conn->state != CONN_TYPE_AP + || !ap_conn->socks_request) { + send_control0_error(conn, ERR_NO_STREAM, + "No AP connection found with given ID"); + return 0; + } + } else { + smartlist_t *args; + int ok; + 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 CLOSECIRCUIT\r\n"); + else if (!(ap_conn = get_stream(smartlist_get(args, 0)))) + connection_printf_to_buf(conn, "552 Unknown stream \"%s\"\r\n", + (char*)smartlist_get(args, 0)); + else { + reason = (uint8_t) tor_parse_ulong(smartlist_get(args,1), 10, 0, 255, + &ok, NULL); + if (!ok) { + connection_printf_to_buf(conn, "552 Unrecognized reason \"%s\"\r\n", + (char*)smartlist_get(args, 1)); + ap_conn = NULL; + } + } + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); + smartlist_free(args); + if (!ap_conn) + return 0; } + connection_mark_unattached_ap(ap_conn, reason); send_control_done(conn); return 0; @@ -1149,22 +1398,47 @@ static int handle_control_closecircuit(connection_t *conn, uint32_t len, const char *body) { - /* XXXX V1 */ - uint32_t circ_id; - circuit_t *circ; - int safe; + circuit_t *circ = NULL; + int safe = 0; - if (len < 5) { - 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 (STATE_IS_V0(conn->state)) { + uint32_t circ_id; + if (len < 5) { + 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_control0_error(conn, ERR_NO_CIRC, - "No circuit found with given ID"); - return 0; + if (!(circ = circuit_get_by_global_id(circ_id))) { + send_control0_error(conn, ERR_NO_CIRC, + "No circuit found with given ID"); + return 0; + } + } else { + smartlist_t *args; + args = smartlist_create(); + smartlist_split_string(args, body, " ", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(args)<1) + connection_printf_to_buf(conn, "512 Missing argument to CLOSECIRCUIT\r\n"); + else if (!(circ=get_circ(smartlist_get(args, 0)))) + connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n", + (char*)smartlist_get(args, 0)); + else { + int i; + for (i=1; i < smartlist_len(args); ++i) { + if (!strcasecmp(smartlist_get(args, i), "IfUnused")) + safe = 1; + else + log_fn(LOG_INFO, "Skipping unknown option %s", + (char*)smartlist_get(args,i)); + } + } + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); + smartlist_free(args); + if (!circ) + return 0; } if (!safe || !circ->p_streams) { @@ -1693,18 +1967,17 @@ control_event_bandwidth_used(uint32_t n_read, uint32_t n_written) void control_event_logmsg(int severity, const char *msg) { - /* XXXX V1 */ static int sending_logmsg=0; int oldlog, event; if (sending_logmsg) return; - oldlog = EVENT_IS_INTERESTING(EVENT_LOG_OBSOLETE) && + oldlog = EVENT_IS_INTERESTING0(EVENT_LOG_OBSOLETE) && (severity == LOG_NOTICE || severity == LOG_WARN || severity == LOG_ERR); event = log_severity_to_event(severity); - if (event<0 || !EVENT_IS_INTERESTING(event)) + if (event<0 || !EVENT_IS_INTERESTING0(event)) event = 0; if (oldlog || event) { @@ -1716,6 +1989,28 @@ control_event_logmsg(int severity, const char *msg) send_control0_event(EVENT_LOG_OBSOLETE, (uint32_t)(len+1), msg); sending_logmsg = 0; } + + event = log_severity_to_event(severity); + if (event >= 0 && EVENT_IS_INTERESTING1(event)) { + char *b = NULL; + const char *s; + if (strchr(msg, '\n')) { + char *cp; + b = tor_strdup(msg); + for (cp = b; *cp; ++cp) + if (*cp == '\r' || *cp == '\n') + *cp = ' '; + } + switch(severity) { + case LOG_INFO: s = "INFO"; break; + case LOG_NOTICE: s = "NOTICE"; break; + case LOG_WARN: s = "WARN"; break; + case LOG_ERR: s = "ERR"; break; + default: s = "UnknownLogSeverity"; break; + } + send_control1_event(event, "650 %s %s\r\n", s, b?b:msg); + tor_free(b); + } } /** Called whenever we receive new router descriptors: tell any |