commit 01b07c548b93bcc58adac612a02c69dcb4b63b28
Author: Nick Mathewson <nickm(a)torproject.org>
Date: Tue Apr 2 19:00:23 2019 -0400
Use parsing code for the simpler controller commands.
(This should be all of the command that work nicely with positional
arguments only.)
Some of these commands should probably treat extra arguments as
incorrect, but for now I'm trying to be careful not to break
any existing users.
---
scripts/maint/practracker/exceptions.txt | 4 +-
src/feature/control/control_cmd.c | 239 +++++++++++++++----------------
src/feature/control/control_cmd.h | 44 +++---
src/feature/control/control_getinfo.c | 16 +--
src/feature/control/control_getinfo.h | 8 +-
5 files changed, 154 insertions(+), 157 deletions(-)
diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt
index 7582395fe..438c1c582 100644
--- a/scripts/maint/practracker/exceptions.txt
+++ b/scripts/maint/practracker/exceptions.txt
@@ -54,7 +54,7 @@ problem function-size /src/app/main/main.c:sandbox_init_filter() 291
problem function-size /src/app/main/main.c:run_tor_main_loop() 105
problem function-size /src/app/main/ntmain.c:nt_service_install() 125
problem include-count /src/app/main/shutdown.c 52
-problem file-size /src/core/mainloop/connection.c 5558
+problem file-size /src/core/mainloop/connection.c 5559
problem include-count /src/core/mainloop/connection.c 61
problem function-size /src/core/mainloop/connection.c:connection_free_minimal() 185
problem function-size /src/core/mainloop/connection.c:connection_listener_new() 328
@@ -152,7 +152,7 @@ problem function-size /src/feature/control/control_cmd.c:handle_control_add_onio
problem function-size /src/feature/control/control_cmd.c:add_onion_helper_keyarg() 125
problem function-size /src/feature/control/control_cmd.c:handle_control_command() 104
problem function-size /src/feature/control/control_events.c:control_event_stream_status() 119
-problem include-count /src/feature/control/control_getinfo.c 52
+problem include-count /src/feature/control/control_getinfo.c 53
problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_misc() 109
problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_dir() 304
problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_events() 236
diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c
index 53cbf2bd0..f457e9fa5 100644
--- a/src/feature/control/control_cmd.c
+++ b/src/feature/control/control_cmd.c
@@ -160,13 +160,17 @@ handle_control_resetconf(control_connection_t *conn, uint32_t len, char *body)
return control_setconf_helper(conn, len, body, 1);
}
+static const control_cmd_syntax_t getconf_syntax = {
+ .max_args=UINT_MAX
+};
+
/** Called when we receive a GETCONF message. Parse the request, and
* reply with a CONFVALUE or an ERROR message */
static int
-handle_control_getconf(control_connection_t *conn, uint32_t body_len,
- char *body)
+handle_control_getconf(control_connection_t *conn,
+ const control_cmd_args_t *args)
{
- smartlist_t *questions = smartlist_new();
+ const smartlist_t *questions = args->args;
smartlist_t *answers = smartlist_new();
smartlist_t *unrecognized = smartlist_new();
char *msg = NULL;
@@ -174,9 +178,6 @@ handle_control_getconf(control_connection_t *conn, uint32_t body_len,
const or_options_t *options = get_options();
int i, len;
- (void) body_len; /* body is NUL-terminated; so we can ignore len. */
- smartlist_split_string(questions, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
if (!option_is_recognized(q)) {
smartlist_add(unrecognized, (char*) q);
@@ -221,8 +222,6 @@ handle_control_getconf(control_connection_t *conn, uint32_t body_len,
SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
smartlist_free(answers);
- SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp));
- smartlist_free(questions);
smartlist_free(unrecognized);
tor_free(msg);
@@ -230,17 +229,21 @@ handle_control_getconf(control_connection_t *conn, uint32_t body_len,
return 0;
}
+static const control_cmd_syntax_t loadconf_syntax = {
+ .want_object = true
+};
+
/** Called when we get a +LOADCONF message. */
static int
-handle_control_loadconf(control_connection_t *conn, uint32_t len,
- const char *body)
+handle_control_loadconf(control_connection_t *conn,
+ const control_cmd_args_t *args)
{
setopt_err_t retval;
char *errstring = NULL;
const char *msg = NULL;
- (void) len;
- retval = options_init_from_string(NULL, body, CMD_RUN_TOR, NULL, &errstring);
+ retval = options_init_from_string(NULL, args->object,
+ CMD_RUN_TOR, NULL, &errstring);
if (retval != SETOPT_OK)
log_warn(LD_CONTROL,
@@ -276,20 +279,20 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len,
return 0;
}
+static const control_cmd_syntax_t setevents_syntax = {
+ .max_args = UINT_MAX
+};
+
/** Called when we get a SETEVENTS message: update conn->event_mask,
* and reply with DONE or ERROR. */
static int
-handle_control_setevents(control_connection_t *conn, uint32_t len,
- const char *body)
+handle_control_setevents(control_connection_t *conn,
+ const control_cmd_args_t *args)
{
int event_code;
event_mask_t event_mask = 0;
- smartlist_t *events = smartlist_new();
-
- (void) len;
+ const smartlist_t *events = args->args;
- smartlist_split_string(events, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
{
if (!strcasecmp(ev, "EXTENDED") ||
@@ -311,16 +314,12 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
if (event_code == -1) {
connection_printf_to_buf(conn, "552 Unrecognized event \"%s\"\r\n",
ev);
- SMARTLIST_FOREACH(events, char *, e, tor_free(e));
- smartlist_free(events);
return 0;
}
}
event_mask |= (((event_mask_t)1) << event_code);
}
SMARTLIST_FOREACH_END(ev);
- SMARTLIST_FOREACH(events, char *, e, tor_free(e));
- smartlist_free(events);
conn->event_mask = event_mask;
@@ -348,23 +347,23 @@ handle_control_saveconf(control_connection_t *conn, uint32_t len,
return 0;
}
+static const control_cmd_syntax_t signal_syntax = {
+ .min_args = 1,
+ .max_args = 1,
+};
+
/** Called when we get a SIGNAL command. React to the provided signal, and
* report success or failure. (If the signal results in a shutdown, success
* may not be reported.) */
static int
-handle_control_signal(control_connection_t *conn, uint32_t len,
- const char *body)
+handle_control_signal(control_connection_t *conn,
+ const control_cmd_args_t *args)
{
int sig = -1;
int i;
- int n = 0;
- char *s;
-
- (void) len;
- while (body[n] && ! TOR_ISSPACE(body[n]))
- ++n;
- s = tor_strndup(body, n);
+ tor_assert(smartlist_len(args->args) == 1);
+ const char *s = smartlist_get(args->args, 0);
for (i = 0; signal_table[i].signal_name != NULL; ++i) {
if (!strcasecmp(s, signal_table[i].signal_name)) {
@@ -376,7 +375,6 @@ handle_control_signal(control_connection_t *conn, uint32_t len,
if (sig < 0)
connection_printf_to_buf(conn, "552 Unrecognized signal code \"%s\"\r\n",
s);
- tor_free(s);
if (sig < 0)
return 0;
@@ -390,15 +388,18 @@ handle_control_signal(control_connection_t *conn, uint32_t len,
return 0;
}
+static const control_cmd_syntax_t takeownership_syntax = {
+ .max_args = UINT_MAX, // This should probably become zero. XXXXX
+};
+
/** 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)
+handle_control_takeownership(control_connection_t *conn,
+ const control_cmd_args_t *args)
{
- (void)len;
- (void)body;
+ (void)args;
conn->is_owning_control_connection = 1;
@@ -410,15 +411,18 @@ handle_control_takeownership(control_connection_t *conn, uint32_t len,
return 0;
}
+static const control_cmd_syntax_t dropownership_syntax = {
+ .max_args = UINT_MAX, // This should probably become zero. XXXXX
+};
+
/** Called when we get a DROPOWNERSHIP command. Mark this connection
* as a non-owning connection, so that we will not exit if the connection
* closes. */
static int
-handle_control_dropownership(control_connection_t *conn, uint32_t len,
- const char *body)
+handle_control_dropownership(control_connection_t *conn,
+ const control_cmd_args_t *args)
{
- (void)len;
- (void)body;
+ (void)args;
conn->is_owning_control_connection = 0;
@@ -1099,21 +1103,21 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
return 0;
}
+static const control_cmd_syntax_t redirectstream_syntax = {
+ .min_args = 2,
+ .max_args = UINT_MAX, // XXX should be 3.
+};
+
/** Called when we receive a REDIRECTSTERAM command. Try to change the target
* address of the named AP stream, and report success or failure. */
static int
-handle_control_redirectstream(control_connection_t *conn, uint32_t len,
- const char *body)
+handle_control_redirectstream(control_connection_t *conn,
+ const control_cmd_args_t *cmd_args)
{
entry_connection_t *ap_conn = NULL;
char *new_addr = NULL;
uint16_t new_port = 0;
- smartlist_t *args;
- (void) len;
-
- args = getargs_helper("REDIRECTSTREAM", conn, body, 2, -1);
- if (!args)
- return 0;
+ const smartlist_t *args = cmd_args->args;
if (!(ap_conn = get_stream(smartlist_get(args, 0)))
|| !ap_conn->socks_request) {
@@ -1133,8 +1137,6 @@ handle_control_redirectstream(control_connection_t *conn, uint32_t len,
}
}
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
if (!new_addr)
return 0;
@@ -1147,23 +1149,26 @@ handle_control_redirectstream(control_connection_t *conn, uint32_t len,
return 0;
}
+static const control_cmd_syntax_t closestream_syntax = {
+ .min_args = 2,
+ .max_args = UINT_MAX, /* XXXX This is the original behavior, but
+ * maybe we should change the spec. */
+};
+
/** Called when we get a CLOSESTREAM command; try to close the named stream
* and report success or failure. */
static int
-handle_control_closestream(control_connection_t *conn, uint32_t len,
- const char *body)
+handle_control_closestream(control_connection_t *conn,
+ const control_cmd_args_t *cmd_args)
{
entry_connection_t *ap_conn=NULL;
uint8_t reason=0;
- smartlist_t *args;
int ok;
- (void) len;
+ const smartlist_t *args = cmd_args->args;
- args = getargs_helper("CLOSESTREAM", conn, body, 2, -1);
- if (!args)
- return 0;
+ tor_assert(smartlist_len(args) >= 2);
- else if (!(ap_conn = get_stream(smartlist_get(args, 0))))
+ 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 {
@@ -1175,8 +1180,6 @@ handle_control_closestream(control_connection_t *conn, uint32_t len,
ap_conn = NULL;
}
}
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
if (!ap_conn)
return 0;
@@ -1269,19 +1272,20 @@ handle_control_resolve(control_connection_t *conn, uint32_t len,
return 0;
}
+static const control_cmd_syntax_t protocolinfo_syntax = {
+ .max_args = UINT_MAX
+};
+
/** Called when we get a PROTOCOLINFO command: send back a reply. */
static int
-handle_control_protocolinfo(control_connection_t *conn, uint32_t len,
- const char *body)
+handle_control_protocolinfo(control_connection_t *conn,
+ const control_cmd_args_t *cmd_args)
{
const char *bad_arg = NULL;
- smartlist_t *args;
- (void)len;
+ const smartlist_t *args = cmd_args->args;
conn->have_sent_protocolinfo = 1;
- args = smartlist_new();
- smartlist_split_string(args, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+
SMARTLIST_FOREACH(args, const char *, arg, {
int ok;
tor_parse_long(arg, 10, 0, LONG_MAX, &ok, NULL);
@@ -1337,24 +1341,21 @@ handle_control_protocolinfo(control_connection_t *conn, uint32_t len,
tor_free(esc_cfile);
}
done:
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
return 0;
}
+static const control_cmd_syntax_t usefeature_syntax = {
+ .max_args = UINT_MAX
+};
+
/** Called when we get a USEFEATURE command: parse the feature list, and
* set up the control_connection's options properly. */
static int
handle_control_usefeature(control_connection_t *conn,
- uint32_t len,
- const char *body)
+ const control_cmd_args_t *cmd_args)
{
- smartlist_t *args;
+ const smartlist_t *args = cmd_args->args;
int bad = 0;
- (void) len; /* body is nul-terminated; it's safe to ignore the length */
- args = smartlist_new();
- smartlist_split_string(args, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
if (!strcasecmp(arg, "VERBOSE_NAMES"))
;
@@ -1372,22 +1373,19 @@ handle_control_usefeature(control_connection_t *conn,
send_control_done(conn);
}
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
return 0;
}
+static const control_cmd_syntax_t dropguards_syntax = {
+ .max_args = 0,
+};
+
/** Implementation for the DROPGUARDS command. */
static int
handle_control_dropguards(control_connection_t *conn,
- uint32_t len,
- const char *body)
+ const control_cmd_args_t *args)
{
- smartlist_t *args;
- (void) len; /* body is nul-terminated; it's safe to ignore the length */
- args = smartlist_new();
- smartlist_split_string(args, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ (void) args; /* We don't take arguments. */
static int have_warned = 0;
if (! have_warned) {
@@ -1397,15 +1395,9 @@ handle_control_dropguards(control_connection_t *conn,
have_warned = 1;
}
- if (smartlist_len(args)) {
- connection_printf_to_buf(conn, "512 Too many arguments to DROPGUARDS\r\n");
- } else {
- remove_all_entry_guards();
- send_control_done(conn);
- }
+ remove_all_entry_guards();
+ send_control_done(conn);
- SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
- smartlist_free(args);
return 0;
}
@@ -2202,19 +2194,19 @@ add_onion_helper_clientauth(const char *arg, int *created, char **err_msg)
return client;
}
+static const control_cmd_syntax_t del_onion_syntax = {
+ .min_args = 1, .max_args = 1,
+};
+
/** Called when we get a DEL_ONION command; parse the body, and remove
* the existing ephemeral Onion Service. */
static int
handle_control_del_onion(control_connection_t *conn,
- uint32_t len,
- const char *body)
+ const control_cmd_args_t *cmd_args)
{
int hs_version = 0;
- smartlist_t *args;
- (void) len; /* body is nul-terminated; it's safe to ignore the length */
- args = getargs_helper("DEL_ONION", conn, body, 1, 1);
- if (!args)
- return 0;
+ smartlist_t *args = cmd_args->args;
+ tor_assert(smartlist_len(args) == 1);
const char *service_id = smartlist_get(args, 0);
if (rend_valid_v2_service_id(service_id)) {
@@ -2280,11 +2272,6 @@ handle_control_del_onion(control_connection_t *conn,
}
out:
- SMARTLIST_FOREACH(args, char *, cp, {
- memwipe(cp, 0, strlen(cp));
- tor_free(cp);
- });
- smartlist_free(args);
return 0;
}
@@ -2399,20 +2386,26 @@ typedef struct control_cmd_def_t {
**/
#define ONE_LINE(name, htype, flags) \
ONE_LINE_(name, htype, flags, NULL)
-#define ONE_LINE_PARSED(name, flags, syntax) \
- ONE_LINE_(name, parsed, flags, syntax)
+#define ONE_LINE_PARSED(name, flags) \
+ ONE_LINE_(name, parsed, flags, &name ##_syntax)
/**
* Macro: declare a command with a multi-line argument and a given set of
* flags.
**/
-#define MULTLINE(name, htype, flags) \
+#define MULTLINE_(name, htype, flags, syntax) \
{ "+"#name, \
hnd_ ##htype, \
{ .htype = handle_control_ ##name }, \
flags, \
- NULL \
+ syntax \
}
+
+#define MULTLINE(name, htype, flags) \
+ MULTLINE_(name, htype, flags, NULL)
+#define MULTLINE_PARSED(name, flags) \
+ MULTLINE_(name, parsed, flags, &name##_syntax)
+
/**
* Macro: declare an obsolete command. (Obsolete commands give a different
* error than non-existent ones.)
@@ -2432,33 +2425,33 @@ static const control_cmd_def_t CONTROL_COMMANDS[] =
{
ONE_LINE(setconf, legacy_mut, 0),
ONE_LINE(resetconf, legacy_mut, 0),
- ONE_LINE(getconf, legacy_mut, 0),
- MULTLINE(loadconf, legacy, 0),
- ONE_LINE(setevents, legacy, 0),
+ ONE_LINE_PARSED(getconf, 0),
+ MULTLINE_PARSED(loadconf, 0),
+ ONE_LINE_PARSED(setevents, 0),
ONE_LINE(authenticate, legacy, CMD_FL_WIPE),
ONE_LINE(saveconf, legacy, 0),
- ONE_LINE(signal, legacy, 0),
- ONE_LINE(takeownership, legacy, 0),
- ONE_LINE(dropownership, legacy, 0),
+ ONE_LINE_PARSED(signal, 0),
+ ONE_LINE_PARSED(takeownership, 0),
+ ONE_LINE_PARSED(dropownership, 0),
ONE_LINE(mapaddress, legacy, 0),
- ONE_LINE(getinfo, legacy, 0),
+ ONE_LINE_PARSED(getinfo, 0),
ONE_LINE(extendcircuit, legacy, 0),
ONE_LINE(setcircuitpurpose, legacy, 0),
OBSOLETE(setrouterpurpose),
ONE_LINE(attachstream, legacy, 0),
MULTLINE(postdescriptor, legacy, 0),
- ONE_LINE(redirectstream, legacy, 0),
- ONE_LINE(closestream, legacy, 0),
+ ONE_LINE_PARSED(redirectstream, 0),
+ ONE_LINE_PARSED(closestream, 0),
ONE_LINE(closecircuit, legacy, 0),
- ONE_LINE(usefeature, legacy, 0),
+ ONE_LINE_PARSED(usefeature, 0),
ONE_LINE(resolve, legacy, 0),
- ONE_LINE(protocolinfo, legacy, 0),
+ ONE_LINE_PARSED(protocolinfo, 0),
ONE_LINE(authchallenge, legacy, CMD_FL_WIPE),
- ONE_LINE(dropguards, legacy, 0),
+ ONE_LINE_PARSED(dropguards, 0),
ONE_LINE(hsfetch, legacy, 0),
MULTLINE(hspost, legacy, 0),
ONE_LINE(add_onion, legacy, CMD_FL_WIPE),
- ONE_LINE(del_onion, legacy, CMD_FL_WIPE),
+ ONE_LINE_PARSED(del_onion, CMD_FL_WIPE),
};
/**
diff --git a/src/feature/control/control_cmd.h b/src/feature/control/control_cmd.h
index 1070a9edb..801bf4370 100644
--- a/src/feature/control/control_cmd.h
+++ b/src/feature/control/control_cmd.h
@@ -25,28 +25,6 @@ void control_cmd_args_free_(control_cmd_args_t *args);
#define control_cmd_args_free(v) \
FREE_AND_NULL(control_cmd_args_t, control_cmd_args_free_, (v))
-#ifdef CONTROL_CMD_PRIVATE
-#include "lib/crypt_ops/crypto_ed25519.h"
-
-/* ADD_ONION secret key to create an ephemeral service. The command supports
- * multiple versions so this union stores the key and passes it to the HS
- * subsystem depending on the requested version. */
-typedef union add_onion_secret_key_t {
- /* Hidden service v2 secret key. */
- crypto_pk_t *v2;
- /* Hidden service v3 secret key. */
- ed25519_secret_key_t *v3;
-} add_onion_secret_key_t;
-
-STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk,
- const char **key_new_alg_out,
- char **key_new_blob_out,
- add_onion_secret_key_t *decoded_key,
- int *hs_version, char **err_msg_out);
-
-STATIC rend_authorized_client_t *add_onion_helper_clientauth(const char *arg,
- int *created, char **err_msg_out);
-
/**
* Definition for the syntax of a controller command, as parsed by
* control_cmd_parse_args.
@@ -71,6 +49,28 @@ typedef struct control_cmd_syntax_t {
bool want_object;
} control_cmd_syntax_t;
+#ifdef CONTROL_CMD_PRIVATE
+#include "lib/crypt_ops/crypto_ed25519.h"
+
+/* ADD_ONION secret key to create an ephemeral service. The command supports
+ * multiple versions so this union stores the key and passes it to the HS
+ * subsystem depending on the requested version. */
+typedef union add_onion_secret_key_t {
+ /* Hidden service v2 secret key. */
+ crypto_pk_t *v2;
+ /* Hidden service v3 secret key. */
+ ed25519_secret_key_t *v3;
+} add_onion_secret_key_t;
+
+STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk,
+ const char **key_new_alg_out,
+ char **key_new_blob_out,
+ add_onion_secret_key_t *decoded_key,
+ int *hs_version, char **err_msg_out);
+
+STATIC rend_authorized_client_t *add_onion_helper_clientauth(const char *arg,
+ int *created, char **err_msg_out);
+
STATIC control_cmd_args_t *control_cmd_parse_args(
const char *command,
const control_cmd_syntax_t *syntax,
diff --git a/src/feature/control/control_getinfo.c b/src/feature/control/control_getinfo.c
index a7a85f2fd..5c6a0d4aa 100644
--- a/src/feature/control/control_getinfo.c
+++ b/src/feature/control/control_getinfo.c
@@ -55,6 +55,7 @@
#include "core/or/origin_circuit_st.h"
#include "core/or/socks_request_st.h"
#include "feature/control/control_connection_st.h"
+#include "feature/control/control_cmd_args_st.h"
#include "feature/dircache/cached_dir_st.h"
#include "feature/nodelist/extrainfo_st.h"
#include "feature/nodelist/microdesc_st.h"
@@ -1584,21 +1585,22 @@ handle_getinfo_helper(control_connection_t *control_conn,
return 0; /* unrecognized */
}
+const control_cmd_syntax_t getinfo_syntax = {
+ .max_args = UINT_MAX,
+};
+
/** Called when we receive a GETINFO command. Try to fetch all requested
* information, and reply with information or error message. */
int
-handle_control_getinfo(control_connection_t *conn, uint32_t len,
- const char *body)
+handle_control_getinfo(control_connection_t *conn,
+ const control_cmd_args_t *args)
{
- smartlist_t *questions = smartlist_new();
+ const smartlist_t *questions = args->args;
smartlist_t *answers = smartlist_new();
smartlist_t *unrecognized = smartlist_new();
char *ans = NULL;
int i;
- (void) len; /* body is NUL-terminated, so it's safe to ignore the length. */
- smartlist_split_string(questions, body, " ",
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
const char *errmsg = NULL;
@@ -1653,8 +1655,6 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
done:
SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
smartlist_free(answers);
- SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp));
- smartlist_free(questions);
SMARTLIST_FOREACH(unrecognized, char *, cp, tor_free(cp));
smartlist_free(unrecognized);
diff --git a/src/feature/control/control_getinfo.h b/src/feature/control/control_getinfo.h
index d5a2feb3e..2d56586f6 100644
--- a/src/feature/control/control_getinfo.h
+++ b/src/feature/control/control_getinfo.h
@@ -12,8 +12,12 @@
#ifndef TOR_CONTROL_GETINFO_H
#define TOR_CONTROL_GETINFO_H
-int handle_control_getinfo(control_connection_t *conn, uint32_t len,
- const char *body);
+struct control_cmd_syntax_t;
+struct control_cmd_args_t;
+extern const struct control_cmd_syntax_t getinfo_syntax;
+
+int handle_control_getinfo(control_connection_t *conn,
+ const struct control_cmd_args_t *args);
#ifdef CONTROL_GETINFO_PRIVATE
STATIC int getinfo_helper_onions(