Implement even more control functionality

svn:r4454
This commit is contained in:
Nick Mathewson 2005-06-18 02:39:25 +00:00
parent 8fdab20704
commit c2f6fe9b85

View File

@ -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,
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);
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;
}
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;
}
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