aboutsummaryrefslogtreecommitdiff
path: root/src/or/control.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/control.c')
-rw-r--r--src/or/control.c989
1 files changed, 587 insertions, 402 deletions
diff --git a/src/or/control.c b/src/or/control.c
index 3ffe10bf2d..1e411ec9c1 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -11,6 +11,28 @@
#define CONTROL_PRIVATE
#include "or.h"
+#include "buffers.h"
+#include "circuitbuild.h"
+#include "circuitlist.h"
+#include "circuituse.h"
+#include "config.h"
+#include "connection.h"
+#include "connection_edge.h"
+#include "control.h"
+#include "directory.h"
+#include "dirserv.h"
+#include "dnsserv.h"
+#include "geoip.h"
+#include "hibernate.h"
+#include "main.h"
+#include "networkstatus.h"
+#include "policies.h"
+#include "reasons.h"
+#include "router.h"
+#include "routerlist.h"
+#include "routerparse.h"
+
+#include "procmon.h"
/** Yield true iff <b>s</b> is the state of a control_connection_t that has
* finished authentication and is accepting commands. */
@@ -43,7 +65,8 @@
#define EVENT_STREAM_BANDWIDTH_USED 0x0014
#define EVENT_CLIENTS_SEEN 0x0015
#define EVENT_NEWCONSENSUS 0x0016
-#define _EVENT_MAX 0x0016
+#define EVENT_BUILDTIMEOUT_SET 0x0017
+#define _EVENT_MAX 0x0017
/* If _EVENT_MAX ever hits 0x0020, we need to make the mask wider. */
/** Bitfield: The bit 1&lt;&lt;e is set if <b>any</b> open control
@@ -54,13 +77,9 @@
**/
typedef uint32_t event_mask_t;
-/** An event mask of all the events that controller with the LONG_NAMES option
- * set is interested in receiving. */
-static event_mask_t global_event_mask1long = 0;
-
-/** An event mask of all the events that controller with the SHORT_NAMES option
- * set is interested in receiving. */
-static event_mask_t global_event_mask1short = 0;
+/** An event mask of all the events that any controller is interested in
+ * receiving. */
+static event_mask_t global_event_mask = 0;
/** True iff we have disabled log messages from being sent to the controller */
static int disable_log_messages = 0;
@@ -68,13 +87,7 @@ static int disable_log_messages = 0;
/** Macro: true if any control connection is interested in events of type
* <b>e</b>. */
#define EVENT_IS_INTERESTING(e) \
- ((global_event_mask1long|global_event_mask1short) & (1<<(e)))
-/** Macro: true if any control connection with the LONG_NAMES option is
- * interested in events of type <b>e</b>. */
-#define EVENT_IS_INTERESTING1L(e) (global_event_mask1long & (1<<(e)))
-/** Macro: true if any control connection with the SHORT_NAMES option is
- * interested in events of type <b>e</b>. */
-#define EVENT_IS_INTERESTING1S(e) (global_event_mask1short & (1<<(e)))
+ (global_event_mask & (1<<(e)))
/** If we're using cookie-type authentication, how long should our cookies be?
*/
@@ -85,7 +98,7 @@ static int disable_log_messages = 0;
static int authentication_cookie_is_set = 0;
/** If authentication_cookie_is_set, a secret cookie that we've stored to disk
* and which we're using to authenticate controllers. (If the controller can
- * read it off disk, it has permission to connect. */
+ * read it off disk, it has permission to connect.) */
static char authentication_cookie[AUTHENTICATION_COOKIE_LEN];
/** A sufficiently large size to record the last bootstrap phase string. */
@@ -95,25 +108,13 @@ static char authentication_cookie[AUTHENTICATION_COOKIE_LEN];
* of this so we can respond to getinfo status/bootstrap-phase queries. */
static char last_sent_bootstrap_message[BOOTSTRAP_MSG_LEN];
-/** Flag for event_format_t. Indicates that we should use the old
- * name format of nickname|hexdigest
- */
-#define SHORT_NAMES 1
-/** Flag for event_format_t. Indicates that we should use the new
- * name format of $hexdigest[=~]nickname
+/** Flag for event_format_t. Indicates that we should use the one standard
+ format.
*/
-#define LONG_NAMES 2
-#define ALL_NAMES (SHORT_NAMES|LONG_NAMES)
-/** Flag for event_format_t. Indicates that we should use the new event
- * format where extra event fields are allowed using a NAME=VAL format. */
-#define EXTENDED_FORMAT 4
-/** Flag for event_format_t. Indicates that we are using the old event format
- * where extra fields aren't allowed. */
-#define NONEXTENDED_FORMAT 8
-#define ALL_FORMATS (EXTENDED_FORMAT|NONEXTENDED_FORMAT)
+#define ALL_FORMATS 1
/** Bit field of flags to select how to format a controller event. Recognized
- * flags are SHORT_NAMES, LONG_NAMES, EXTENDED_FORMAT, NONEXTENDED_FORMAT. */
+ * flag is ALL_FORMATS. */
typedef int event_format_t;
static void connection_printf_to_buf(control_connection_t *conn,
@@ -123,9 +124,6 @@ static void send_control_done(control_connection_t *conn);
static void send_control_event(uint16_t event, event_format_t which,
const char *format, ...)
CHECK_PRINTF(3,4);
-static void send_control_event_extended(uint16_t event, event_format_t which,
- const char *format, ...)
- CHECK_PRINTF(3,4);
static int handle_control_setconf(control_connection_t *conn, uint32_t len,
char *body);
static int handle_control_resetconf(control_connection_t *conn, uint32_t len,
@@ -139,8 +137,6 @@ static int handle_control_setevents(control_connection_t *conn, uint32_t len,
static int handle_control_authenticate(control_connection_t *conn,
uint32_t len,
const char *body);
-static int handle_control_saveconf(control_connection_t *conn, uint32_t len,
- const char *body);
static int handle_control_signal(control_connection_t *conn, uint32_t len,
const char *body);
static int handle_control_mapaddress(control_connection_t *conn, uint32_t len,
@@ -174,7 +170,7 @@ static int handle_control_usefeature(control_connection_t *conn,
const char *body);
static int write_stream_target_to_buf(edge_connection_t *conn, char *buf,
size_t len);
-static void orconn_target_get_name(int long_names, char *buf, size_t len,
+static void orconn_target_get_name(char *buf, size_t len,
or_connection_t *conn);
static char *get_cookie_file(void);
@@ -214,25 +210,19 @@ control_update_global_event_mask(void)
{
smartlist_t *conns = get_connection_array();
event_mask_t old_mask, new_mask;
- old_mask = global_event_mask1short;
- old_mask |= global_event_mask1long;
+ old_mask = global_event_mask;
- global_event_mask1short = 0;
- global_event_mask1long = 0;
+ global_event_mask = 0;
SMARTLIST_FOREACH(conns, connection_t *, _conn,
{
if (_conn->type == CONN_TYPE_CONTROL &&
STATE_IS_OPEN(_conn->state)) {
control_connection_t *conn = TO_CONTROL_CONN(_conn);
- if (conn->use_long_names)
- global_event_mask1long |= conn->event_mask;
- else
- global_event_mask1short |= conn->event_mask;
+ global_event_mask |= conn->event_mask;
}
});
- new_mask = global_event_mask1short;
- new_mask |= global_event_mask1long;
+ new_mask = global_event_mask;
/* Handle the aftermath. Set up the log callback to tell us only what
* we want to hear...*/
@@ -312,7 +302,7 @@ connection_write_str_to_buf(const char *s, control_connection_t *conn)
/** Given a <b>len</b>-character string in <b>data</b>, made of lines
* terminated by CRLF, allocate a new string in *<b>out</b>, and copy the
* contents of <b>data</b> into *<b>out</b>, adding a period before any period
- * that that appears at the start of a line, and adding a period-CRLF line at
+ * that appears at the start of a line, and adding a period-CRLF line at
* the end. Replace all LF characters sequences with CRLF. Return the number
* of bytes in *<b>out</b>.
*/
@@ -491,33 +481,73 @@ decode_escaped_string(const char *start, size_t in_len_max,
}
/** Acts like sprintf, but writes its formatted string to the end of
- * <b>conn</b>-\>outbuf. The message may be truncated if it is too long,
- * but it will always end with a CRLF sequence.
- *
- * Currently the length of the message is limited to 1024 (including the
- * ending CR LF NUL ("\\r\\n\\0"). */
+ * <b>conn</b>-\>outbuf. */
static void
connection_printf_to_buf(control_connection_t *conn, const char *format, ...)
{
-#define CONNECTION_PRINTF_TO_BUF_BUFFERSIZE 1024
va_list ap;
- char buf[CONNECTION_PRINTF_TO_BUF_BUFFERSIZE];
- int r;
- size_t len;
+ char *buf = NULL;
+ int len;
+
va_start(ap,format);
- r = tor_vsnprintf(buf, sizeof(buf), format, ap);
+ len = tor_vasprintf(&buf, format, ap);
va_end(ap);
- if (r<0) {
+
+ if (len < 0) {
log_warn(LD_BUG, "Unable to format string for controller.");
return;
}
- len = strlen(buf);
- if (fast_memcmp("\r\n\0", buf+len-2, 3)) {
- buf[CONNECTION_PRINTF_TO_BUF_BUFFERSIZE-1] = '\0';
- buf[CONNECTION_PRINTF_TO_BUF_BUFFERSIZE-2] = '\n';
- buf[CONNECTION_PRINTF_TO_BUF_BUFFERSIZE-3] = '\r';
+
+ connection_write_to_buf(buf, (size_t)len, TO_CONN(conn));
+
+ tor_free(buf);
+}
+
+/** Write all of the open control ports to ControlPortWriteToFile */
+void
+control_ports_write_to_file(void)
+{
+ smartlist_t *lines;
+ char *joined = NULL;
+ or_options_t *options = get_options();
+
+ if (!options->ControlPortWriteToFile)
+ return;
+
+ lines = smartlist_create();
+
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), const connection_t *, conn) {
+ char *port_str = NULL;
+ if (conn->type != CONN_TYPE_CONTROL_LISTENER || conn->marked_for_close)
+ continue;
+#ifdef AF_UNIX
+ if (conn->socket_family == AF_UNIX) {
+ tor_asprintf(&port_str, "UNIX_PORT=%s\n", conn->address);
+ smartlist_add(lines, port_str);
+ continue;
+ }
+#endif
+ tor_asprintf(&port_str, "PORT=%s:%d\n", conn->address, conn->port);
+ smartlist_add(lines, port_str);
+ } SMARTLIST_FOREACH_END(conn);
+
+ joined = smartlist_join_strings(lines, "", 0, NULL);
+
+ if (write_str_to_file(options->ControlPortWriteToFile, joined, 0) < 0) {
+ log_warn(LD_CONTROL, "Writing %s failed: %s",
+ options->ControlPortWriteToFile, strerror(errno));
+ }
+#ifndef MS_WINDOWS
+ if (options->ControlPortFileGroupReadable) {
+ if (chmod(options->ControlPortWriteToFile, 0640)) {
+ log_warn(LD_FS,"Unable to make %s group-readable.",
+ options->ControlPortWriteToFile);
+ }
}
- connection_write_to_buf(buf, len, TO_CONN(conn));
+#endif
+ tor_free(joined);
+ SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
+ smartlist_free(lines);
}
/** Send a "DONE" message down the control connection <b>conn</b>. */
@@ -542,28 +572,15 @@ send_control_event_string(uint16_t event, event_format_t which,
const char *msg)
{
smartlist_t *conns = get_connection_array();
+ (void)which;
tor_assert(event >= _EVENT_MIN && event <= _EVENT_MAX);
- SMARTLIST_FOREACH(conns, connection_t *, conn,
- {
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
if (conn->type == CONN_TYPE_CONTROL &&
!conn->marked_for_close &&
conn->state == CONTROL_CONN_STATE_OPEN) {
control_connection_t *control_conn = TO_CONTROL_CONN(conn);
- if (control_conn->use_long_names) {
- if (!(which & LONG_NAMES))
- continue;
- } else {
- if (!(which & SHORT_NAMES))
- continue;
- }
- if (control_conn->use_extended_events) {
- if (!(which & EXTENDED_FORMAT))
- continue;
- } else {
- if (!(which & NONEXTENDED_FORMAT))
- continue;
- }
+
if (control_conn->event_mask & (1<<event)) {
int is_err = 0;
connection_write_to_buf(msg, strlen(msg), TO_CONN(control_conn));
@@ -579,89 +596,41 @@ send_control_event_string(uint16_t event, event_format_t which,
connection_handle_write(TO_CONN(control_conn), 1);
}
}
- });
+ } SMARTLIST_FOREACH_END(conn);
}
-/** Helper for send_control1_event and send_control1_event_extended:
+/** Helper for send_control_event and control_event_status:
* Send an event to all v1 controllers that are listening for code
* <b>event</b>. The event's body is created by the printf-style format in
- * <b>format</b>, and other arguments as provided.
- *
- * If <b>extended</b> is true, and the format contains a single '@' character,
- * it will be replaced with a space and all text after that character will be
- * sent only to controllers that have enabled extended events.
- *
- * Currently the length of the message is limited to 1024 (including the
- * ending \\r\\n\\0). */
+ * <b>format</b>, and other arguments as provided. */
static void
-send_control_event_impl(uint16_t event, event_format_t which, int extended,
- const char *format, va_list ap)
+send_control_event_impl(uint16_t event, event_format_t which,
+ const char *format, va_list ap)
{
- /* This is just a little longer than the longest allowed log message */
-#define SEND_CONTROL1_EVENT_BUFFERSIZE 10064
- int r;
- char buf[SEND_CONTROL1_EVENT_BUFFERSIZE];
- size_t len;
- char *cp;
+ char *buf = NULL;
+ int len;
- r = tor_vsnprintf(buf, sizeof(buf), format, ap);
- if (r<0) {
+ len = tor_vasprintf(&buf, format, ap);
+ if (len < 0) {
log_warn(LD_BUG, "Unable to format event for controller.");
return;
}
- len = strlen(buf);
- if (fast_memcmp("\r\n\0", buf+len-2, 3)) {
- /* if it is not properly terminated, do it now */
- buf[SEND_CONTROL1_EVENT_BUFFERSIZE-1] = '\0';
- buf[SEND_CONTROL1_EVENT_BUFFERSIZE-2] = '\n';
- buf[SEND_CONTROL1_EVENT_BUFFERSIZE-3] = '\r';
- }
+ send_control_event_string(event, which|ALL_FORMATS, buf);
- if (extended && (cp = strchr(buf, '@'))) {
- which &= ~ALL_FORMATS;
- *cp = ' ';
- send_control_event_string(event, which|EXTENDED_FORMAT, buf);
- memcpy(cp, "\r\n\0", 3);
- send_control_event_string(event, which|NONEXTENDED_FORMAT, buf);
- } else {
- send_control_event_string(event, which|ALL_FORMATS, buf);
- }
+ tor_free(buf);
}
/** Send an event to all v1 controllers that are listening for code
* <b>event</b>. The event's body is created by the printf-style format in
- * <b>format</b>, and other arguments as provided.
- *
- * Currently the length of the message is limited to 1024 (including the
- * ending \\n\\r\\0. */
+ * <b>format</b>, and other arguments as provided. */
static void
send_control_event(uint16_t event, event_format_t which,
const char *format, ...)
{
va_list ap;
va_start(ap, format);
- send_control_event_impl(event, which, 0, format, ap);
- va_end(ap);
-}
-
-/** Send an event to all v1 controllers that are listening for code
- * <b>event</b>. The event's body is created by the printf-style format in
- * <b>format</b>, and other arguments as provided.
- *
- * If the format contains a single '@' character, it will be replaced with a
- * space and all text after that character will be sent only to controllers
- * that have enabled extended events.
- *
- * Currently the length of the message is limited to 1024 (including the
- * ending \\n\\r\\0. */
-static void
-send_control_event_extended(uint16_t event, event_format_t which,
- const char *format, ...)
-{
- va_list ap;
- va_start(ap, format);
- send_control_event_impl(event, which, 1, format, ap);
+ send_control_event_impl(event, which, format, ap);
va_end(ap);
}
@@ -907,36 +876,37 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len,
retval = options_init_from_string(body, CMD_RUN_TOR, NULL, &errstring);
- if (retval != SETOPT_OK) {
+ if (retval != SETOPT_OK)
log_warn(LD_CONTROL,
"Controller gave us config file that didn't validate: %s",
errstring);
- switch (retval) {
- case SETOPT_ERR_PARSE:
- msg = "552 Invalid config file";
- break;
- case SETOPT_ERR_TRANSITION:
- msg = "553 Transition not allowed";
- break;
- case SETOPT_ERR_SETTING:
- msg = "553 Unable to set option";
- break;
- case SETOPT_ERR_MISC:
- default:
- msg = "550 Unable to load config";
- break;
- case SETOPT_OK:
- tor_fragile_assert();
- break;
- }
+
+ switch (retval) {
+ case SETOPT_ERR_PARSE:
+ msg = "552 Invalid config file";
+ break;
+ case SETOPT_ERR_TRANSITION:
+ msg = "553 Transition not allowed";
+ break;
+ case SETOPT_ERR_SETTING:
+ msg = "553 Unable to set option";
+ break;
+ case SETOPT_ERR_MISC:
+ default:
+ msg = "550 Unable to load config";
+ break;
+ case SETOPT_OK:
+ break;
+ }
+ if (msg) {
if (errstring)
connection_printf_to_buf(conn, "%s: %s\r\n", msg, errstring);
else
connection_printf_to_buf(conn, "%s\r\n", msg);
- tor_free(errstring);
- return 0;
+ } else {
+ send_control_done(conn);
}
- send_control_done(conn);
+ tor_free(errstring);
return 0;
}
@@ -948,7 +918,6 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
{
uint16_t event_code;
uint32_t event_mask = 0;
- unsigned int extended = 0;
smartlist_t *events = smartlist_create();
(void) len;
@@ -958,7 +927,6 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
{
if (!strcasecmp(ev, "EXTENDED")) {
- extended = 1;
continue;
} else if (!strcasecmp(ev, "CIRC"))
event_code = EVENT_CIRCUIT_STATUS;
@@ -1002,6 +970,8 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
event_code = EVENT_CLIENTS_SEEN;
else if (!strcasecmp(ev, "NEWCONSENSUS"))
event_code = EVENT_NEWCONSENSUS;
+ else if (!strcasecmp(ev, "BUILDTIMEOUT_SET"))
+ event_code = EVENT_BUILDTIMEOUT_SET;
else {
connection_printf_to_buf(conn, "552 Unrecognized event \"%s\"\r\n",
ev);
@@ -1016,8 +986,6 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
smartlist_free(events);
conn->event_mask = event_mask;
- if (extended)
- conn->use_extended_events = 1;
control_update_global_event_mask();
send_control_done(conn);
@@ -1281,7 +1249,29 @@ handle_control_signal(control_connection_t *conn, uint32_t len,
/* Flush the "done" first if the signal might make us shut down. */
if (sig == SIGTERM || sig == SIGINT)
connection_handle_write(TO_CONN(conn), 1);
- control_signal_act(sig);
+
+ process_signal(sig);
+
+ return 0;
+}
+
+/** Called when we get a TAKEOWNERSHIP command. Mark this connection
+ * as an owning connection, so that we will exit if the connection
+ * closes. */
+static int
+handle_control_takeownership(control_connection_t *conn, uint32_t len,
+ const char *body)
+{
+ (void)len;
+ (void)body;
+
+ conn->is_owning_control_connection = 1;
+
+ log_info(LD_CONTROL, "Control connection %d has taken ownership of this "
+ "Tor instance.",
+ (int)(conn->_base.s));
+
+ send_control_done(conn);
return 0;
}
@@ -1328,7 +1318,7 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len,
smartlist_add(reply, ans);
log_warn(LD_CONTROL,
"Unable to allocate address for '%s' in MapAddress msg",
- safe_str(line));
+ safe_str_client(line));
} else {
tor_snprintf(ans, anslen, "250-%s=%s", address, to);
smartlist_add(reply, ans);
@@ -1345,7 +1335,8 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len,
"not of expected form 'foo=bar'.", line);
smartlist_add(reply, ans);
log_info(LD_CONTROL, "Skipping MapAddress '%s': wrong "
- "number of items.", safe_str(line));
+ "number of items.",
+ safe_str_client(line));
}
SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
smartlist_clear(elts);
@@ -1374,36 +1365,43 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len,
* trivial-to-implement questions. */
static int
getinfo_helper_misc(control_connection_t *conn, const char *question,
- char **answer)
+ char **answer, const char **errmsg)
{
(void) conn;
if (!strcmp(question, "version")) {
*answer = tor_strdup(get_version());
} else if (!strcmp(question, "config-file")) {
*answer = tor_strdup(get_torrc_fname());
+ } else if (!strcmp(question, "config-text")) {
+ *answer = options_dump(get_options(), 1);
} else if (!strcmp(question, "info/names")) {
*answer = list_getinfo_options();
} else if (!strcmp(question, "events/names")) {
*answer = tor_strdup("CIRC STREAM ORCONN BW DEBUG INFO NOTICE WARN ERR "
"NEWDESC ADDRMAP AUTHDIR_NEWDESCS DESCCHANGED "
"NS STATUS_GENERAL STATUS_CLIENT STATUS_SERVER "
- "GUARD STREAM_BW CLIENTS_SEEN NEWCONSENSUS");
+ "GUARD STREAM_BW CLIENTS_SEEN NEWCONSENSUS "
+ "BUILDTIMEOUT_SET");
} else if (!strcmp(question, "features/names")) {
*answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
} else if (!strcmp(question, "address")) {
uint32_t addr;
- if (router_pick_published_address(get_options(), &addr) < 0)
+ if (router_pick_published_address(get_options(), &addr) < 0) {
+ *errmsg = "Address unknown";
return -1;
+ }
*answer = tor_dup_ip(addr);
} else if (!strcmp(question, "dir-usage")) {
*answer = directory_dump_request_log();
} else if (!strcmp(question, "fingerprint")) {
- routerinfo_t *me = router_get_my_routerinfo();
- if (!me)
+ crypto_pk_env_t *server_key;
+ if (!server_mode(get_options())) {
+ *errmsg = "Not running in server mode";
return -1;
+ }
+ server_key = get_server_identity_key();
*answer = tor_malloc(HEX_DIGEST_LEN+1);
- base16_encode(*answer, HEX_DIGEST_LEN+1, me->cache_info.identity_digest,
- DIGEST_LEN);
+ crypto_pk_get_fingerprint(server_key, *answer, 0);
}
return 0;
}
@@ -1457,12 +1455,71 @@ munge_extrainfo_into_routerinfo(const char *ri_body, signed_descriptor_t *ri,
return tor_strndup(ri_body, ri->signed_descriptor_len);
}
+/** Implementation helper for GETINFO: answers requests for information about
+ * which ports are bound. */
+static int
+getinfo_helper_listeners(control_connection_t *control_conn,
+ const char *question,
+ char **answer, const char **errmsg)
+{
+ int type;
+ smartlist_t *res;
+
+ (void)control_conn;
+ (void)errmsg;
+
+ if (!strcmp(question, "net/listeners/or"))
+ type = CONN_TYPE_OR_LISTENER;
+ else if (!strcmp(question, "net/listeners/dir"))
+ type = CONN_TYPE_DIR_LISTENER;
+ else if (!strcmp(question, "net/listeners/socks"))
+ type = CONN_TYPE_AP_LISTENER;
+ else if (!strcmp(question, "net/listeners/trans"))
+ type = CONN_TYPE_AP_TRANS_LISTENER;
+ else if (!strcmp(question, "net/listeners/natd"))
+ type = CONN_TYPE_AP_NATD_LISTENER;
+ else if (!strcmp(question, "net/listeners/dns"))
+ type = CONN_TYPE_AP_DNS_LISTENER;
+ else if (!strcmp(question, "net/listeners/control"))
+ type = CONN_TYPE_CONTROL_LISTENER;
+ else
+ return 0; /* unknown key */
+
+ res = smartlist_create();
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
+ char *addr;
+ struct sockaddr_storage ss;
+ socklen_t ss_len = sizeof(ss);
+
+ if (conn->type != type || conn->marked_for_close || !SOCKET_OK(conn->s))
+ continue;
+
+ if (getsockname(conn->s, (struct sockaddr *)&ss, &ss_len) < 0) {
+ tor_asprintf(&addr, "%s:%d", conn->address, (int)conn->port);
+ } else {
+ char *tmp = tor_sockaddr_to_str((struct sockaddr *)&ss);
+ addr = esc_for_log(tmp);
+ tor_free(tmp);
+ }
+ if (addr)
+ smartlist_add(res, addr);
+ } SMARTLIST_FOREACH_END(conn);
+
+ *answer = smartlist_join_strings(res, " ", 0, NULL);
+
+ SMARTLIST_FOREACH(res, char *, cp, tor_free(cp));
+ smartlist_free(res);
+ return 0;
+}
+
/** Implementation helper for GETINFO: knows the answers for questions about
* directory information. */
static int
getinfo_helper_dir(control_connection_t *control_conn,
- const char *question, char **answer)
+ const char *question, char **answer,
+ const char **errmsg)
{
+ (void) control_conn;
if (!strcmpstart(question, "desc/id/")) {
routerinfo_t *ri = router_get_by_hexdigest(question+strlen("desc/id/"));
if (ri) {
@@ -1537,6 +1594,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
log_warn(LD_CONTROL, "getinfo '%s': %s", question, msg);
smartlist_free(descs);
tor_free(url);
+ *errmsg = msg;
return -1;
}
SMARTLIST_FOREACH(descs, signed_descriptor_t *, sd,
@@ -1587,7 +1645,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
}
} else if (!strcmp(question, "dir/status-vote/current/consensus")) { /* v3 */
if (directory_caches_dir_info(get_options())) {
- const cached_dir_t *consensus = dirserv_get_consensus();
+ const cached_dir_t *consensus = dirserv_get_consensus("ns");
if (consensus)
*answer = tor_strdup(consensus->dir);
}
@@ -1598,10 +1656,8 @@ getinfo_helper_dir(control_connection_t *control_conn,
}
} else if (!strcmp(question, "network-status")) { /* v1 */
routerlist_t *routerlist = router_get_routerlist();
- int verbose = control_conn->use_long_names;
if (!routerlist || !routerlist->routers ||
- list_server_status_v1(routerlist->routers, answer,
- verbose ? 2 : 1) < 0) {
+ list_server_status_v1(routerlist->routers, answer, 1) < 0) {
return -1;
}
} else if (!strcmpstart(question, "extra-info/digest/")) {
@@ -1635,8 +1691,10 @@ getinfo_helper_dir(control_connection_t *control_conn,
* current states of things we send events about. */
static int
getinfo_helper_events(control_connection_t *control_conn,
- const char *question, char **answer)
+ const char *question, char **answer,
+ const char **errmsg)
{
+ (void) control_conn;
if (!strcmp(question, "circuit-status")) {
circuit_t *circ;
smartlist_t *status = smartlist_create();
@@ -1647,10 +1705,9 @@ getinfo_helper_events(control_connection_t *control_conn,
const char *purpose;
if (! CIRCUIT_IS_ORIGIN(circ) || circ->marked_for_close)
continue;
- if (control_conn->use_long_names)
- path = circuit_list_path_for_controller(TO_ORIGIN_CIRCUIT(circ));
- else
- path = circuit_list_path(TO_ORIGIN_CIRCUIT(circ),0);
+
+ path = circuit_list_path_for_controller(TO_ORIGIN_CIRCUIT(circ));
+
if (circ->state == CIRCUIT_STATE_OPEN)
state = "BUILT";
else if (strlen(path))
@@ -1728,8 +1785,7 @@ getinfo_helper_events(control_connection_t *control_conn,
} else if (!strcmp(question, "orconn-status")) {
smartlist_t *conns = get_connection_array();
smartlist_t *status = smartlist_create();
- SMARTLIST_FOREACH(conns, connection_t *, base_conn,
- {
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
const char *state;
char *s;
char name[128];
@@ -1744,29 +1800,19 @@ getinfo_helper_events(control_connection_t *control_conn,
state = "LAUNCHED";
else
state = "NEW";
- orconn_target_get_name(control_conn->use_long_names, name, sizeof(name),
- conn);
+ orconn_target_get_name(name, sizeof(name), conn);
slen = strlen(name)+strlen(state)+2;
s = tor_malloc(slen+1);
tor_snprintf(s, slen, "%s %s", name, state);
smartlist_add(status, s);
- });
+ } SMARTLIST_FOREACH_END(base_conn);
*answer = smartlist_join_strings(status, "\r\n", 0, NULL);
SMARTLIST_FOREACH(status, char *, cp, tor_free(cp));
smartlist_free(status);
- } else if (!strcmpstart(question, "addr-mappings/") ||
- !strcmpstart(question, "address-mappings/")) {
+ } else if (!strcmpstart(question, "address-mappings/")) {
time_t min_e, max_e;
smartlist_t *mappings;
- int want_expiry = !strcmpstart(question, "address-mappings/");
- if (!strcmpstart(question, "addr-mappings/")) {
- /* XXXX022 This has been deprecated since 0.2.0.3-alpha, and has
- generated a warning since 0.2.1.10-alpha; remove late in 0.2.2.x. */
- log_warn(LD_CONTROL, "Controller used obsolete addr-mappings/ GETINFO "
- "key; use address-mappings/ instead.");
- }
- question += strlen(want_expiry ? "address-mappings/"
- : "addr-mappings/");
+ question += strlen("address-mappings/");
if (!strcmp(question, "all")) {
min_e = 0; max_e = TIME_MAX;
} else if (!strcmp(question, "cache")) {
@@ -1779,7 +1825,7 @@ getinfo_helper_events(control_connection_t *control_conn,
return 0;
}
mappings = smartlist_create();
- addressmap_get_mappings(mappings, min_e, max_e, want_expiry);
+ addressmap_get_mappings(mappings, min_e, max_e, 1);
*answer = smartlist_join_strings(mappings, "\r\n", 0, NULL);
SMARTLIST_FOREACH(mappings, char *, cp, tor_free(cp));
smartlist_free(mappings);
@@ -1787,7 +1833,7 @@ getinfo_helper_events(control_connection_t *control_conn,
/* Note that status/ is not a catch-all for events; there's only supposed
* to be a status GETINFO if there's a corresponding STATUS event. */
if (!strcmp(question, "status/circuit-established")) {
- *answer = tor_strdup(has_completed_circuit ? "1" : "0");
+ *answer = tor_strdup(can_complete_circuit ? "1" : "0");
} else if (!strcmp(question, "status/enough-dir-info")) {
*answer = tor_strdup(router_have_minimum_dir_info() ? "1" : "0");
} else if (!strcmp(question, "status/good-server-descriptor") ||
@@ -1846,21 +1892,12 @@ getinfo_helper_events(control_connection_t *control_conn,
"information", question);
}
} else if (!strcmp(question, "status/clients-seen")) {
- char geoip_start[ISO_TIME_LEN+1];
- size_t answer_len;
- char *geoip_summary = extrainfo_get_client_geoip_summary(time(NULL));
-
- if (!geoip_summary)
+ char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL));
+ if (!bridge_stats) {
+ *errmsg = "No bridge-client stats available";
return -1;
-
- answer_len = strlen("TimeStarted=\"\" CountrySummary=") +
- ISO_TIME_LEN + strlen(geoip_summary) + 1;
- *answer = tor_malloc(answer_len);
- format_iso_time(geoip_start, geoip_get_history_start());
- tor_snprintf(*answer, answer_len,
- "TimeStarted=\"%s\" CountrySummary=%s",
- geoip_start, geoip_summary);
- tor_free(geoip_summary);
+ }
+ *answer = bridge_stats;
} else {
return 0;
}
@@ -1870,11 +1907,14 @@ getinfo_helper_events(control_connection_t *control_conn,
/** Callback function for GETINFO: on a given control connection, try to
* answer the question <b>q</b> and store the newly-allocated answer in
- * *<b>a</b>. If there's no answer, or an error occurs, just don't set
- * <b>a</b>. Return 0.
+ * *<b>a</b>. If an internal error occurs, return -1 and optionally set
+ * *<b>error_out</b> to point to an error message to be delivered to the
+ * controller. On success, _or if the key is not recognized_, return 0. Do not
+ * set <b>a</b> if the key is not recognized.
*/
typedef int (*getinfo_helper_t)(control_connection_t *,
- const char *q, char **a);
+ const char *q, char **a,
+ const char **error_out);
/** A single item for the GETINFO question-to-answer-function table. */
typedef struct getinfo_item_t {
@@ -1894,6 +1934,8 @@ typedef struct getinfo_item_t {
static const getinfo_item_t getinfo_items[] = {
ITEM("version", misc, "The current version of Tor."),
ITEM("config-file", misc, "Current location of the \"torrc\" file."),
+ ITEM("config-text", misc,
+ "Return the string that would be written by a saveconf command."),
ITEM("accounting/bytes", accounting,
"Number of bytes read/written so far in the accounting interval."),
ITEM("accounting/bytes-left", accounting,
@@ -1924,6 +1966,7 @@ static const getinfo_item_t getinfo_items[] = {
"All non-expired, non-superseded router descriptors."),
ITEM("desc/all-recent-extrainfo-hack", dir, NULL), /* Hack. */
PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
+ PREFIX("net/listeners/", listeners, "Bound addresses by type"),
ITEM("ns/all", networkstatus,
"Brief summary of router status (v2 directory format)"),
PREFIX("ns/id/", networkstatus,
@@ -1933,7 +1976,6 @@ static const getinfo_item_t getinfo_items[] = {
PREFIX("ns/purpose/", networkstatus,
"Brief summary of router status by purpose (v2 directory format)."),
- PREFIX("unregistered-servers-", dirserv_unregistered, NULL),
ITEM("network-status", dir,
"Brief summary of router status (v1 directory format)"),
ITEM("circuit-status", events, "List of current circuits originating here."),
@@ -1945,14 +1987,6 @@ static const getinfo_item_t getinfo_items[] = {
DOC("address-mappings/config",
"Current address mappings from configuration."),
DOC("address-mappings/control", "Current address mappings from controller."),
- PREFIX("addr-mappings/", events, NULL),
- DOC("addr-mappings/all", "Current address mappings without expiry times."),
- DOC("addr-mappings/cache",
- "Current cached DNS replies without expiry times."),
- DOC("addr-mappings/config",
- "Current address mappings from configuration without expiry times."),
- DOC("addr-mappings/control",
- "Current address mappings from controller without expiry times."),
PREFIX("status/", events, NULL),
DOC("status/circuit-established",
"Whether we think client functionality is working."),
@@ -1977,8 +2011,8 @@ static const getinfo_item_t getinfo_items[] = {
"v2 networkstatus docs as retrieved from a DirPort."),
ITEM("dir/status-vote/current/consensus", dir,
"v3 Networkstatus consensus as retrieved from a DirPort."),
- PREFIX("exit-policy/default", policies,
- "The default value appended to the configured exit policy."),
+ ITEM("exit-policy/default", policies,
+ "The default value appended to the configured exit policy."),
PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"),
{ NULL, NULL, NULL, 0 }
};
@@ -1988,18 +2022,18 @@ static char *
list_getinfo_options(void)
{
int i;
- char buf[300];
+ char *buf=NULL;
smartlist_t *lines = smartlist_create();
char *ans;
for (i = 0; getinfo_items[i].varname; ++i) {
if (!getinfo_items[i].desc)
continue;
- tor_snprintf(buf, sizeof(buf), "%s%s -- %s\n",
+ tor_asprintf(&buf, "%s%s -- %s\n",
getinfo_items[i].varname,
getinfo_items[i].is_prefix ? "*" : "",
getinfo_items[i].desc);
- smartlist_add(lines, tor_strdup(buf));
+ smartlist_add(lines, buf);
}
smartlist_sort_strings(lines);
@@ -2016,7 +2050,8 @@ list_getinfo_options(void)
* internal error. */
static int
handle_getinfo_helper(control_connection_t *control_conn,
- const char *question, char **answer)
+ const char *question, char **answer,
+ const char **err_out)
{
int i;
*answer = NULL; /* unrecognized key by default */
@@ -2029,7 +2064,7 @@ handle_getinfo_helper(control_connection_t *control_conn,
match = !strcmp(question, getinfo_items[i].varname);
if (match) {
tor_assert(getinfo_items[i].fn);
- return getinfo_items[i].fn(control_conn, question, answer);
+ return getinfo_items[i].fn(control_conn, question, answer, err_out);
}
}
@@ -2051,10 +2086,12 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
smartlist_split_string(questions, body, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- SMARTLIST_FOREACH(questions, const char *, q,
- {
- if (handle_getinfo_helper(conn, q, &ans) < 0) {
- connection_write_str_to_buf("551 Internal error\r\n", conn);
+ SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
+ const char *errmsg = NULL;
+ if (handle_getinfo_helper(conn, q, &ans, &errmsg) < 0) {
+ if (!errmsg)
+ errmsg = "Internal error";
+ connection_printf_to_buf(conn, "551 %s\r\n", errmsg);
goto done;
}
if (!ans) {
@@ -2063,7 +2100,7 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
smartlist_add(answers, tor_strdup(q));
smartlist_add(answers, ans);
}
- });
+ } SMARTLIST_FOREACH_END(q);
if (smartlist_len(unrecognized)) {
for (i=0; i < smartlist_len(unrecognized)-1; ++i)
connection_printf_to_buf(conn,
@@ -2108,12 +2145,12 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
static uint8_t
circuit_purpose_from_string(const char *string)
{
- if (!strcmpstart(string, "purpose="))
+ if (!strcasecmpstart(string, "purpose="))
string += strlen("purpose=");
- if (!strcmp(string, "general"))
+ if (!strcasecmp(string, "general"))
return CIRCUIT_PURPOSE_C_GENERAL;
- else if (!strcmp(string, "controller"))
+ else if (!strcasecmp(string, "controller"))
return CIRCUIT_PURPOSE_CONTROLLER;
else
return CIRCUIT_PURPOSE_UNKNOWN;
@@ -2145,6 +2182,31 @@ getargs_helper(const char *command, control_connection_t *conn,
return NULL;
}
+/** Helper. Return the first element of <b>sl</b> at index <b>start_at</b> or
+ * higher that starts with <b>prefix</b>, case-insensitive. Return NULL if no
+ * such element exists. */
+static const char *
+find_element_starting_with(smartlist_t *sl, int start_at, const char *prefix)
+{
+ int i;
+ for (i = start_at; i < smartlist_len(sl); ++i) {
+ const char *elt = smartlist_get(sl, i);
+ if (!strcasecmpstart(elt, prefix))
+ return elt;
+ }
+ return NULL;
+}
+
+/** Helper. Return true iff s is an argument that we should treat as a
+ * key-value pair. */
+static int
+is_keyval_pair(const char *s)
+{
+ /* An argument is a key-value pair if it has an =, and it isn't of the form
+ * $fingeprint=name */
+ return strchr(s, '=') && s[0] != '$';
+}
+
/** Called when we get an EXTENDCIRCUIT message. Try to extend the listed
* circuit, and report success or failure. */
static int
@@ -2160,33 +2222,57 @@ 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 && !(circ = get_circ(smartlist_get(args,0)))) {
- connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
- (char*)smartlist_get(args, 0));
- }
- 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);
+ if (zero_circ) {
+ const char *purp = find_element_starting_with(args, 1, "PURPOSE=");
+
+ 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);
+ goto done;
+ }
+ }
+
+ if ((smartlist_len(args) == 1) ||
+ (smartlist_len(args) >= 2 && is_keyval_pair(smartlist_get(args, 1)))) {
+ // "EXTENDCIRCUIT 0" || EXTENDCIRCUIT 0 foo=bar"
+ 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);
+ }
SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
smartlist_free(args);
goto done;
}
+ // "EXTENDCIRCUIT 0 router1,router2" ||
+ // "EXTENDCIRCUIT 0 router1,router2 PURPOSE=foo"
}
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
- if (!zero_circ && !circ) {
+
+ 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));
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
goto done;
}
+ smartlist_split_string(router_nicknames, smartlist_get(args,1), ",", 0, 0);
+
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+
routers = smartlist_create();
SMARTLIST_FOREACH(router_nicknames, const char *, n,
{
@@ -2244,8 +2330,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
done:
SMARTLIST_FOREACH(router_nicknames, char *, n, tor_free(n));
smartlist_free(router_nicknames);
- if (routers)
- smartlist_free(routers);
+ smartlist_free(routers);
return 0;
}
@@ -2271,7 +2356,7 @@ handle_control_setcircuitpurpose(control_connection_t *conn,
}
{
- char *purp = smartlist_get(args,1);
+ const char *purp = find_element_starting_with(args,1,"PURPOSE=");
new_purpose = circuit_purpose_from_string(purp);
if (new_purpose == CIRCUIT_PURPOSE_UNKNOWN) {
connection_printf_to_buf(conn, "552 Unknown purpose \"%s\"\r\n", purp);
@@ -2282,7 +2367,7 @@ handle_control_setcircuitpurpose(control_connection_t *conn,
circ->_base.purpose = new_purpose;
connection_write_str_to_buf("250 OK\r\n", conn);
-done:
+ done:
if (args) {
SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
smartlist_free(args);
@@ -2316,9 +2401,9 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
} else if (!zero_circ && !(circ = get_circ(smartlist_get(args, 1)))) {
connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
(char*)smartlist_get(args, 1));
- } else if (circ && smartlist_len(args) > 2) {
- char *hopstring = smartlist_get(args, 2);
- if (!strcasecmpstart(hopstring, "HOP=")) {
+ } else if (circ) {
+ const char *hopstring = find_element_starting_with(args,2,"HOP=");
+ if (hopstring) {
hopstring += strlen("HOP=");
hop = (int) tor_parse_ulong(hopstring, 10, 0, INT_MAX,
&hop_line_ok, NULL);
@@ -2365,7 +2450,7 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
char* exit_digest;
if (circ->build_state &&
circ->build_state->chosen_exit &&
- circ->build_state->chosen_exit->identity_digest) {
+ !tor_digest_is_zero(circ->build_state->chosen_exit->identity_digest)) {
exit_digest = circ->build_state->chosen_exit->identity_digest;
r = router_get_by_digest(exit_digest);
}
@@ -2426,9 +2511,9 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
}
} else if (!strcasecmpstart(option, "cache=")) {
option += strlen("cache=");
- if (!strcmp(option, "no"))
+ if (!strcasecmp(option, "no"))
cache = 0;
- else if (!strcmp(option, "yes"))
+ else if (!strcasecmp(option, "yes"))
cache = 1;
else {
connection_printf_to_buf(conn, "552 Unknown cache request \"%s\"\r\n",
@@ -2610,17 +2695,17 @@ handle_control_resolve(control_connection_t *conn, uint32_t len,
args = smartlist_create();
smartlist_split_string(args, body, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- if (smartlist_len(args) &&
- !strcasecmp(smartlist_get(args, 0), "mode=reverse")) {
- char *cp = smartlist_get(args, 0);
- smartlist_del_keeporder(args, 0);
- tor_free(cp);
- is_reverse = 1;
+ {
+ const char *modearg = find_element_starting_with(args, 0, "mode=");
+ if (modearg && !strcasecmp(modearg, "mode=reverse"))
+ is_reverse = 1;
}
failed = smartlist_create();
SMARTLIST_FOREACH(args, const char *, arg, {
- if (dnsserv_launch_request(arg, is_reverse)<0)
- smartlist_add(failed, (char*)arg);
+ if (!is_keyval_pair(arg)) {
+ if (dnsserv_launch_request(arg, is_reverse)<0)
+ smartlist_add(failed, (char*)arg);
+ }
});
send_control_done(conn);
@@ -2710,7 +2795,6 @@ handle_control_usefeature(control_connection_t *conn,
const char *body)
{
smartlist_t *args;
- int verbose_names = 0, extended_events = 0;
int bad = 0;
(void) len; /* body is nul-terminated; it's safe to ignore the length */
args = smartlist_create();
@@ -2718,9 +2802,9 @@ handle_control_usefeature(control_connection_t *conn,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH(args, const char *, arg, {
if (!strcasecmp(arg, "VERBOSE_NAMES"))
- verbose_names = 1;
+ ;
else if (!strcasecmp(arg, "EXTENDED_EVENTS"))
- extended_events = 1;
+ ;
else {
connection_printf_to_buf(conn, "552 Unrecognized feature \"%s\"\r\n",
arg);
@@ -2730,12 +2814,6 @@ handle_control_usefeature(control_connection_t *conn,
});
if (!bad) {
- if (verbose_names) {
- conn->use_long_names = 1;
- control_update_global_event_mask();
- }
- if (extended_events)
- conn->use_extended_events = 1;
send_control_done(conn);
}
@@ -2765,6 +2843,43 @@ connection_control_reached_eof(control_connection_t *conn)
return 0;
}
+/** Shut down this Tor instance in the same way that SIGINT would, but
+ * with a log message appropriate for the loss of an owning controller. */
+static void
+lost_owning_controller(const char *owner_type, const char *loss_manner)
+{
+ int shutdown_slowly = server_mode(get_options());
+
+ log_notice(LD_CONTROL, "Owning controller %s has %s -- %s.",
+ owner_type, loss_manner,
+ shutdown_slowly ? "shutting down" : "exiting now");
+
+ /* XXXX Perhaps this chunk of code should be a separate function,
+ * called here and by process_signal(SIGINT). */
+
+ if (!shutdown_slowly) {
+ tor_cleanup();
+ exit(0);
+ }
+ /* XXXX This will close all listening sockets except control-port
+ * listeners. Perhaps we should close those too. */
+ hibernate_begin_shutdown();
+}
+
+/** Called when <b>conn</b> is being freed. */
+void
+connection_control_closed(control_connection_t *conn)
+{
+ tor_assert(conn);
+
+ conn->event_mask = 0;
+ control_update_global_event_mask();
+
+ if (conn->is_owning_control_connection) {
+ lost_owning_controller("connection", "closed");
+ }
+}
+
/** Return true iff <b>cmd</b> is allowable (or at least forgivable) at this
* stage of the protocol. */
static int
@@ -2885,9 +3000,10 @@ connection_control_process_inbuf(control_connection_t *conn)
&& !TOR_ISSPACE(conn->incoming_cmd[cmd_len]))
++cmd_len;
- data_len -= cmd_len;
conn->incoming_cmd[cmd_len]='\0';
args = conn->incoming_cmd+cmd_len+1;
+ tor_assert(data_len>(size_t)cmd_len);
+ data_len -= (cmd_len+1); /* skip the command and NUL we added after it */
while (*args == ' ' || *args == '\t') {
++args;
--data_len;
@@ -2919,6 +3035,9 @@ connection_control_process_inbuf(control_connection_t *conn)
return 0;
}
+ /* XXXX Why is this not implemented as a table like the GETINFO
+ * items are? Even handling the plus signs at the beginnings of
+ * commands wouldn't be very hard with proper macros. */
cmd_data_len = (uint32_t)data_len;
if (!strcasecmp(conn->incoming_cmd, "SETCONF")) {
if (handle_control_setconf(conn, cmd_data_len, args))
@@ -2944,6 +3063,9 @@ connection_control_process_inbuf(control_connection_t *conn)
} else if (!strcasecmp(conn->incoming_cmd, "SIGNAL")) {
if (handle_control_signal(conn, cmd_data_len, args))
return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "TAKEOWNERSHIP")) {
+ if (handle_control_takeownership(conn, cmd_data_len, args))
+ return -1;
} else if (!strcasecmp(conn->incoming_cmd, "MAPADDRESS")) {
if (handle_control_mapaddress(conn, cmd_data_len, args))
return -1;
@@ -3037,20 +3159,11 @@ control_event_circuit_status(origin_circuit_t *circ, circuit_status_event_t tp,
tor_free(reason);
}
- if (EVENT_IS_INTERESTING1S(EVENT_CIRCUIT_STATUS)) {
- char *path = circuit_list_path(circ,0);
- const char *sp = strlen(path) ? " " : "";
- send_control_event_extended(EVENT_CIRCUIT_STATUS, SHORT_NAMES,
- "650 CIRC %lu %s%s%s@%s\r\n",
- (unsigned long)circ->global_identifier,
- status, sp, path, extended_buf);
- tor_free(path);
- }
- if (EVENT_IS_INTERESTING1L(EVENT_CIRCUIT_STATUS)) {
+ {
char *vpath = circuit_list_path_for_controller(circ);
const char *sp = strlen(vpath) ? " " : "";
- send_control_event_extended(EVENT_CIRCUIT_STATUS, LONG_NAMES,
- "650 CIRC %lu %s%s%s@%s\r\n",
+ send_control_event(EVENT_CIRCUIT_STATUS, ALL_FORMATS,
+ "650 CIRC %lu %s%s%s %s\r\n",
(unsigned long)circ->global_identifier,
status, sp, vpath, extended_buf);
tor_free(vpath);
@@ -3129,26 +3242,26 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
char *r = NULL;
if (!reason_str) {
r = tor_malloc(16);
- tor_snprintf(r, 16, "UNKNOWN_%d", reason_code);
+ tor_snprintf(r, 16, " UNKNOWN_%d", reason_code);
reason_str = r;
}
if (reason_code & END_STREAM_REASON_FLAG_REMOTE)
tor_snprintf(reason_buf, sizeof(reason_buf),
- "REASON=END REMOTE_REASON=%s", reason_str);
+ " REASON=END REMOTE_REASON=%s", reason_str);
else
tor_snprintf(reason_buf, sizeof(reason_buf),
- "REASON=%s", reason_str);
+ " REASON=%s", reason_str);
tor_free(r);
} else if (reason_code && tp == STREAM_EVENT_REMAP) {
switch (reason_code) {
case REMAP_STREAM_SOURCE_CACHE:
- strlcpy(reason_buf, "SOURCE=CACHE", sizeof(reason_buf));
+ strlcpy(reason_buf, " SOURCE=CACHE", sizeof(reason_buf));
break;
case REMAP_STREAM_SOURCE_EXIT:
- strlcpy(reason_buf, "SOURCE=EXIT", sizeof(reason_buf));
+ strlcpy(reason_buf, " SOURCE=EXIT", sizeof(reason_buf));
break;
default:
- tor_snprintf(reason_buf, sizeof(reason_buf), "REASON=UNKNOWN_%d",
+ tor_snprintf(reason_buf, sizeof(reason_buf), " REASON=UNKNOWN_%d",
reason_code);
/* XXX do we want SOURCE=UNKNOWN_%d above instead? -RD */
break;
@@ -3156,8 +3269,7 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
}
if (tp == STREAM_EVENT_NEW) {
- tor_snprintf(addrport_buf,sizeof(addrport_buf), "%sSOURCE_ADDR=%s:%d",
- strlen(reason_buf) ? " " : "",
+ tor_snprintf(addrport_buf,sizeof(addrport_buf), " SOURCE_ADDR=%s:%d",
TO_CONN(conn)->address, TO_CONN(conn)->port );
} else {
addrport_buf[0] = '\0';
@@ -3186,8 +3298,8 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
circ = circuit_get_by_edge_conn(conn);
if (circ && CIRCUIT_IS_ORIGIN(circ))
origin_circ = TO_ORIGIN_CIRCUIT(circ);
- send_control_event_extended(EVENT_STREAM_STATUS, ALL_NAMES,
- "650 STREAM "U64_FORMAT" %s %lu %s@%s%s%s\r\n",
+ send_control_event(EVENT_STREAM_STATUS, ALL_FORMATS,
+ "650 STREAM "U64_FORMAT" %s %lu %s%s%s%s\r\n",
U64_PRINTF_ARG(conn->_base.global_identifier), status,
origin_circ?
(unsigned long)origin_circ->global_identifier : 0ul,
@@ -3200,30 +3312,21 @@ control_event_stream_status(edge_connection_t *conn, stream_status_event_t tp,
/** Figure out the best name for the target router of an OR connection
* <b>conn</b>, and write it into the <b>len</b>-character buffer
- * <b>name</b>. Use verbose names if <b>long_names</b> is set. */
+ * <b>name</b>. */
static void
-orconn_target_get_name(int long_names,
- char *name, size_t len, or_connection_t *conn)
+orconn_target_get_name(char *name, size_t len, or_connection_t *conn)
{
- if (! long_names) {
- if (conn->nickname)
- strlcpy(name, conn->nickname, len);
- else
- tor_snprintf(name, len, "%s:%d",
- conn->_base.address, conn->_base.port);
+ routerinfo_t *ri = router_get_by_digest(conn->identity_digest);
+ if (ri) {
+ tor_assert(len > MAX_VERBOSE_NICKNAME_LEN);
+ router_get_verbose_nickname(name, ri);
+ } else if (! tor_digest_is_zero(conn->identity_digest)) {
+ name[0] = '$';
+ base16_encode(name+1, len-1, conn->identity_digest,
+ DIGEST_LEN);
} else {
- routerinfo_t *ri = router_get_by_digest(conn->identity_digest);
- if (ri) {
- tor_assert(len > MAX_VERBOSE_NICKNAME_LEN);
- router_get_verbose_nickname(name, ri);
- } else if (! tor_digest_is_zero(conn->identity_digest)) {
- name[0] = '$';
- base16_encode(name+1, len-1, conn->identity_digest,
- DIGEST_LEN);
- } else {
- tor_snprintf(name, len, "%s:%d",
- conn->_base.address, conn->_base.port);
- }
+ tor_snprintf(name, len, "%s:%d",
+ conn->_base.address, conn->_base.port);
}
}
@@ -3262,24 +3365,13 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp,
reason ? " " : "", ncircs);
}
- if (EVENT_IS_INTERESTING1S(EVENT_OR_CONN_STATUS)) {
- orconn_target_get_name(0, name, sizeof(name), conn);
- send_control_event_extended(EVENT_OR_CONN_STATUS, SHORT_NAMES,
- "650 ORCONN %s %s@%s%s%s\r\n",
- name, status,
- reason ? "REASON=" : "",
- orconn_end_reason_to_control_string(reason),
- ncircs_buf);
- }
- if (EVENT_IS_INTERESTING1L(EVENT_OR_CONN_STATUS)) {
- orconn_target_get_name(1, name, sizeof(name), conn);
- send_control_event_extended(EVENT_OR_CONN_STATUS, LONG_NAMES,
- "650 ORCONN %s %s@%s%s%s\r\n",
- name, status,
- reason ? "REASON=" : "",
- orconn_end_reason_to_control_string(reason),
- ncircs_buf);
- }
+ orconn_target_get_name(name, sizeof(name), conn);
+ send_control_event(EVENT_OR_CONN_STATUS, ALL_FORMATS,
+ "650 ORCONN %s %s %s%s%s\r\n",
+ name, status,
+ reason ? "REASON=" : "",
+ orconn_end_reason_to_control_string(reason),
+ ncircs_buf);
return 0;
}
@@ -3294,7 +3386,7 @@ control_event_stream_bandwidth(edge_connection_t *edge_conn)
if (!edge_conn->n_read && !edge_conn->n_written)
return 0;
- send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_NAMES,
+ send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_FORMATS,
"650 STREAM_BW "U64_FORMAT" %lu %lu\r\n",
U64_PRINTF_ARG(edge_conn->_base.global_identifier),
(unsigned long)edge_conn->n_read,
@@ -3323,7 +3415,7 @@ control_event_stream_bandwidth_used(void)
if (!edge_conn->n_read && !edge_conn->n_written)
continue;
- send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_NAMES,
+ send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_FORMATS,
"650 STREAM_BW "U64_FORMAT" %lu %lu\r\n",
U64_PRINTF_ARG(edge_conn->_base.global_identifier),
(unsigned long)edge_conn->n_read,
@@ -3343,7 +3435,7 @@ int
control_event_bandwidth_used(uint32_t n_read, uint32_t n_written)
{
if (EVENT_IS_INTERESTING(EVENT_BANDWIDTH_USED)) {
- send_control_event(EVENT_BANDWIDTH_USED, ALL_NAMES,
+ send_control_event(EVENT_BANDWIDTH_USED, ALL_FORMATS,
"650 BW %lu %lu\r\n",
(unsigned long)n_read,
(unsigned long)n_written);
@@ -3388,7 +3480,7 @@ control_event_logmsg(int severity, uint32_t domain, const char *msg)
severity <= LOG_NOTICE) {
char *esc = esc_for_log(msg);
++disable_log_messages;
- control_event_general_status(severity, "BUG REASON=\"%s\"", esc);
+ control_event_general_status(severity, "BUG REASON=%s", esc);
--disable_log_messages;
tor_free(esc);
}
@@ -3413,7 +3505,7 @@ control_event_logmsg(int severity, uint32_t domain, const char *msg)
default: s = "UnknownLogSeverity"; break;
}
++disable_log_messages;
- send_control_event(event, ALL_NAMES, "650 %s %s\r\n", s, b?b:msg);
+ send_control_event(event, ALL_FORMATS, "650 %s %s\r\n", s, b?b:msg);
--disable_log_messages;
tor_free(b);
}
@@ -3426,31 +3518,12 @@ control_event_logmsg(int severity, uint32_t domain, const char *msg)
int
control_event_descriptors_changed(smartlist_t *routers)
{
- size_t len;
char *msg;
- smartlist_t *identities = NULL;
- char buf[HEX_DIGEST_LEN+1];
if (!EVENT_IS_INTERESTING(EVENT_NEW_DESC))
return 0;
- if (EVENT_IS_INTERESTING1S(EVENT_NEW_DESC)) {
- identities = smartlist_create();
- SMARTLIST_FOREACH(routers, routerinfo_t *, r,
- {
- base16_encode(buf,sizeof(buf),r->cache_info.identity_digest,DIGEST_LEN);
- smartlist_add(identities, tor_strdup(buf));
- });
- }
- if (EVENT_IS_INTERESTING1S(EVENT_NEW_DESC)) {
- char *ids = smartlist_join_strings(identities, " ", 0, &len);
- size_t ids_len = strlen(ids)+32;
- msg = tor_malloc(ids_len);
- tor_snprintf(msg, ids_len, "650 NEWDESC %s\r\n", ids);
- send_control_event_string(EVENT_NEW_DESC, SHORT_NAMES|ALL_FORMATS, msg);
- tor_free(ids);
- tor_free(msg);
- }
- if (EVENT_IS_INTERESTING1L(EVENT_NEW_DESC)) {
+
+ {
smartlist_t *names = smartlist_create();
char *ids;
size_t names_len;
@@ -3463,16 +3536,12 @@ control_event_descriptors_changed(smartlist_t *routers)
names_len = strlen(ids)+32;
msg = tor_malloc(names_len);
tor_snprintf(msg, names_len, "650 NEWDESC %s\r\n", ids);
- send_control_event_string(EVENT_NEW_DESC, LONG_NAMES|ALL_FORMATS, msg);
+ send_control_event_string(EVENT_NEW_DESC, ALL_FORMATS, msg);
tor_free(ids);
tor_free(msg);
SMARTLIST_FOREACH(names, char *, cp, tor_free(cp));
smartlist_free(names);
}
- if (identities) {
- SMARTLIST_FOREACH(identities, char *, cp, tor_free(cp));
- smartlist_free(identities);
- }
return 0;
}
@@ -3489,17 +3558,17 @@ control_event_address_mapped(const char *from, const char *to, time_t expires,
return 0;
if (expires < 3 || expires == TIME_MAX)
- send_control_event_extended(EVENT_ADDRMAP, ALL_NAMES,
- "650 ADDRMAP %s %s NEVER@%s\r\n", from, to,
+ send_control_event(EVENT_ADDRMAP, ALL_FORMATS,
+ "650 ADDRMAP %s %s NEVER %s\r\n", from, to,
error?error:"");
else {
char buf[ISO_TIME_LEN+1];
char buf2[ISO_TIME_LEN+1];
format_local_iso_time(buf,expires);
format_iso_time(buf2,expires);
- send_control_event_extended(EVENT_ADDRMAP, ALL_NAMES,
+ send_control_event(EVENT_ADDRMAP, ALL_FORMATS,
"650 ADDRMAP %s %s \"%s\""
- "@%s%sEXPIRES=\"%s\"\r\n",
+ " %s%sEXPIRES=\"%s\"\r\n",
from, to, buf,
error?error:"", error?" ":"",
buf2);
@@ -3539,9 +3608,9 @@ control_event_or_authdir_new_descriptor(const char *action,
buf = tor_malloc(totallen);
strlcpy(buf, firstline, totallen);
strlcpy(buf+strlen(firstline), esc, totallen);
- send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_NAMES|ALL_FORMATS,
+ send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_FORMATS,
buf);
- send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_NAMES|ALL_FORMATS,
+ send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_FORMATS,
"650 OK\r\n");
tor_free(esc);
tor_free(buf);
@@ -3579,8 +3648,8 @@ control_event_networkstatus_changed_helper(smartlist_t *statuses,
SMARTLIST_FOREACH(strs, char *, cp, tor_free(cp));
smartlist_free(strs);
tor_free(s);
- send_control_event_string(event, ALL_NAMES|ALL_FORMATS, esc);
- send_control_event_string(event, ALL_NAMES|ALL_FORMATS,
+ send_control_event_string(event, ALL_FORMATS, esc);
+ send_control_event_string(event, ALL_FORMATS,
"650 OK\r\n");
tor_free(esc);
@@ -3606,6 +3675,55 @@ control_event_newconsensus(const networkstatus_t *consensus)
consensus->routerstatus_list, EVENT_NEWCONSENSUS, "NEWCONSENSUS");
}
+/** Called when we compute a new circuitbuildtimeout */
+int
+control_event_buildtimeout_set(const circuit_build_times_t *cbt,
+ buildtimeout_set_event_t type)
+{
+ const char *type_string = NULL;
+ double qnt = circuit_build_times_quantile_cutoff();
+
+ if (!control_event_is_interesting(EVENT_BUILDTIMEOUT_SET))
+ return 0;
+
+ switch (type) {
+ case BUILDTIMEOUT_SET_EVENT_COMPUTED:
+ type_string = "COMPUTED";
+ break;
+ case BUILDTIMEOUT_SET_EVENT_RESET:
+ type_string = "RESET";
+ qnt = 1.0;
+ break;
+ case BUILDTIMEOUT_SET_EVENT_SUSPENDED:
+ type_string = "SUSPENDED";
+ qnt = 1.0;
+ break;
+ case BUILDTIMEOUT_SET_EVENT_DISCARD:
+ type_string = "DISCARD";
+ qnt = 1.0;
+ break;
+ case BUILDTIMEOUT_SET_EVENT_RESUME:
+ type_string = "RESUME";
+ break;
+ default:
+ type_string = "UNKNOWN";
+ break;
+ }
+
+ send_control_event(EVENT_BUILDTIMEOUT_SET, ALL_FORMATS,
+ "650 BUILDTIMEOUT_SET %s TOTAL_TIMES=%lu "
+ "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f "
+ "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f\r\n",
+ type_string, (unsigned long)cbt->total_build_times,
+ (unsigned long)cbt->timeout_ms,
+ (unsigned long)cbt->Xm, cbt->alpha, qnt,
+ circuit_build_times_timeout_rate(cbt),
+ (unsigned long)cbt->close_ms,
+ circuit_build_times_close_rate(cbt));
+
+ return 0;
+}
+
/** Called when a single local_routerstatus_t has changed: Sends an NS event
* to any controller that cares. */
int
@@ -3629,7 +3747,7 @@ control_event_networkstatus_changed_single(routerstatus_t *rs)
int
control_event_my_descriptor_changed(void)
{
- send_control_event(EVENT_DESCCHANGED, ALL_NAMES, "650 DESCCHANGED\r\n");
+ send_control_event(EVENT_DESCCHANGED, ALL_FORMATS, "650 DESCCHANGED\r\n");
return 0;
}
@@ -3677,7 +3795,7 @@ control_event_status(int type, int severity, const char *format, va_list args)
return -1;
}
- send_control_event_impl(type, ALL_NAMES|ALL_FORMATS, 0, format_buf, args);
+ send_control_event_impl(type, ALL_FORMATS, format_buf, args);
return 0;
}
@@ -3741,7 +3859,7 @@ control_event_guard(const char *nickname, const char *digest,
if (!EVENT_IS_INTERESTING(EVENT_GUARD))
return 0;
- if (EVENT_IS_INTERESTING1L(EVENT_GUARD)) {
+ {
char buf[MAX_VERBOSE_NICKNAME_LEN+1];
routerinfo_t *ri = router_get_by_digest(digest);
if (ri) {
@@ -3749,13 +3867,9 @@ control_event_guard(const char *nickname, const char *digest,
} else {
tor_snprintf(buf, sizeof(buf), "$%s~%s", hbuf, nickname);
}
- send_control_event(EVENT_GUARD, LONG_NAMES,
+ send_control_event(EVENT_GUARD, ALL_FORMATS,
"650 GUARD ENTRY %s %s\r\n", buf, status);
}
- if (EVENT_IS_INTERESTING1S(EVENT_GUARD)) {
- send_control_event(EVENT_GUARD, SHORT_NAMES,
- "650 GUARD ENTRY $%s %s\r\n", hbuf, status);
- }
return 0;
}
@@ -3812,6 +3926,75 @@ init_cookie_authentication(int enabled)
return 0;
}
+/** A copy of the process specifier of Tor's owning controller, or
+ * NULL if this Tor instance is not currently owned by a process. */
+static char *owning_controller_process_spec = NULL;
+
+/** A process-termination monitor for Tor's owning controller, or NULL
+ * if this Tor instance is not currently owned by a process. */
+static tor_process_monitor_t *owning_controller_process_monitor = NULL;
+
+/** Process-termination monitor callback for Tor's owning controller
+ * process. */
+static void
+owning_controller_procmon_cb(void *unused)
+{
+ (void)unused;
+
+ lost_owning_controller("process", "vanished");
+}
+
+/** Set <b>process_spec</b> as Tor's owning controller process.
+ * Exit on failure. */
+void
+monitor_owning_controller_process(const char *process_spec)
+{
+ const char *msg;
+
+ tor_assert((owning_controller_process_spec == NULL) ==
+ (owning_controller_process_monitor == NULL));
+
+ if (owning_controller_process_spec != NULL) {
+ if ((process_spec != NULL) && !strcmp(process_spec,
+ owning_controller_process_spec)) {
+ /* Same process -- return now, instead of disposing of and
+ * recreating the process-termination monitor. */
+ return;
+ }
+
+ /* We are currently owned by a process, and we should no longer be
+ * owned by it. Free the process-termination monitor. */
+ tor_process_monitor_free(owning_controller_process_monitor);
+ owning_controller_process_monitor = NULL;
+
+ tor_free(owning_controller_process_spec);
+ owning_controller_process_spec = NULL;
+ }
+
+ tor_assert((owning_controller_process_spec == NULL) &&
+ (owning_controller_process_monitor == NULL));
+
+ if (process_spec == NULL)
+ return;
+
+ owning_controller_process_spec = tor_strdup(process_spec);
+ owning_controller_process_monitor =
+ tor_process_monitor_new(tor_libevent_get_base(),
+ owning_controller_process_spec,
+ LD_CONTROL,
+ owning_controller_procmon_cb, NULL,
+ &msg);
+
+ if (owning_controller_process_monitor == NULL) {
+ log_err(LD_BUG, "Couldn't create process-termination monitor for "
+ "owning controller: %s. Exiting.",
+ msg);
+ owning_controller_process_spec = NULL;
+ tor_cleanup();
+ exit(0);
+ }
+}
+
/** Convert the name of a bootstrapping phase <b>s</b> into strings
* <b>tag</b> and <b>summary</b> suitable for display by the controller. */
static int
@@ -3908,7 +4091,7 @@ static int bootstrap_problems = 0;
* information and initial circuits.
*
* <b>status</b> is the new status, that is, what task we will be doing
- * next. <b>percent</b> is zero if we just started this task, else it
+ * next. <b>progress</b> is zero if we just started this task, else it
* represents progress on the task. */
void
control_event_bootstrap(bootstrap_status_t status, int progress)
@@ -3964,6 +4147,9 @@ control_event_bootstrap_problem(const char *warn, int reason)
char buf[BOOTSTRAP_MSG_LEN];
const char *recommendation = "ignore";
+ /* bootstrap_percent must not be in "undefined" state here. */
+ tor_assert(status >= 0);
+
if (bootstrap_percent == 100)
return; /* already bootstrapped; nothing to be done here. */
@@ -4006,10 +4192,9 @@ control_event_bootstrap_problem(const char *warn, int reason)
* from recently. Send a copy to the controller in case it wants to
* display it for the user. */
void
-control_event_clients_seen(const char *timestarted, const char *countries)
+control_event_clients_seen(const char *controller_str)
{
send_control_event(EVENT_CLIENTS_SEEN, 0,
- "650 CLIENTS_SEEN TimeStarted=\"%s\" CountrySummary=%s\r\n",
- timestarted, countries);
+ "650 CLIENTS_SEEN %s\r\n", controller_str);
}