Browse Source

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.
Nick Mathewson 6 years ago
parent
commit
01b07c548b

+ 2 - 2
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/main.c:run_tor_main_loop() 105
 problem function-size /src/app/main/ntmain.c:nt_service_install() 125
 problem function-size /src/app/main/ntmain.c:nt_service_install() 125
 problem include-count /src/app/main/shutdown.c 52
 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 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_free_minimal() 185
 problem function-size /src/core/mainloop/connection.c:connection_listener_new() 328
 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: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_cmd.c:handle_control_command() 104
 problem function-size /src/feature/control/control_events.c:control_event_stream_status() 119
 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_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_dir() 304
 problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_events() 236
 problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_events() 236

+ 116 - 123
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);
   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
 /** Called when we receive a GETCONF message.  Parse the request, and
  * reply with a CONFVALUE or an ERROR message */
  * reply with a CONFVALUE or an ERROR message */
 static int
 static int
-handle_control_getconf(control_connection_t *conn, uint32_t body_len,
+handle_control_getconf(control_connection_t *conn,
-                       char *body)
+                       const control_cmd_args_t *args)
 {
 {
-  smartlist_t *questions = smartlist_new();
+  const smartlist_t *questions = args->args;
   smartlist_t *answers = smartlist_new();
   smartlist_t *answers = smartlist_new();
   smartlist_t *unrecognized = smartlist_new();
   smartlist_t *unrecognized = smartlist_new();
   char *msg = NULL;
   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();
   const or_options_t *options = get_options();
   int i, len;
   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) {
   SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
     if (!option_is_recognized(q)) {
     if (!option_is_recognized(q)) {
       smartlist_add(unrecognized, (char*) 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_FOREACH(answers, char *, cp, tor_free(cp));
   smartlist_free(answers);
   smartlist_free(answers);
-  SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp));
-  smartlist_free(questions);
   smartlist_free(unrecognized);
   smartlist_free(unrecognized);
 
 
   tor_free(msg);
   tor_free(msg);
@@ -230,17 +229,21 @@ handle_control_getconf(control_connection_t *conn, uint32_t body_len,
   return 0;
   return 0;
 }
 }
 
 
+static const control_cmd_syntax_t loadconf_syntax = {
+  .want_object = true
+};
+
 /** Called when we get a +LOADCONF message. */
 /** Called when we get a +LOADCONF message. */
 static int
 static int
-handle_control_loadconf(control_connection_t *conn, uint32_t len,
+handle_control_loadconf(control_connection_t *conn,
-                         const char *body)
+                        const control_cmd_args_t *args)
 {
 {
   setopt_err_t retval;
   setopt_err_t retval;
   char *errstring = NULL;
   char *errstring = NULL;
   const char *msg = 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)
   if (retval != SETOPT_OK)
     log_warn(LD_CONTROL,
     log_warn(LD_CONTROL,
@@ -276,20 +279,20 @@ handle_control_loadconf(control_connection_t *conn, uint32_t len,
   return 0;
   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,
 /** Called when we get a SETEVENTS message: update conn->event_mask,
  * and reply with DONE or ERROR. */
  * and reply with DONE or ERROR. */
 static int
 static int
-handle_control_setevents(control_connection_t *conn, uint32_t len,
+handle_control_setevents(control_connection_t *conn,
-                         const char *body)
+                         const control_cmd_args_t *args)
 {
 {
   int event_code;
   int event_code;
   event_mask_t event_mask = 0;
   event_mask_t event_mask = 0;
-  smartlist_t *events = smartlist_new();
+  const smartlist_t *events = args->args;
-
-  (void) len;
 
 
-  smartlist_split_string(events, body, " ",
-                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
   SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
   SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
     {
     {
       if (!strcasecmp(ev, "EXTENDED") ||
       if (!strcasecmp(ev, "EXTENDED") ||
@@ -311,16 +314,12 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
         if (event_code == -1) {
         if (event_code == -1) {
           connection_printf_to_buf(conn, "552 Unrecognized event \"%s\"\r\n",
           connection_printf_to_buf(conn, "552 Unrecognized event \"%s\"\r\n",
                                    ev);
                                    ev);
-          SMARTLIST_FOREACH(events, char *, e, tor_free(e));
-          smartlist_free(events);
           return 0;
           return 0;
         }
         }
       }
       }
       event_mask |= (((event_mask_t)1) << event_code);
       event_mask |= (((event_mask_t)1) << event_code);
     }
     }
   SMARTLIST_FOREACH_END(ev);
   SMARTLIST_FOREACH_END(ev);
-  SMARTLIST_FOREACH(events, char *, e, tor_free(e));
-  smartlist_free(events);
 
 
   conn->event_mask = event_mask;
   conn->event_mask = event_mask;
 
 
@@ -348,23 +347,23 @@ handle_control_saveconf(control_connection_t *conn, uint32_t len,
   return 0;
   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
 /** 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
  * report success or failure. (If the signal results in a shutdown, success
  * may not be reported.) */
  * may not be reported.) */
 static int
 static int
-handle_control_signal(control_connection_t *conn, uint32_t len,
+handle_control_signal(control_connection_t *conn,
-                      const char *body)
+                      const control_cmd_args_t *args)
 {
 {
   int sig = -1;
   int sig = -1;
   int i;
   int i;
-  int n = 0;
-  char *s;
-
-  (void) len;
 
 
-  while (body[n] && ! TOR_ISSPACE(body[n]))
+  tor_assert(smartlist_len(args->args) == 1);
-    ++n;
+  const char *s = smartlist_get(args->args, 0);
-  s = tor_strndup(body, n);
 
 
   for (i = 0; signal_table[i].signal_name != NULL; ++i) {
   for (i = 0; signal_table[i].signal_name != NULL; ++i) {
     if (!strcasecmp(s, signal_table[i].signal_name)) {
     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)
   if (sig < 0)
     connection_printf_to_buf(conn, "552 Unrecognized signal code \"%s\"\r\n",
     connection_printf_to_buf(conn, "552 Unrecognized signal code \"%s\"\r\n",
                              s);
                              s);
-  tor_free(s);
   if (sig < 0)
   if (sig < 0)
     return 0;
     return 0;
 
 
@@ -390,15 +388,18 @@ handle_control_signal(control_connection_t *conn, uint32_t len,
   return 0;
   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
 /** Called when we get a TAKEOWNERSHIP command.  Mark this connection
  * as an owning connection, so that we will exit if the connection
  * as an owning connection, so that we will exit if the connection
  * closes. */
  * closes. */
 static int
 static int
-handle_control_takeownership(control_connection_t *conn, uint32_t len,
+handle_control_takeownership(control_connection_t *conn,
-                             const char *body)
+                             const control_cmd_args_t *args)
 {
 {
-  (void)len;
+  (void)args;
-  (void)body;
 
 
   conn->is_owning_control_connection = 1;
   conn->is_owning_control_connection = 1;
 
 
@@ -410,15 +411,18 @@ handle_control_takeownership(control_connection_t *conn, uint32_t len,
   return 0;
   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
 /** Called when we get a DROPOWNERSHIP command.  Mark this connection
  * as a non-owning connection, so that we will not exit if the connection
  * as a non-owning connection, so that we will not exit if the connection
  * closes. */
  * closes. */
 static int
 static int
-handle_control_dropownership(control_connection_t *conn, uint32_t len,
+handle_control_dropownership(control_connection_t *conn,
-                             const char *body)
+                             const control_cmd_args_t *args)
 {
 {
-  (void)len;
+  (void)args;
-  (void)body;
 
 
   conn->is_owning_control_connection = 0;
   conn->is_owning_control_connection = 0;
 
 
@@ -1099,21 +1103,21 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
   return 0;
   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
 /** Called when we receive a REDIRECTSTERAM command.  Try to change the target
  * address of the named AP stream, and report success or failure. */
  * address of the named AP stream, and report success or failure. */
 static int
 static int
-handle_control_redirectstream(control_connection_t *conn, uint32_t len,
+handle_control_redirectstream(control_connection_t *conn,
-                              const char *body)
+                              const control_cmd_args_t *cmd_args)
 {
 {
   entry_connection_t *ap_conn = NULL;
   entry_connection_t *ap_conn = NULL;
   char *new_addr = NULL;
   char *new_addr = NULL;
   uint16_t new_port = 0;
   uint16_t new_port = 0;
-  smartlist_t *args;
+  const smartlist_t *args = cmd_args->args;
-  (void) len;
-
-  args = getargs_helper("REDIRECTSTREAM", conn, body, 2, -1);
-  if (!args)
-    return 0;
 
 
   if (!(ap_conn = get_stream(smartlist_get(args, 0)))
   if (!(ap_conn = get_stream(smartlist_get(args, 0)))
            || !ap_conn->socks_request) {
            || !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)
   if (!new_addr)
     return 0;
     return 0;
 
 
@@ -1147,23 +1149,26 @@ handle_control_redirectstream(control_connection_t *conn, uint32_t len,
   return 0;
   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
 /** Called when we get a CLOSESTREAM command; try to close the named stream
  * and report success or failure. */
  * and report success or failure. */
 static int
 static int
-handle_control_closestream(control_connection_t *conn, uint32_t len,
+handle_control_closestream(control_connection_t *conn,
-                           const char *body)
+                           const control_cmd_args_t *cmd_args)
 {
 {
   entry_connection_t *ap_conn=NULL;
   entry_connection_t *ap_conn=NULL;
   uint8_t reason=0;
   uint8_t reason=0;
-  smartlist_t *args;
   int ok;
   int ok;
-  (void) len;
+  const smartlist_t *args = cmd_args->args;
 
 
-  args = getargs_helper("CLOSESTREAM", conn, body, 2, -1);
+  tor_assert(smartlist_len(args) >= 2);
-  if (!args)
-    return 0;
 
 
-  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",
     connection_printf_to_buf(conn, "552 Unknown stream \"%s\"\r\n",
                              (char*)smartlist_get(args, 0));
                              (char*)smartlist_get(args, 0));
   else {
   else {
@@ -1175,8 +1180,6 @@ handle_control_closestream(control_connection_t *conn, uint32_t len,
       ap_conn = NULL;
       ap_conn = NULL;
     }
     }
   }
   }
-  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
-  smartlist_free(args);
   if (!ap_conn)
   if (!ap_conn)
     return 0;
     return 0;
 
 
@@ -1269,19 +1272,20 @@ handle_control_resolve(control_connection_t *conn, uint32_t len,
   return 0;
   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. */
 /** Called when we get a PROTOCOLINFO command: send back a reply. */
 static int
 static int
-handle_control_protocolinfo(control_connection_t *conn, uint32_t len,
+handle_control_protocolinfo(control_connection_t *conn,
-                            const char *body)
+                            const control_cmd_args_t *cmd_args)
 {
 {
   const char *bad_arg = NULL;
   const char *bad_arg = NULL;
-  smartlist_t *args;
+  const smartlist_t *args = cmd_args->args;
-  (void)len;
 
 
   conn->have_sent_protocolinfo = 1;
   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, {
   SMARTLIST_FOREACH(args, const char *, arg, {
       int ok;
       int ok;
       tor_parse_long(arg, 10, 0, LONG_MAX, &ok, NULL);
       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);
     tor_free(esc_cfile);
   }
   }
  done:
  done:
-  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
-  smartlist_free(args);
   return 0;
   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
 /** Called when we get a USEFEATURE command: parse the feature list, and
  * set up the control_connection's options properly. */
  * set up the control_connection's options properly. */
 static int
 static int
 handle_control_usefeature(control_connection_t *conn,
 handle_control_usefeature(control_connection_t *conn,
-                          uint32_t len,
+                          const control_cmd_args_t *cmd_args)
-                          const char *body)
 {
 {
-  smartlist_t *args;
+  const smartlist_t *args = cmd_args->args;
   int bad = 0;
   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) {
   SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
       if (!strcasecmp(arg, "VERBOSE_NAMES"))
       if (!strcasecmp(arg, "VERBOSE_NAMES"))
         ;
         ;
@@ -1372,22 +1373,19 @@ handle_control_usefeature(control_connection_t *conn,
     send_control_done(conn);
     send_control_done(conn);
   }
   }
 
 
-  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
-  smartlist_free(args);
   return 0;
   return 0;
 }
 }
 
 
+static const control_cmd_syntax_t dropguards_syntax = {
+  .max_args = 0,
+};
+
 /** Implementation for the DROPGUARDS command. */
 /** Implementation for the DROPGUARDS command. */
 static int
 static int
 handle_control_dropguards(control_connection_t *conn,
 handle_control_dropguards(control_connection_t *conn,
-                          uint32_t len,
+                          const control_cmd_args_t *args)
-                          const char *body)
 {
 {
-  smartlist_t *args;
+  (void) args; /* We don't take arguments. */
-  (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);
 
 
   static int have_warned = 0;
   static int have_warned = 0;
   if (! have_warned) {
   if (! have_warned) {
@@ -1397,15 +1395,9 @@ handle_control_dropguards(control_connection_t *conn,
     have_warned = 1;
     have_warned = 1;
   }
   }
 
 
-  if (smartlist_len(args)) {
+  remove_all_entry_guards();
-    connection_printf_to_buf(conn, "512 Too many arguments to DROPGUARDS\r\n");
+  send_control_done(conn);
-  } else {
-    remove_all_entry_guards();
-    send_control_done(conn);
-  }
 
 
-  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
-  smartlist_free(args);
   return 0;
   return 0;
 }
 }
 
 
@@ -2202,19 +2194,19 @@ add_onion_helper_clientauth(const char *arg, int *created, char **err_msg)
   return client;
   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
 /** Called when we get a DEL_ONION command; parse the body, and remove
  * the existing ephemeral Onion Service. */
  * the existing ephemeral Onion Service. */
 static int
 static int
 handle_control_del_onion(control_connection_t *conn,
 handle_control_del_onion(control_connection_t *conn,
-                          uint32_t len,
+                         const control_cmd_args_t *cmd_args)
-                          const char *body)
 {
 {
   int hs_version = 0;
   int hs_version = 0;
-  smartlist_t *args;
+  smartlist_t *args = cmd_args->args;
-  (void) len; /* body is nul-terminated; it's safe to ignore the length */
+  tor_assert(smartlist_len(args) == 1);
-  args = getargs_helper("DEL_ONION", conn, body, 1, 1);
-  if (!args)
-    return 0;
 
 
   const char *service_id = smartlist_get(args, 0);
   const char *service_id = smartlist_get(args, 0);
   if (rend_valid_v2_service_id(service_id)) {
   if (rend_valid_v2_service_id(service_id)) {
@@ -2280,11 +2272,6 @@ handle_control_del_onion(control_connection_t *conn,
   }
   }
 
 
  out:
  out:
-  SMARTLIST_FOREACH(args, char *, cp, {
-    memwipe(cp, 0, strlen(cp));
-    tor_free(cp);
-  });
-  smartlist_free(args);
   return 0;
   return 0;
 }
 }
 
 
@@ -2399,20 +2386,26 @@ typedef struct control_cmd_def_t {
  **/
  **/
 #define ONE_LINE(name, htype, flags) \
 #define ONE_LINE(name, htype, flags) \
   ONE_LINE_(name, htype, flags, NULL)
   ONE_LINE_(name, htype, flags, NULL)
-#define ONE_LINE_PARSED(name, flags, syntax) \
+#define ONE_LINE_PARSED(name, flags) \
-  ONE_LINE_(name, parsed, flags, syntax)
+  ONE_LINE_(name, parsed, flags, &name ##_syntax)
 
 
 /**
 /**
  * Macro: declare a command with a multi-line argument and a given set of
  * Macro: declare a command with a multi-line argument and a given set of
  * flags.
  * flags.
  **/
  **/
-#define MULTLINE(name, htype, flags)                            \
+#define MULTLINE_(name, htype, flags, syntax)                   \
   { "+"#name,                                                   \
   { "+"#name,                                                   \
       hnd_ ##htype,                                             \
       hnd_ ##htype,                                             \
       { .htype = handle_control_ ##name },                      \
       { .htype = handle_control_ ##name },                      \
       flags,                                                    \
       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
  * Macro: declare an obsolete command. (Obsolete commands give a different
  * error than non-existent ones.)
  * 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(setconf, legacy_mut, 0),
   ONE_LINE(resetconf, legacy_mut, 0),
   ONE_LINE(resetconf, legacy_mut, 0),
-  ONE_LINE(getconf, legacy_mut, 0),
+  ONE_LINE_PARSED(getconf, 0),
-  MULTLINE(loadconf, legacy, 0),
+  MULTLINE_PARSED(loadconf, 0),
-  ONE_LINE(setevents, legacy, 0),
+  ONE_LINE_PARSED(setevents, 0),
   ONE_LINE(authenticate, legacy, CMD_FL_WIPE),
   ONE_LINE(authenticate, legacy, CMD_FL_WIPE),
   ONE_LINE(saveconf, legacy, 0),
   ONE_LINE(saveconf, legacy, 0),
-  ONE_LINE(signal, legacy, 0),
+  ONE_LINE_PARSED(signal, 0),
-  ONE_LINE(takeownership, legacy, 0),
+  ONE_LINE_PARSED(takeownership, 0),
-  ONE_LINE(dropownership, legacy, 0),
+  ONE_LINE_PARSED(dropownership, 0),
   ONE_LINE(mapaddress, legacy, 0),
   ONE_LINE(mapaddress, legacy, 0),
-  ONE_LINE(getinfo, legacy, 0),
+  ONE_LINE_PARSED(getinfo, 0),
   ONE_LINE(extendcircuit, legacy, 0),
   ONE_LINE(extendcircuit, legacy, 0),
   ONE_LINE(setcircuitpurpose, legacy, 0),
   ONE_LINE(setcircuitpurpose, legacy, 0),
   OBSOLETE(setrouterpurpose),
   OBSOLETE(setrouterpurpose),
   ONE_LINE(attachstream, legacy, 0),
   ONE_LINE(attachstream, legacy, 0),
   MULTLINE(postdescriptor, legacy, 0),
   MULTLINE(postdescriptor, legacy, 0),
-  ONE_LINE(redirectstream, legacy, 0),
+  ONE_LINE_PARSED(redirectstream, 0),
-  ONE_LINE(closestream, legacy, 0),
+  ONE_LINE_PARSED(closestream, 0),
   ONE_LINE(closecircuit, legacy, 0),
   ONE_LINE(closecircuit, legacy, 0),
-  ONE_LINE(usefeature, legacy, 0),
+  ONE_LINE_PARSED(usefeature, 0),
   ONE_LINE(resolve, legacy, 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(authchallenge, legacy, CMD_FL_WIPE),
-  ONE_LINE(dropguards, legacy, 0),
+  ONE_LINE_PARSED(dropguards, 0),
   ONE_LINE(hsfetch, legacy, 0),
   ONE_LINE(hsfetch, legacy, 0),
   MULTLINE(hspost, legacy, 0),
   MULTLINE(hspost, legacy, 0),
   ONE_LINE(add_onion, legacy, CMD_FL_WIPE),
   ONE_LINE(add_onion, legacy, CMD_FL_WIPE),
-  ONE_LINE(del_onion, legacy, CMD_FL_WIPE),
+  ONE_LINE_PARSED(del_onion, CMD_FL_WIPE),
 };
 };
 
 
 /**
 /**

+ 22 - 22
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) \
 #define control_cmd_args_free(v) \
   FREE_AND_NULL(control_cmd_args_t, 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
  * Definition for the syntax of a controller command, as parsed by
  * control_cmd_parse_args.
  * control_cmd_parse_args.
@@ -71,6 +49,28 @@ typedef struct control_cmd_syntax_t {
   bool want_object;
   bool want_object;
 } control_cmd_syntax_t;
 } 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(
 STATIC control_cmd_args_t *control_cmd_parse_args(
                                    const char *command,
                                    const char *command,
                                    const control_cmd_syntax_t *syntax,
                                    const control_cmd_syntax_t *syntax,

+ 8 - 8
src/feature/control/control_getinfo.c

@@ -55,6 +55,7 @@
 #include "core/or/origin_circuit_st.h"
 #include "core/or/origin_circuit_st.h"
 #include "core/or/socks_request_st.h"
 #include "core/or/socks_request_st.h"
 #include "feature/control/control_connection_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/dircache/cached_dir_st.h"
 #include "feature/nodelist/extrainfo_st.h"
 #include "feature/nodelist/extrainfo_st.h"
 #include "feature/nodelist/microdesc_st.h"
 #include "feature/nodelist/microdesc_st.h"
@@ -1584,21 +1585,22 @@ handle_getinfo_helper(control_connection_t *control_conn,
   return 0; /* unrecognized */
   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
 /** Called when we receive a GETINFO command.  Try to fetch all requested
  * information, and reply with information or error message. */
  * information, and reply with information or error message. */
 int
 int
-handle_control_getinfo(control_connection_t *conn, uint32_t len,
+handle_control_getinfo(control_connection_t *conn,
-                       const char *body)
+                       const control_cmd_args_t *args)
 {
 {
-  smartlist_t *questions = smartlist_new();
+  const smartlist_t *questions = args->args;
   smartlist_t *answers = smartlist_new();
   smartlist_t *answers = smartlist_new();
   smartlist_t *unrecognized = smartlist_new();
   smartlist_t *unrecognized = smartlist_new();
   char *ans = NULL;
   char *ans = NULL;
   int i;
   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) {
   SMARTLIST_FOREACH_BEGIN(questions, const char *, q) {
     const char *errmsg = NULL;
     const char *errmsg = NULL;
 
 
@@ -1653,8 +1655,6 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
  done:
  done:
   SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
   SMARTLIST_FOREACH(answers, char *, cp, tor_free(cp));
   smartlist_free(answers);
   smartlist_free(answers);
-  SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp));
-  smartlist_free(questions);
   SMARTLIST_FOREACH(unrecognized, char *, cp, tor_free(cp));
   SMARTLIST_FOREACH(unrecognized, char *, cp, tor_free(cp));
   smartlist_free(unrecognized);
   smartlist_free(unrecognized);
 
 

+ 6 - 2
src/feature/control/control_getinfo.h

@@ -12,8 +12,12 @@
 #ifndef TOR_CONTROL_GETINFO_H
 #ifndef TOR_CONTROL_GETINFO_H
 #define TOR_CONTROL_GETINFO_H
 #define TOR_CONTROL_GETINFO_H
 
 
-int handle_control_getinfo(control_connection_t *conn, uint32_t len,
+struct control_cmd_syntax_t;
-                           const char *body);
+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
 #ifdef CONTROL_GETINFO_PRIVATE
 STATIC int getinfo_helper_onions(
 STATIC int getinfo_helper_onions(